use std::io;
use serde::Serialize;
use vernier_core::{Metric, ParityMode, StatLine, Summary};
use crate::error::CliError;
use crate::format::{FormatContext, FormatName, Formatter};
pub(crate) const SCHEMA_VERSION: &str = "1";
#[derive(Debug, Serialize)]
struct SchemaV1<'a> {
version: &'a str,
iou_type: &'a str,
parity_mode: &'a str,
max_dets: &'a [usize],
use_cats: bool,
lines: Vec<LineV1<'a>>,
stats: Vec<f64>,
}
#[derive(Debug, Serialize)]
struct LineV1<'a> {
metric: &'static str,
iou_threshold: Option<f64>,
iou_threshold_label: String,
area: &'a str,
max_dets: usize,
value: f64,
}
pub(crate) struct Json;
impl Formatter for Json {
fn name(&self) -> &'static str {
"json"
}
fn id(&self) -> FormatName {
FormatName::Json
}
fn render(
&self,
summary: &Summary,
ctx: &FormatContext<'_>,
out: &mut dyn io::Write,
) -> Result<(), CliError> {
let lines: Vec<LineV1<'_>> = summary.lines.iter().map(line_to_v1).collect();
let stats = summary.stats();
let doc = SchemaV1 {
version: SCHEMA_VERSION,
iou_type: ctx.iou_type.as_str(),
parity_mode: parity_mode_str(ctx.parity_mode),
max_dets: ctx.max_dets,
use_cats: ctx.use_cats,
lines,
stats,
};
serde_json::to_writer(&mut *out, &doc)?;
writeln!(out)?;
Ok(())
}
}
fn line_to_v1(line: &StatLine) -> LineV1<'_> {
let iou_threshold_label = match line.iou_threshold {
Some(t) => format!("{t:0.2}"),
None => "0.50:0.95".to_string(),
};
LineV1 {
metric: metric_str(line.metric),
iou_threshold: line.iou_threshold,
iou_threshold_label,
area: line.area.label.as_ref(),
max_dets: line.max_dets,
value: line.value,
}
}
fn metric_str(m: Metric) -> &'static str {
match m {
Metric::AveragePrecision => "AP",
Metric::AverageRecall => "AR",
}
}
fn parity_mode_str(m: ParityMode) -> &'static str {
match m {
ParityMode::Strict => "strict",
ParityMode::Corrected => "corrected",
}
}