use fast_ssim2::compute_ssimulacra2;
use imgref::ImgVec;
use super::icc::ColorProfile;
use crate::error::{Error, Result};
pub fn calculate_ssimulacra2(
reference: &[u8],
test: &[u8],
width: usize,
height: usize,
) -> Result<f64> {
if reference.len() != test.len() {
return Err(Error::DimensionMismatch {
expected: (width, height),
actual: (test.len() / 3 / height, height),
});
}
let expected_len = width * height * 3;
if reference.len() != expected_len {
return Err(Error::MetricCalculation {
metric: "SSIMULACRA2".to_string(),
reason: format!(
"Invalid image size: expected {} bytes, got {}",
expected_len,
reference.len()
),
});
}
let ref_pixels: Vec<[u8; 3]> = reference
.chunks_exact(3)
.map(|c| [c[0], c[1], c[2]])
.collect();
let test_pixels: Vec<[u8; 3]> = test.chunks_exact(3).map(|c| [c[0], c[1], c[2]]).collect();
let ref_img = ImgVec::new(ref_pixels, width, height);
let test_img = ImgVec::new(test_pixels, width, height);
compute_ssimulacra2(ref_img.as_ref(), test_img.as_ref()).map_err(|e| Error::MetricCalculation {
metric: "SSIMULACRA2".to_string(),
reason: format!("Failed to compute SSIMULACRA2: {e:?}"),
})
}
pub fn calculate_ssimulacra2_icc(
reference: &[u8],
reference_profile: &ColorProfile,
test: &[u8],
test_profile: &ColorProfile,
width: usize,
height: usize,
) -> Result<f64> {
let (ref_srgb, test_srgb) =
super::icc::prepare_for_comparison(reference, reference_profile, test, test_profile)?;
calculate_ssimulacra2(&ref_srgb, &test_srgb, width, height)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_identical_images() {
let data: Vec<u8> = (0..100 * 100 * 3).map(|i| (i % 256) as u8).collect();
let score = calculate_ssimulacra2(&data, &data, 100, 100).unwrap();
assert!(
score > 99.0,
"Identical images should have score ~100, got {score}"
);
}
#[test]
fn test_different_images() {
let ref_data: Vec<u8> = vec![100u8; 100 * 100 * 3];
let test_data: Vec<u8> = vec![200u8; 100 * 100 * 3];
let score = calculate_ssimulacra2(&ref_data, &test_data, 100, 100).unwrap();
assert!(
score < 80.0,
"Very different images should have low score, got {score}"
);
}
#[test]
fn test_size_mismatch() {
let small: Vec<u8> = vec![128u8; 50 * 50 * 3];
let large: Vec<u8> = vec![128u8; 100 * 100 * 3];
let result = calculate_ssimulacra2(&small, &large, 100, 100);
assert!(result.is_err());
}
}