use ndarray::{s, Array, Array1, ArrayRef, Axis, Dimension, Zip};
use num_traits::{FromPrimitive, Num};
use crate::{array_like, pad_to, BorderMode};
pub fn uniform_filter<A, D>(
data: &ArrayRef<A, D>,
size: usize,
mode: BorderMode<A>,
) -> Array<A, D>
where
A: Copy + Num + FromPrimitive + PartialOrd + 'static,
D: Dimension,
{
let half = size / 2;
let mut data = data.to_owned();
let mut output = array_like(&data, data.dim(), A::zero());
for d in 0..data.ndim() {
let n = data.len_of(Axis(d));
if half > n {
panic!("Data size is too small for the inputs (sigma and truncate)");
}
inner_uniform1d(&data, size, Axis(d), mode, &mut output);
if d != data.ndim() - 1 {
std::mem::swap(&mut output, &mut data);
}
}
output
}
pub fn uniform_filter1d<A, D>(
data: &ArrayRef<A, D>,
size: usize,
axis: Axis,
mode: BorderMode<A>,
) -> Array<A, D>
where
A: Copy + Num + FromPrimitive + PartialOrd + 'static,
D: Dimension,
{
let mut output = array_like(data, data.dim(), A::zero());
inner_uniform1d(data, size, axis, mode, &mut output);
output
}
pub(crate) fn inner_uniform1d<A, D>(
data: &ArrayRef<A, D>,
size: usize,
axis: Axis,
mode: BorderMode<A>,
output: &mut Array<A, D>,
) where
A: Copy + Num + FromPrimitive + PartialOrd,
D: Dimension,
{
let size1 = size / 2;
let size2 = size - size1 - 1;
let size_as_a = A::from_usize(size).unwrap();
let mode = mode.to_pad_mode();
let n = data.len_of(axis);
let pad = vec![[size1, size2]];
let mut buffer = Array1::from_elem(n + size - 1, mode.init());
Zip::from(data.lanes(axis)).and(output.lanes_mut(axis)).for_each(|input, o| {
pad_to(&input, &pad, mode, &mut buffer);
let mut accumulator = buffer.slice(s![..size - 1]).sum();
Zip::from(o).and(buffer.slice(s![size - 1..])).and(buffer.slice(s![..n])).for_each(
|o, &leading_edge, &trailing_edge| {
accumulator = accumulator + leading_edge;
*o = accumulator / size_as_a;
accumulator = accumulator - trailing_edge;
},
);
});
}