use alloc::vec;
use alloc::vec::Vec;
use crate::dynmatrix::DynMatrix;
use crate::traits::FloatScalar;
use super::border::BorderMode;
use super::filters::{gaussian_blur, sobel_gradients};
pub fn canny<T: FloatScalar>(
src: &DynMatrix<T>,
sigma: T,
low_threshold: T,
high_threshold: T,
border: BorderMode<T>,
) -> DynMatrix<T> {
let h = src.nrows();
let w = src.ncols();
let zero = T::zero();
let one = T::one();
if h == 0 || w == 0 {
return DynMatrix::<T>::zeros(h, w);
}
let smoothed = gaussian_blur(src, sigma, border);
let (gx, gy) = sobel_gradients(&smoothed, border);
let mut mag = DynMatrix::<T>::zeros(h, w);
for j in 0..w {
for i in 0..h {
let a = gx[(i, j)];
let b = gy[(i, j)];
mag[(i, j)] = (a * a + b * b).sqrt();
}
}
let half = T::from(0.5_f64).unwrap();
let tan_22_5 = T::from(0.41421356237309503_f64).unwrap(); let tan_67_5 = T::from(2.414213562373095_f64).unwrap();
let mut nms = DynMatrix::<T>::zeros(h, w);
for j in 1..w.saturating_sub(1) {
for i in 1..h.saturating_sub(1) {
let m = mag[(i, j)];
if m == zero {
continue;
}
let dx = gx[(i, j)];
let dy = gy[(i, j)];
let adx = dx.abs();
let ady = dy.abs();
let (n1, n2) = if ady <= tan_22_5 * adx {
(mag[(i, j - 1)], mag[(i, j + 1)])
} else if ady >= tan_67_5 * adx {
(mag[(i - 1, j)], mag[(i + 1, j)])
} else if (dx > zero) == (dy > zero) {
(mag[(i - 1, j - 1)], mag[(i + 1, j + 1)])
} else {
(mag[(i - 1, j + 1)], mag[(i + 1, j - 1)])
};
let eps = T::epsilon() * m * half;
if m + eps >= n1 && m + eps >= n2 {
nms[(i, j)] = m;
}
}
}
let mut cls: Vec<u8> = vec![0; h * w];
let mut strong_seeds: Vec<(usize, usize)> = Vec::new();
for j in 0..w {
for i in 0..h {
let v = nms[(i, j)];
if v >= high_threshold {
cls[j * h + i] = 2;
strong_seeds.push((i, j));
} else if v >= low_threshold {
cls[j * h + i] = 1;
}
}
}
let mut stack = strong_seeds;
while let Some((i, j)) = stack.pop() {
for di in -1_isize..=1 {
for dj in -1_isize..=1 {
if di == 0 && dj == 0 {
continue;
}
let ni = i as isize + di;
let nj = j as isize + dj;
if ni < 0 || nj < 0 || ni >= h as isize || nj >= w as isize {
continue;
}
let idx = (nj as usize) * h + (ni as usize);
if cls[idx] == 1 {
cls[idx] = 2;
stack.push((ni as usize, nj as usize));
}
}
}
}
let mut out = DynMatrix::<T>::zeros(h, w);
for j in 0..w {
for i in 0..h {
out[(i, j)] = if cls[j * h + i] == 2 { one } else { zero };
}
}
out
}