#[derive(Debug, Clone, Copy)]
pub struct Metrics {
pub n: usize,
pub max_abs_err: f32,
pub rmse: f32,
pub psnr_db: f32,
}
impl Metrics {
pub const ZERO: Self = Self {
n: 0,
max_abs_err: 0.0,
rmse: 0.0,
psnr_db: f32::INFINITY,
};
#[must_use]
pub fn compare(reference: &[f32], output: &[f32]) -> Self {
let n = reference.len().min(output.len());
let mut max = 0.0f32;
let mut sumsq = 0.0f64;
for i in 0..n {
let e = (reference[i] - output[i]).abs();
if e > max {
max = e;
}
sumsq += f64::from(e) * f64::from(e);
}
let rmse = if n > 0 {
(sumsq / n as f64).sqrt() as f32
} else {
0.0
};
let psnr_db = if rmse > 0.0 {
(20.0 * (1.0 / f64::from(rmse)).log10()) as f32
} else {
f32::INFINITY
};
Self {
n,
max_abs_err: max,
rmse,
psnr_db,
}
}
#[must_use]
pub fn mean(rows: &[Self]) -> Self {
if rows.is_empty() {
return Self::ZERO;
}
let mut max = 0.0f32;
let mut sumsq = 0.0f64;
let mut n = 0usize;
for m in rows {
if m.max_abs_err > max {
max = m.max_abs_err;
}
sumsq += f64::from(m.rmse) * f64::from(m.rmse) * m.n as f64;
n += m.n;
}
let rmse = if n > 0 {
(sumsq / n as f64).sqrt() as f32
} else {
0.0
};
let psnr_db = if rmse > 0.0 {
(20.0 * (1.0 / f64::from(rmse)).log10()) as f32
} else {
f32::INFINITY
};
Self {
n,
max_abs_err: max,
rmse,
psnr_db,
}
}
#[must_use]
pub fn psnr_json(&self) -> String {
if self.psnr_db.is_finite() {
format!("{:.3}", self.psnr_db)
} else {
"null".to_string()
}
}
#[must_use]
pub fn summary(&self) -> String {
let psnr = if self.psnr_db.is_finite() {
format!("{:.2} dB", self.psnr_db)
} else {
"exact".to_string()
};
format!(
"maxerr={:.6} rmse={:.6} psnr={psnr}",
self.max_abs_err, self.rmse
)
}
}