From dd8300ee7cdc01d624f3a28d34e61e55fdedcc96 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Sat, 24 Oct 2026 19:31:37 +0100 Subject: [PATCH] Implement basic kernel convolution --- src/kernel.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 98 insertions(+) create mode 100644 src/kernel.rs diff --git a/src/kernel.rs b/src/kernel.rs new file mode 100644 index 0000000..642b8e6 --- /dev/null +++ b/src/kernel.rs @@ -0,0 +1,97 @@ +use crate::*; + +pub trait Kernel { + fn convolve(&self, img: &image::Image) -> image::Image; +} + +fn sum(kernel: &[[f32; N]; N]) -> f32 { + let mut x = 0.0; + for i in 0..N { + for j in 0..N { + x += kernel[j][i]; + } + } + x +} + +impl Kernel for [[f32; N]; N] { + fn convolve(&self, img: &image::Image) -> image::Image { + let l = N as u32 / 2; + let mag = sum(self); + + let extent = image::Extent { + x: img.extent.x - 2 * l, + y: img.extent.y - 2 * l, + }; + + let mut pixels = Vec::with_capacity(extent.size() as usize); + for y in 0..extent.y { + for x in 0..extent.x { + let mut pixel = image::Pixel::default(); + 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; + pixel += img.pixels[idx] * self[j][i]; + } + } + pixels.push(pixel / mag); + } + } + + image::Image { extent, pixels } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn size_of_result_img() { + let input = image::Image { + extent: image::Extent { x: 6, y: 10 }, + pixels: vec![image::Pixel::default(); 60], + }; + let kernel = [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; + let output = kernel.convolve(&input); + assert_eq!(output.extent.x, 4); + 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::Pixel::rgba(0.1, 0.0, 0.0, 1.0), + image::Pixel::rgba(0.0, 0.2, 0.0, 1.0), + image::Pixel::rgba(0.0, 0.0, 0.3, 1.0), + image::Pixel::rgba(0.2, 0.0, 0.0, 1.0), + image::Pixel::rgba(0.0, 0.3, 0.0, 1.0), + image::Pixel::rgba(0.0, 0.0, 0.1, 1.0), + image::Pixel::rgba(0.3, 0.0, 0.0, 1.0), + image::Pixel::rgba(0.0, 0.1, 0.0, 1.0), + image::Pixel::rgba(0.0, 0.0, 0.2, 1.0), + ], + }; + 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, + ); + assert_eq!(output.pixels[0].a, 1.0); + } +} diff --git a/src/lib.rs b/src/lib.rs index 14995d4..2ba1056 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ pub mod image; +pub mod kernel;