use ndarray::{Array, ArrayBase, AsArray, Dimension, ViewRepr};
use crate::image::{histogram, histogram_bin_midpoint};
use crate::prelude::*;
use crate::statistics::min_max;
use crate::threshold::manual::manual_mask;
#[inline]
pub fn otsu_mask<'a, T, A, D>(
data: A,
bins: Option<usize>,
threads: Option<usize>,
) -> Result<Array<bool, D>, ImgalError>
where
A: AsArray<'a, T, D>,
D: Dimension,
T: 'a + AsNumeric,
{
let data: ArrayBase<ViewRepr<&'a T>, D> = data.into();
let threshold = otsu_value(&data, bins, threads)?;
Ok(manual_mask(data, threshold, threads))
}
#[inline]
pub fn otsu_value<'a, T, A, D>(
data: A,
bins: Option<usize>,
threads: Option<usize>,
) -> Result<T, ImgalError>
where
A: AsArray<'a, T, D>,
D: Dimension,
T: 'a + AsNumeric,
{
let data: ArrayBase<ViewRepr<&'a T>, D> = data.into();
let hist = histogram(&data, bins, threads)?;
let dl = hist.len();
let (min, max) = min_max(data, threads)?;
let mut bcv: f64 = 0.0;
let mut bcv_max: f64 = 0.0;
let mut hist_sum: f64 = 0.0;
let mut hist_inten: f64 = 0.0;
let mut inten_k: f64 = 0.0;
let mut k_star: usize = 0;
let mut n_k: f64 = 0.0;
hist.iter().enumerate().for_each(|(i, &v)| {
let v = v as f64;
hist_sum += v;
hist_inten += i as f64 * v;
});
hist.iter().take(dl - 1).enumerate().for_each(|(i, &v)| {
let v = v as f64;
inten_k += i as f64 * v;
n_k += v;
let denom = n_k * (hist_sum - n_k);
if denom != 0.0 {
let num = (n_k / hist_sum) * hist_inten - inten_k;
bcv = (num * num) / denom;
} else {
bcv = 0.0;
}
if bcv >= bcv_max {
bcv_max = bcv;
k_star = i;
}
});
histogram_bin_midpoint(k_star, min, max, bins.unwrap_or(256))
}