Implement automatic double threshold for Canny filter

This commit is contained in:
cdo committed 2026-10-27 19:09:27 +00:00
1 parent ba0c3b4756
commit 7e62163c26
1 file changed
+85
+85
View File
@@ -0,0 +1,85 @@
use crate::image::*;
#[derive(Clone, Copy, PartialEq)]
pub enum Candidate {
None,
Weak,
Strong,
}
impl Default for Candidate {
fn default() -> Self {
Self::None
}
}
pub fn double_threshold(img: Image<f32>) -> Image<Candidate> {
let t = otsu::<20>(&img.pixels);
let mut result = Image::<Candidate>::with_extent(img.extent);
result.for_each(|x, y, p| {
let value = img.at(x, y);
*p = if value < t / 2.0 {
Candidate::None
} else if value < t {
Candidate::Weak
} else {
Candidate::Strong
}
});
result
}
fn otsu<const N: usize>(values: &Vec<f32>) -> f32 {
let mut hist = [0.0; N];
for x in values {
let bin = std::cmp::min(N - 1, (*x * (N as f32)) as usize);
hist[bin] += 1.0;
}
for i in 0..N {
hist[i] /= values.len() as f32;
}
let mut max = 0.0;
let mut res = 0;
for t in 0..N {
let var = variance(t, hist);
if var > max {
res = t;
max = var;
}
}
res as f32 / N as f32
}
fn variance<const N: usize>(t: usize, hist: [f32; N]) -> f32 {
let omega_0 = omega(0, t, hist);
let omega_1 = omega(t, N, hist);
let mu_0 = mu(0, t, hist, omega_0);
let mu_1 = mu(t, N, hist, omega_1);
omega_0 * omega_1 * (mu_0 - mu_1).powf(2.0)
}
fn omega<const N: usize>(from: usize, to: usize, hist: [f32; N]) -> f32 {
let mut sum = 0.0;
for i in from..to {
sum += hist[i];
}
sum
}
fn mu<const N: usize>(
from: usize,
to: usize,
hist: [f32; N],
omega: f32,
) -> f32 {
let mut sum = 0.0;
for i in from..to {
let bin_centre = (i as f32 + 0.5) / N as f32;
sum += bin_centre * hist[i];
}
sum / omega
}