use crate::core::*;
use ndarray::prelude::*;
use ndarray_stats::{HistogramExt, histogram::Grid};
use num_traits::{Num, NumAssignOps};
use num_traits::cast::{FromPrimitive, ToPrimitive};
pub trait HistogramEqExt<A> where A: Ord {
fn equalise_hist(&self, grid: Grid<A>) -> Self;
fn equalise_hist_inplace(&mut self, grid: Grid<A>);
}
impl<T> HistogramEqExt<T> for Array3<T>
where
T: Copy + Clone + Ord + Num + NumAssignOps + ToPrimitive + FromPrimitive + PixelBound
{
fn equalise_hist(&self, grid: Grid<T>) -> Self {
let mut result = self.clone();
result.equalise_hist_inplace(grid);
result
}
fn equalise_hist_inplace(&mut self, grid: Grid<T>) {
for mut c in self.axis_iter_mut(Axis(2)) {
let flat = Array::from_iter(c.iter())
.mapv(|x| *x)
.insert_axis(Axis(1));
let hist = flat.histogram(grid.clone());
let mut running_total = 0;
let mut min = 0.0;
let cdf = hist.counts().mapv(|x| {
running_total += x;
if min == 0.0 && running_total > 0 {
min = running_total as f32;
}
running_total as f32
});
let scale = (T::max_pixel() - T::min_pixel()).to_f32().unwrap_or_default();
let denominator = flat.len() as f32 - min;
c.mapv_inplace(|x| {
let index = match grid.index_of(&arr1(&[x])) {
Some(i) => {
if i.is_empty() {
0
} else {
i[0]
}
},
None => 0,
};
let mut f_res = ((cdf[index] - min)/denominator)*scale;
if T::discrete_levels().is_some() {
f_res = f_res.round();
}
T::from_f32(f_res).unwrap_or_else(T::zero) + T::min_pixel()
});
}
}
}
impl<T, C> HistogramEqExt<T> for Image<T, C>
where
Image<T, C>: Clone,
T: Copy + Clone + Ord + Num + NumAssignOps + ToPrimitive + FromPrimitive + PixelBound,
C: ColourModel,
{
fn equalise_hist(&self, grid: Grid<T>) -> Self {
let mut result = self.clone();
result.equalise_hist_inplace(grid);
result
}
fn equalise_hist_inplace(&mut self, grid: Grid<T>) {
self.data.equalise_hist_inplace(grid);
}
}
#[cfg(test)]
mod tests {
use crate::core::Gray;
use ndarray_stats::histogram::{Bins, Edges};
use super::*;
#[test]
fn hist_eq_test() {
let input_pixels = vec![52, 55, 61, 59, 70, 61, 76, 61,
62, 59, 55, 104, 94, 85, 59, 71,
63, 65, 66, 113, 144, 104, 63, 72,
64, 70, 70, 126, 154, 109, 71, 69,
67, 73, 68, 106, 122, 88, 68, 68,
68, 79, 60, 79, 77, 66, 58, 75,
69, 85, 64, 58, 55, 61, 65, 83,
70, 87, 69, 68, 65, 73, 78, 90,];
let output_pixels = vec![0, 12, 53, 32, 146, 53, 174, 53,
57, 32, 12, 227, 219, 202, 32, 154,
65, 85, 93, 239, 251, 227, 65, 158,
73, 146, 146, 247, 255, 235, 154, 130,
97, 166, 117, 231, 243, 210, 117, 117,
117, 190, 36, 190, 178, 93, 20, 170,
130, 202, 73, 20, 12, 53, 85, 194,
146, 206, 130, 117, 85, 166, 182, 215];
let input = Image::<u8, Gray>::from_shape_data(8, 8, input_pixels);
let expected = Image::<u8, Gray>::from_shape_data(8, 8, output_pixels);
let edges_vec: Vec<u8> = (0..255).collect();
let grid = Grid::from(vec![Bins::new(Edges::from(edges_vec))]);
let equalised = input.equalise_hist(grid);
assert_eq!(expected, equalised);
}
}