use std::fmt::Debug;
use std::ops::{Add, Div, Sub};
use crate::pad::{pad, PadMethod};
use crate::spatial::spatial;
use crate::traits::NumOps;
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub enum SpatialOperations {
Contrast,
Maximum,
Gradient,
Minimum,
Mean
}
impl SpatialOperations {
pub fn from_string_result(input: &str) -> Result<Self, String> {
match input
{
"contrast" => Ok(Self::Contrast),
"maximum" | "max" => Ok(Self::Maximum),
"gradient" => Ok(Self::Gradient),
"minimum" | "min" => Ok(Self::Minimum),
"mean" | "avg" => Ok(Self::Mean),
_ => Err(
"Unknown statistic type,accepted values are contrast,(maximum|max),gradient,(minimum|min),mean"
.to_string()
)
}
}
}
fn find_min<T: PartialOrd + Default + Copy + NumOps<T>>(data: &[T]) -> T {
let mut minimum = T::max_val();
for datum in data {
if *datum < minimum {
minimum = *datum;
}
}
minimum
}
fn find_contrast<
T: PartialOrd + Default + Copy + NumOps<T> + Sub<Output = T> + Add<Output = T> + Div<Output = T>
>(
data: &[T]
) -> T {
let mut minimum = T::max_val();
let mut maximum = T::min_val();
for datum in data {
if *datum < minimum {
minimum = *datum;
}
if *datum > maximum {
maximum = *datum;
}
}
let num = maximum - minimum;
let div = (maximum + minimum).saturating_add(T::one());
num / div
}
fn find_gradient<
T: PartialOrd + Default + Copy + NumOps<T> + Sub<Output = T> + Add<Output = T> + Div<Output = T>
>(
data: &[T]
) -> T {
let mut minimum = T::max_val();
let mut maximum = T::min_val();
for datum in data {
if *datum < minimum {
minimum = *datum;
}
if *datum > maximum {
maximum = *datum;
}
}
maximum - minimum
}
#[inline(always)]
fn find_max<T: PartialOrd + Copy + NumOps<T>>(data: &[T]) -> T {
let mut maximum = T::min_val();
for datum in data {
if *datum > maximum {
maximum = *datum;
}
}
maximum
}
#[allow(clippy::cast_possible_truncation)]
fn find_mean<T>(data: &[T]) -> T
where
T: Default + Copy + NumOps<T> + Add<Output = T> + Div<Output = T>,
u32: std::convert::From<T>
{
let mut maximum = u32::default();
let len = data.len() as u32;
for datum in data {
maximum += u32::from(*datum);
}
T::from_u32(maximum / len)
}
pub fn spatial_ops<T>(
in_channel: &[T], out_channel: &mut [T], radius: usize, width: usize, height: usize,
operations: SpatialOperations
) where
T: PartialOrd
+ Default
+ Copy
+ NumOps<T>
+ Sub<Output = T>
+ Add<Output = T>
+ Div<Output = T>,
u32: std::convert::From<T>
{
let padded_input = pad(
in_channel,
width,
height,
radius,
radius,
PadMethod::Replicate
);
let ptr = match operations {
SpatialOperations::Contrast => find_contrast::<T>,
SpatialOperations::Maximum => find_max::<T>,
SpatialOperations::Gradient => find_gradient::<T>,
SpatialOperations::Minimum => find_min::<T>,
SpatialOperations::Mean => find_mean::<T>
};
spatial(&padded_input, out_channel, radius, width, height, ptr);
}
#[cfg(feature = "benchmarks")]
#[cfg(test)]
mod benchmarks {
extern crate test;
use crate::spatial_ops::{spatial_ops, SpatialOperations};
#[bench]
fn bench_spatial_mean(b: &mut test::Bencher) {
let width = 800;
let height = 800;
let dimensions = width * height;
let in_vec = vec![255_u16; dimensions];
let mut out_vec = vec![255_u16; dimensions];
let radius = 3;
b.iter(|| {
spatial_ops(
&in_vec,
&mut out_vec,
radius,
width,
height,
SpatialOperations::Mean
);
});
}
#[bench]
fn bench_spatial_min(b: &mut test::Bencher) {
let width = 800;
let height = 800;
let dimensions = width * height;
let in_vec = vec![255_u16; dimensions];
let mut out_vec = vec![255_u16; dimensions];
let radius = 3;
b.iter(|| {
spatial_ops(
&in_vec,
&mut out_vec,
radius,
width,
height,
SpatialOperations::Minimum
);
});
}
}