From a3743dd921389b05566ef3586de8cbbb68036709 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Mon, 26 Oct 2026 15:52:02 +0000 Subject: [PATCH] Use proptest for kernel convolution --- src/kernel.rs | 88 ++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/src/kernel.rs b/src/kernel.rs index 2b2888f..20c1b7e 100644 --- a/src/kernel.rs +++ b/src/kernel.rs @@ -28,25 +28,23 @@ impl Kernel for [[f32; N]; N] { x: img.extent.x - 2 * l, y: img.extent.y - 2 * l, }; - let mut result = image::Image::with_extent(extent); - result.for_each(|x, y, p| { + let mut result = image::Image::with_extent(extent); + result.for_each(|x, y, p| { for i in 0..N { for j in 0..N { - let img_x = x as usize + i; - let img_y = y as usize + j; - let idx = img_y * img.extent.x as usize + img_x; - *p += img.pixels[idx] * self[j][i]; + *p += img.at(x + i, y + j) * self[j][i]; } } *p /= mag; }); - result + result } } #[cfg(test)] mod tests { use super::*; + use proptest::prelude::*; #[test] fn size_of_result_img() { @@ -60,37 +58,49 @@ mod tests { assert_eq!(output.extent.y, 8); } - #[test] - fn single_pixel_result() { - let input = image::Image { - extent: image::Extent { x: 3, y: 3 }, - pixels: vec![ - image::SRGB::new(0.1, 0.0, 0.0), - image::SRGB::new(0.0, 0.2, 0.0), - image::SRGB::new(0.0, 0.0, 0.3), - image::SRGB::new(0.2, 0.0, 0.0), - image::SRGB::new(0.0, 0.3, 0.0), - image::SRGB::new(0.0, 0.0, 0.1), - image::SRGB::new(0.3, 0.0, 0.0), - image::SRGB::new(0.0, 0.1, 0.0), - image::SRGB::new(0.0, 0.0, 0.2), - ], - }; - let kernel = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]]; - let output = kernel.convolve(&input); - assert_eq!(output.extent.x, 1); - assert_eq!(output.extent.y, 1); - assert_eq!( - output.pixels[0].r, - (0.1 * 0.1 + 0.2 * 0.4 + 0.3 * 0.7) / 4.5, - ); - assert_eq!( - output.pixels[0].g, - (0.2 * 0.2 + 0.3 * 0.5 + 0.1 * 0.8) / 4.5, - ); - assert_eq!( - output.pixels[0].b, - (0.3 * 0.3 + 0.1 * 0.6 + 0.2 * 0.9) / 4.5, - ); + proptest! { + #[test] + fn single_pixel_result_3x3( + p1 in 0.1f32..1.0, + p2 in 0.0f32..1.0, + p3 in 0.0f32..1.0, + p4 in 0.0f32..1.0, + p5 in 0.0f32..1.0, + p6 in 0.0f32..1.0, + p7 in 0.0f32..1.0, + p8 in 0.0f32..1.0, + p9 in 0.0f32..1.0, + k1 in 0.0f32..1.0, + k2 in 0.0f32..1.0, + k3 in 0.0f32..1.0, + k4 in 0.0f32..1.0, + k5 in 0.0f32..1.0, + k6 in 0.0f32..1.0, + k7 in 0.0f32..1.0, + k8 in 0.0f32..1.0, + k9 in 0.0f32..1.0, + ) { + let input = image::Image { + extent: image::Extent { x: 5, y: 5 }, + pixels: vec![ + 10.0, 10.0, 10.0, 10.0, 10.0, + 10.0, p1, p2, p3, 10.0, + 10.0, p4, p5, p6, 10.0, + 10.0, p7, p8, p9, 10.0, + 10.0, 10.0, 10.0, 10.0, 10.0, + ], + }; + let kernel = [[k1, k2, k3], [k4, k5, k6], [k7, k8, k9]]; + let output = kernel.convolve(&input); + prop_assert_eq!(output.extent.x, 3); + prop_assert_eq!(output.extent.y, 3); + + let mag = k1 + k2 + k3 + k4 + k5 + k6 + k7 + k8 + k9; + let sum = p1 * k1 + p2 * k2 + p3 * k3 + + p4 * k4 + p5 * k5 + p6 * k6 + + p7 * k7 + p8 * k8 + p9 * k9; + let exp = sum / mag; + prop_assert!((output.at(1, 1) - exp).abs() < 1e-6); + } } }