diff --git a/src/filter.rs b/src/filter.rs index 8d787df..408da3b 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,5 +1,8 @@ pub mod canny; pub mod sobel; +pub mod threshold; pub use canny::canny; pub use sobel::sobel; +pub use threshold::threshold; +pub use threshold::threshold_auto; diff --git a/src/filter/canny/double_threshold.rs b/src/filter/canny/double_threshold.rs index c7b313e..9dff3f2 100644 --- a/src/filter/canny/double_threshold.rs +++ b/src/filter/canny/double_threshold.rs @@ -1,4 +1,5 @@ use crate::image::*; +use crate::*; #[derive(Clone, Copy, PartialEq)] pub enum Candidate { @@ -14,72 +15,16 @@ impl Default for Candidate { } pub fn double_threshold(img: Image) -> Image { - let t = otsu::<20>(&img.pixels); - let mut result = Image::::with_extent(img.extent); - result.for_each(|x, y, p| { - let value = img.at(x, y); - *p = if value < t / 2.0 { + let t = filter::threshold::otsu::<20, _>( + (&img.pixels).into_iter().map(|p| *p), + ); + img.map(|p| { + if p < t / 2.0 { Candidate::None - } else if value < t { + } else if p < t { Candidate::Weak } else { Candidate::Strong } - }); - result -} - -fn otsu(values: &Vec) -> 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(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(from: usize, to: usize, hist: [f32; N]) -> f32 { - let mut sum = 0.0; - for i in from..to { - sum += hist[i]; - } - sum -} - -fn mu( - 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 + }) } diff --git a/src/filter/threshold.rs b/src/filter/threshold.rs new file mode 100644 index 0000000..cdb3c1f --- /dev/null +++ b/src/filter/threshold.rs @@ -0,0 +1,73 @@ +use crate::image::*; + +pub fn threshold_auto(img: Image) -> Image { + let t = otsu::<20, _>((&img.pixels).into_iter().map(|p| p.y)); + threshold(t, img) +} + +pub fn threshold(t: f32, mut img: Image) -> Image { + img.for_each(|_, _, p| { + p.y = if p.y >= t { 1.0 } else { 0.0 }; + }); + img +} + +pub fn otsu(values: I) -> f32 +where + I: Iterator, +{ + let mut hist = [0.0; N]; + let mut n = 0; + for x in values { + let bin = std::cmp::min(N - 1, (x * (N as f32)) as usize); + hist[bin] += 1.0; + n += 1; + } + for i in 0..N { + hist[i] /= n 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(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(from: usize, to: usize, hist: [f32; N]) -> f32 { + let mut sum = 0.0; + for i in from..to { + sum += hist[i]; + } + sum +} + +fn mu( + 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 +} diff --git a/src/lib.rs b/src/lib.rs index bd26a13..8e343a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ +pub mod filter; pub mod image; pub mod kernel; -pub mod filter;