(
reference: &[[f32; 3]],
candidate: &[[f32; 3]],
width: u32,
height: u32,
tolerance: f32,
)
| 80 | } |
| 81 | |
| 82 | fn compute_stats( |
| 83 | reference: &[[f32; 3]], |
| 84 | candidate: &[[f32; 3]], |
| 85 | width: u32, |
| 86 | height: u32, |
| 87 | tolerance: f32, |
| 88 | ) -> DiffStats { |
| 89 | let n = reference.len() as f32; |
| 90 | let mut sum_sq = [0f64; 3]; |
| 91 | let mut sum_sq_lum = 0f64; |
| 92 | let mut max_abs = 0f32; |
| 93 | let mut n_above = 0u64; |
| 94 | |
| 95 | for (r, c) in reference.iter().zip(candidate.iter()) { |
| 96 | let dr = r[0] - c[0]; |
| 97 | let dg = r[1] - c[1]; |
| 98 | let db = r[2] - c[2]; |
| 99 | sum_sq[0] += (dr as f64) * (dr as f64); |
| 100 | sum_sq[1] += (dg as f64) * (dg as f64); |
| 101 | sum_sq[2] += (db as f64) * (db as f64); |
| 102 | |
| 103 | let lum_r = luminance(*r); |
| 104 | let lum_c = luminance(*c); |
| 105 | let dl = lum_r - lum_c; |
| 106 | sum_sq_lum += (dl as f64) * (dl as f64); |
| 107 | |
| 108 | let mag = dr.abs().max(dg.abs()).max(db.abs()); |
| 109 | if mag > max_abs { |
| 110 | max_abs = mag; |
| 111 | } |
| 112 | if mag > tolerance { |
| 113 | n_above += 1; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | let rmse_r = (sum_sq[0] / n as f64).sqrt() as f32; |
| 118 | let rmse_g = (sum_sq[1] / n as f64).sqrt() as f32; |
| 119 | let rmse_b = (sum_sq[2] / n as f64).sqrt() as f32; |
| 120 | let rmse_luminance = (sum_sq_lum / n as f64).sqrt() as f32; |
| 121 | |
| 122 | let ssim = compute_ssim_luminance(reference, candidate, width, height); |
| 123 | |
| 124 | DiffStats { |
| 125 | rmse_r, |
| 126 | rmse_g, |
| 127 | rmse_b, |
| 128 | rmse_luminance, |
| 129 | max_abs_error: max_abs, |
| 130 | percent_above_tolerance: 100.0 * (n_above as f32) / n, |
| 131 | ssim, |
| 132 | width, |
| 133 | height, |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /// SSIM over the luminance channel with a single-scale 8×8 window. |
| 138 | /// Not as good as MS-SSIM but fast and plenty accurate for our |
no test coverage detected