use scirs2_core::ndarray::{Array, Dimension};
use scirs2_core::numeric::{Float, FromPrimitive};
use std::fmt::Debug;
use super::{rank_filter, BorderMode};
use crate::error::{NdimageError, NdimageResult};
#[allow(dead_code)]
pub fn median_filter<T, D>(
input: &Array<T, D>,
size: &[usize],
mode: Option<BorderMode>,
) -> NdimageResult<Array<T, D>>
where
T: Float
+ FromPrimitive
+ Debug
+ PartialOrd
+ Clone
+ Send
+ Sync
+ std::ops::AddAssign
+ std::ops::DivAssign
+ 'static,
D: Dimension + 'static,
{
if size.len() != input.ndim() {
return Err(NdimageError::DimensionError(format!(
"Size must have same length as input dimensions (got {} expected {})",
size.len(),
input.ndim()
)));
}
let mut window_size = 1;
for &s in size {
if s == 0 {
return Err(NdimageError::InvalidInput(
"Kernel size cannot be zero".into(),
));
}
window_size *= s;
}
let median_rank = if window_size % 2 == 1 {
window_size / 2
} else {
window_size / 2
};
rank_filter(input, median_rank, size, mode)
}
#[allow(dead_code)]
pub fn median_filter_optimized<T, D>(
input: &Array<T, D>,
size: &[usize],
mode: Option<BorderMode>,
optimization_hint: Option<&str>,
) -> NdimageResult<Array<T, D>>
where
T: Float
+ FromPrimitive
+ Debug
+ PartialOrd
+ Clone
+ Send
+ Sync
+ std::ops::AddAssign
+ std::ops::DivAssign
+ 'static,
D: Dimension + 'static,
{
match optimization_hint {
Some("small_kernel") => {
median_filter(input, size, mode)
}
Some("large_kernel") => {
histogram_based_median_filter(input, size, mode)
}
Some("streaming") => {
chunked_median_filter(input, size, mode)
}
_ => {
let kernel_size: usize = size.iter().product();
let array_size = input.len();
if kernel_size <= 25 || array_size < 10000 {
median_filter(input, size, mode)
} else {
median_filter(input, size, mode)
}
}
}
}
#[allow(dead_code)]
fn histogram_based_median_filter<T, D>(
input: &Array<T, D>,
size: &[usize],
mode: Option<BorderMode>,
) -> NdimageResult<Array<T, D>>
where
T: Float
+ FromPrimitive
+ Debug
+ PartialOrd
+ Clone
+ Send
+ Sync
+ std::ops::AddAssign
+ std::ops::DivAssign
+ 'static,
D: Dimension + 'static,
{
median_filter(input, size, mode)
}
#[allow(dead_code)]
fn chunked_median_filter<T, D>(
input: &Array<T, D>,
size: &[usize],
mode: Option<BorderMode>,
) -> NdimageResult<Array<T, D>>
where
T: Float
+ FromPrimitive
+ Debug
+ PartialOrd
+ Clone
+ Send
+ Sync
+ std::ops::AddAssign
+ std::ops::DivAssign
+ 'static,
D: Dimension + 'static,
{
median_filter(input, size, mode)
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::{Array1, Array2};
#[test]
fn test_median_filter_1d() {
let array = Array1::from_vec(vec![1.0, 2.0, 3.0, 100.0, 5.0]);
let result =
median_filter(&array, &[3], None).expect("median_filter should succeed for test");
assert_eq!(result.shape(), array.shape());
assert_eq!(result[3], 5.0); }
#[test]
fn test_median_filter_2d() {
let mut image = Array2::zeros((5, 5));
image[[2, 2]] = 100.0;
let result =
median_filter(&image, &[3, 3], None).expect("median_filter should succeed for test");
assert_eq!(result.shape(), image.shape());
assert_eq!(result[[2, 2]], 0.0);
}
#[test]
fn test_median_filter_noise_removal() {
let array = Array1::from_vec(vec![1.0, 0.0, 1.0, 0.0, 100.0, 1.0, 0.0, 1.0, 0.0]);
let result =
median_filter(&array, &[3], None).expect("median_filter should succeed for test");
assert_eq!(result[4], 1.0); }
#[test]
fn test_median_filter_invalid_size() {
let image: Array2<f64> = Array2::eye(5);
let result = median_filter(&image, &[3], None);
assert!(result.is_err());
}
#[test]
fn test_median_filter_even_kernel() {
let array = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
let result =
median_filter(&array, &[4], None).expect("median_filter should succeed for test");
assert_eq!(result.shape(), array.shape());
}
#[test]
fn test_median_filter_zero_size() {
let array = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
let result = median_filter(&array, &[0], None);
assert!(result.is_err());
}
#[test]
fn test_median_filter_3d() {
use scirs2_core::ndarray::Array3;
let mut cube = Array3::<f64>::zeros((3, 3, 3));
cube[[1, 1, 1]] = 100.0;
let result =
median_filter(&cube, &[3, 3, 3], None).expect("median_filter should succeed for test");
assert_eq!(result.shape(), cube.shape());
assert_eq!(result[[1, 1, 1]], 0.0);
let mut noise_cube = Array3::<f64>::zeros((5, 5, 5));
noise_cube[[1, 2, 3]] = 100.0;
noise_cube[[3, 1, 2]] = -100.0;
noise_cube[[2, 3, 1]] = 50.0;
let filtered = median_filter(&noise_cube, &[3, 3, 3], None)
.expect("median_filter should succeed for test");
assert!(filtered[[1, 2, 3]].abs() < 100.0);
assert!(filtered[[3, 1, 2]].abs() < 100.0);
assert!(filtered[[2, 3, 1]].abs() < 50.0);
}
}