diff --git a/src/filter/canny/gradient_threshold.rs b/src/filter/canny/gradient_threshold.rs new file mode 100644 index 0000000..8b7e74f --- /dev/null +++ b/src/filter/canny/gradient_threshold.rs @@ -0,0 +1,79 @@ +use super::*; + +use crate::*; + +#[derive(Clone, Copy, PartialEq)] +struct Step { + x: isize, + y: isize, +} + +impl Step { + fn of(mut theta: f32) -> Self { + theta /= std::f32::consts::PI; + if theta < 0.0 { + theta += 1.0; + } + + if theta <= 0.125 { + Self { x: 1, y: 0 } + } else if theta <= 0.375 { + Self { x: 1, y: 1 } + } else if theta <= 0.625 { + Self { x: 0, y: 1 } + } else if theta <= 0.875 { + Self { x: -1, y: 1 } + } else if theta <= 1.0 { + Self { x: 1, y: 0 } + } else { + panic!("Angle out of expected range"); + } + } + + fn back(&self) -> Self { + Self { + x: -self.x, + y: -self.y, + } + } + + fn from(&self, x: usize, y: usize) -> (isize, isize) { + (x as isize + self.x, y as isize + self.y) + } +} + +impl Default for Step { + fn default() -> Self { + Step { x: 0, y: 0 } + } +} + +pub fn gradient_threshold(img: Image) -> Image { + let mut result = image::Image::::with_extent(img.extent); + result.for_each(|x, y, p| { + let value = img.at(x, y); + let step = Step::of(value.direction); + + let this = value.magnitude; + let next = get(&img, step.from(x, y)) + .map_or(f32::NEG_INFINITY, |v| v.magnitude); + let prev = get(&img, step.back().from(x, y)) + .map_or(f32::NEG_INFINITY, |v| v.magnitude); + if this > next && this > prev { + *p = this; + } + }); + result +} + +fn get(img: &Image, pos: (isize, isize)) -> Option { + if pos.0 >= 0 + && pos.0 < img.extent.x as isize + && pos.1 >= 0 + && pos.1 < img.extent.y as isize + { + Some(img.at(pos.0 as usize, pos.1 as usize)) + } else { + None + } +}