use alloc::vec::Vec;
use core::cmp::Ordering;
use crate::dynmatrix::DynMatrix;
use crate::traits::{FloatScalar, MatrixMut, MatrixRef};
use super::border::BorderMode;
pub fn rank_filter<T: FloatScalar>(
src: &DynMatrix<T>,
radius: usize,
rank: usize,
border: BorderMode<T>,
) -> DynMatrix<T> {
let nrows = src.nrows();
let ncols = src.ncols();
let mut dst = DynMatrix::<T>::zeros(nrows, ncols);
if nrows == 0 || ncols == 0 {
return dst;
}
if radius == 0 {
return src.clone();
}
let k_side = 2 * radius + 1;
let k_total = k_side * k_side;
debug_assert!(rank < k_total, "rank {rank} out of range for window size {k_total}");
let r = radius as isize;
let mut buf: Vec<T> = Vec::with_capacity(k_total);
for j in 0..ncols {
for i in 0..nrows {
buf.clear();
for dj in -r..=r {
let sj = j as isize + dj;
for di in -r..=r {
let si = i as isize + di;
buf.push(super::convolve::fetch_border_2d(src, si, sj, border));
}
}
buf.select_nth_unstable_by(rank, |a, b| {
a.partial_cmp(b).unwrap_or(Ordering::Equal)
});
dst[(i, j)] = buf[rank];
}
}
dst
}
pub fn percentile_filter<T: FloatScalar>(
src: &DynMatrix<T>,
radius: usize,
percentile: T,
border: BorderMode<T>,
) -> DynMatrix<T> {
if radius == 0 {
return src.clone();
}
let zero = T::zero();
let one = T::one();
let p = if percentile < zero {
zero
} else if percentile > one {
one
} else {
percentile
};
let k_total = (2 * radius + 1) * (2 * radius + 1);
let k_minus_1 = T::from(k_total - 1).unwrap();
let rank_f = (p * k_minus_1).floor();
let rank = rank_f.to_usize().unwrap_or(0).min(k_total - 1);
rank_filter(src, radius, rank, border)
}
pub fn median_filter<T: FloatScalar>(
src: &DynMatrix<T>,
radius: usize,
border: BorderMode<T>,
) -> DynMatrix<T> {
match radius {
0 => src.clone(),
1 => median_3x3(src, border),
2 => median_5x5(src, border),
r => {
let k_total = (2 * r + 1) * (2 * r + 1);
rank_filter(src, r, k_total / 2, border)
}
}
}
fn median_3x3<T: FloatScalar>(src: &DynMatrix<T>, border: BorderMode<T>) -> DynMatrix<T> {
let nrows = src.nrows();
let ncols = src.ncols();
let mut dst = DynMatrix::<T>::zeros(nrows, ncols);
if nrows == 0 || ncols == 0 {
return dst;
}
if nrows < 3 || ncols < 3 {
let r = 1usize;
let k_total = 9;
return rank_filter(src, r, k_total / 2, border);
}
for j in 1..ncols - 1 {
let col_m = src.col_as_slice(j - 1, 0);
let col_c = src.col_as_slice(j, 0);
let col_p = src.col_as_slice(j + 1, 0);
let dst_col = dst.col_as_mut_slice(j, 0);
for i in 1..nrows - 1 {
let mut w: [T; 9] = [
col_m[i - 1], col_c[i - 1], col_p[i - 1],
col_m[i], col_c[i], col_p[i],
col_m[i + 1], col_c[i + 1], col_p[i + 1],
];
w.select_nth_unstable_by(4, |a, b| {
a.partial_cmp(b).unwrap_or(Ordering::Equal)
});
dst_col[i] = w[4];
}
}
let border_pixel = |i: usize, j: usize| -> T {
let mut w: [T; 9] = [T::zero(); 9];
let mut idx = 0;
for di in -1_isize..=1 {
for dj in -1_isize..=1 {
w[idx] = super::convolve::fetch_border_2d(
src,
i as isize + di,
j as isize + dj,
border,
);
idx += 1;
}
}
w.select_nth_unstable_by(4, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
w[4]
};
for j in 0..ncols {
dst[(0, j)] = border_pixel(0, j);
dst[(nrows - 1, j)] = border_pixel(nrows - 1, j);
}
for i in 1..nrows - 1 {
dst[(i, 0)] = border_pixel(i, 0);
dst[(i, ncols - 1)] = border_pixel(i, ncols - 1);
}
dst
}
fn median_5x5<T: FloatScalar>(src: &DynMatrix<T>, border: BorderMode<T>) -> DynMatrix<T> {
let nrows = src.nrows();
let ncols = src.ncols();
let mut dst = DynMatrix::<T>::zeros(nrows, ncols);
if nrows == 0 || ncols == 0 {
return dst;
}
if nrows < 5 || ncols < 5 {
let r = 2usize;
let k_total = 25;
return rank_filter(src, r, k_total / 2, border);
}
for j in 2..ncols - 2 {
let c0 = src.col_as_slice(j - 2, 0);
let c1 = src.col_as_slice(j - 1, 0);
let c2 = src.col_as_slice(j, 0);
let c3 = src.col_as_slice(j + 1, 0);
let c4 = src.col_as_slice(j + 2, 0);
let dst_col = dst.col_as_mut_slice(j, 0);
for i in 2..nrows - 2 {
let mut w: [T; 25] = [
c0[i - 2], c1[i - 2], c2[i - 2], c3[i - 2], c4[i - 2],
c0[i - 1], c1[i - 1], c2[i - 1], c3[i - 1], c4[i - 1],
c0[i], c1[i], c2[i], c3[i], c4[i],
c0[i + 1], c1[i + 1], c2[i + 1], c3[i + 1], c4[i + 1],
c0[i + 2], c1[i + 2], c2[i + 2], c3[i + 2], c4[i + 2],
];
w.select_nth_unstable_by(12, |a, b| {
a.partial_cmp(b).unwrap_or(Ordering::Equal)
});
dst_col[i] = w[12];
}
}
let border_pixel = |i: usize, j: usize| -> T {
let mut w: [T; 25] = [T::zero(); 25];
let mut idx = 0;
for di in -2_isize..=2 {
for dj in -2_isize..=2 {
w[idx] = super::convolve::fetch_border_2d(
src,
i as isize + di,
j as isize + dj,
border,
);
idx += 1;
}
}
w.select_nth_unstable_by(12, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
w[12]
};
for j in 0..ncols {
for i in 0..2 {
dst[(i, j)] = border_pixel(i, j);
dst[(nrows - 1 - i, j)] = border_pixel(nrows - 1 - i, j);
}
}
for i in 2..nrows - 2 {
for j in 0..2 {
dst[(i, j)] = border_pixel(i, j);
dst[(i, ncols - 1 - j)] = border_pixel(i, ncols - 1 - j);
}
}
dst
}
pub fn median_filter_u16(
src: &DynMatrix<u16>,
radius: usize,
border: BorderMode<u16>,
) -> DynMatrix<u16> {
let nrows = src.nrows();
let ncols = src.ncols();
let mut dst = DynMatrix::<u16>::zeros(nrows, ncols);
if nrows == 0 || ncols == 0 {
return dst;
}
if radius == 0 {
return src.clone();
}
const L: usize = 65536;
let r = radius as isize;
let k_total = (2 * radius + 1) * (2 * radius + 1);
let target = (k_total / 2) as u32;
let mut hist: Vec<u32> = alloc::vec![0u32; L];
for i in 0..nrows {
for h in hist.iter_mut() {
*h = 0;
}
for dj in -r..=r {
let sj = dj; for di in -r..=r {
let si = i as isize + di;
let v = super::convolve::fetch_border_2d(src, si, sj, border) as usize;
hist[v] += 1;
}
}
let mut cum: u32 = 0;
let mut m: usize = 0;
while cum + hist[m] <= target {
cum += hist[m];
m += 1;
}
dst[(i, 0)] = m as u16;
for j in 1..ncols {
let sj_out = j as isize - 1 - r;
let sj_in = j as isize + r;
for di in -r..=r {
let si = i as isize + di;
let v_out = super::convolve::fetch_border_2d(src, si, sj_out, border) as usize;
let v_in = super::convolve::fetch_border_2d(src, si, sj_in, border) as usize;
hist[v_out] -= 1;
if v_out < m {
cum -= 1;
}
hist[v_in] += 1;
if v_in < m {
cum += 1;
}
}
while cum > target {
m -= 1;
cum -= hist[m];
}
while cum + hist[m] <= target {
cum += hist[m];
m += 1;
}
dst[(i, j)] = m as u16;
}
}
dst
}