1use dssim_core::Dssim;
6use imgref::ImgVec;
7use rgb::RGBA;
8
9use crate::error::{Error, Result};
10use crate::viewing::ViewingCondition;
11
12pub fn calculate_dssim(
36 reference: &ImgVec<RGBA<f32>>,
37 test: &ImgVec<RGBA<f32>>,
38 _viewing: &ViewingCondition,
39) -> Result<f64> {
40 if reference.width() != test.width() || reference.height() != test.height() {
41 return Err(Error::DimensionMismatch {
42 expected: (reference.width() as u32, reference.height() as u32),
43 actual: (test.width() as u32, test.height() as u32),
44 });
45 }
46
47 let dssim = Dssim::new();
48
49 let ref_image = dssim
50 .create_image(reference)
51 .ok_or_else(|| Error::MetricCalculation {
52 metric: "DSSIM".to_string(),
53 reason: "Failed to create reference image".to_string(),
54 })?;
55
56 let test_image = dssim
57 .create_image(test)
58 .ok_or_else(|| Error::MetricCalculation {
59 metric: "DSSIM".to_string(),
60 reason: "Failed to create test image".to_string(),
61 })?;
62
63 let (dssim_val, _ssim_maps) = dssim.compare(&ref_image, test_image);
64
65 Ok(f64::from(dssim_val))
66}
67
68#[must_use]
80pub fn rgb8_to_dssim_image(data: &[u8], width: usize, height: usize) -> ImgVec<RGBA<f32>> {
81 let pixels: Vec<RGBA<f32>> = data
82 .chunks_exact(3)
83 .map(|rgb| RGBA {
84 r: f32::from(rgb[0]) / 255.0,
85 g: f32::from(rgb[1]) / 255.0,
86 b: f32::from(rgb[2]) / 255.0,
87 a: 1.0,
88 })
89 .collect();
90
91 ImgVec::new(pixels, width, height)
92}
93
94#[must_use]
106pub fn rgba8_to_dssim_image(data: &[u8], width: usize, height: usize) -> ImgVec<RGBA<f32>> {
107 let pixels: Vec<RGBA<f32>> = data
108 .chunks_exact(4)
109 .map(|rgba| RGBA {
110 r: f32::from(rgba[0]) / 255.0,
111 g: f32::from(rgba[1]) / 255.0,
112 b: f32::from(rgba[2]) / 255.0,
113 a: f32::from(rgba[3]) / 255.0,
114 })
115 .collect();
116
117 ImgVec::new(pixels, width, height)
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_identical_images() {
126 let pixels: Vec<RGBA<f32>> = (0..100 * 100)
127 .map(|_| RGBA {
128 r: 0.5,
129 g: 0.5,
130 b: 0.5,
131 a: 1.0,
132 })
133 .collect();
134 let img = ImgVec::new(pixels, 100, 100);
135
136 let dssim = calculate_dssim(&img, &img, &ViewingCondition::desktop()).unwrap();
137 assert!(
138 dssim < 0.0001,
139 "Identical images should have near-zero DSSIM"
140 );
141 }
142
143 #[test]
144 fn test_different_images() {
145 let ref_pixels: Vec<RGBA<f32>> = (0..100 * 100)
146 .map(|_| RGBA {
147 r: 0.3,
148 g: 0.3,
149 b: 0.3,
150 a: 1.0,
151 })
152 .collect();
153 let test_pixels: Vec<RGBA<f32>> = (0..100 * 100)
154 .map(|_| RGBA {
155 r: 0.7,
156 g: 0.7,
157 b: 0.7,
158 a: 1.0,
159 })
160 .collect();
161
162 let ref_img = ImgVec::new(ref_pixels, 100, 100);
163 let test_img = ImgVec::new(test_pixels, 100, 100);
164
165 let dssim = calculate_dssim(&ref_img, &test_img, &ViewingCondition::desktop()).unwrap();
166 assert!(dssim > 0.0, "Different images should have non-zero DSSIM");
167 }
168
169 #[test]
170 fn test_dimension_mismatch() {
171 let small: Vec<RGBA<f32>> = (0..50 * 50)
172 .map(|_| RGBA {
173 r: 0.5,
174 g: 0.5,
175 b: 0.5,
176 a: 1.0,
177 })
178 .collect();
179 let large: Vec<RGBA<f32>> = (0..100 * 100)
180 .map(|_| RGBA {
181 r: 0.5,
182 g: 0.5,
183 b: 0.5,
184 a: 1.0,
185 })
186 .collect();
187
188 let small_img = ImgVec::new(small, 50, 50);
189 let large_img = ImgVec::new(large, 100, 100);
190
191 let result = calculate_dssim(&small_img, &large_img, &ViewingCondition::desktop());
192 assert!(matches!(result, Err(Error::DimensionMismatch { .. })));
193 }
194
195 #[test]
196 fn test_rgb8_conversion() {
197 let rgb_data = vec![255u8, 0, 0, 0, 255, 0]; let img = rgb8_to_dssim_image(&rgb_data, 2, 1);
199
200 assert_eq!(img.width(), 2);
201 assert_eq!(img.height(), 1);
202 let pixels: Vec<_> = img.pixels().collect();
203 assert!((pixels[0].r - 1.0).abs() < 0.001);
204 assert!((pixels[1].g - 1.0).abs() < 0.001);
205 }
206
207 #[test]
208 fn test_rgba8_conversion() {
209 let rgba_data = vec![255u8, 0, 0, 128, 0, 255, 0, 255]; let img = rgba8_to_dssim_image(&rgba_data, 2, 1);
211
212 assert_eq!(img.width(), 2);
213 assert_eq!(img.height(), 1);
214 let pixels: Vec<_> = img.pixels().collect();
215 assert!((pixels[0].a - 0.502).abs() < 0.01);
216 assert!((pixels[1].a - 1.0).abs() < 0.001);
217 }
218}