use crate::image::Image;
use anyhow::Result;
pub fn threshold_binary<T, const CHANNELS: usize>(
image: &Image<T, CHANNELS>,
threshold: T,
max_value: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut output = Image::<T, CHANNELS>::from_size_val(image.size(), T::default())?;
ndarray::Zip::from(&mut output.data)
.and(&image.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold {
max_value
} else {
T::default()
};
});
Ok(output)
}
pub fn threshold_binary_inverse<T, const CHANNELS: usize>(
image: &Image<T, CHANNELS>,
threshold: T,
max_value: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut output = Image::<T, CHANNELS>::from_size_val(image.size(), T::default())?;
ndarray::Zip::from(&mut output.data)
.and(&image.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold {
T::default()
} else {
max_value
};
});
Ok(output)
}
pub fn threshold_truncate<T, const CHANNELS: usize>(
image: &Image<T, CHANNELS>,
threshold: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut output = Image::<T, CHANNELS>::from_size_val(image.size(), T::default()).unwrap();
ndarray::Zip::from(&mut output.data)
.and(&image.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold { threshold } else { inp };
});
Ok(output)
}
pub fn threshold_to_zero<T, const CHANNELS: usize>(
image: &Image<T, CHANNELS>,
threshold: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut output = Image::<T, CHANNELS>::from_size_val(image.size(), T::default()).unwrap();
ndarray::Zip::from(&mut output.data)
.and(&image.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold { inp } else { T::default() };
});
Ok(output)
}
pub fn threshold_to_zero_inverse<T, const CHANNELS: usize>(
image: &Image<T, CHANNELS>,
threshold: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut output = Image::<T, CHANNELS>::from_size_val(image.size(), T::default()).unwrap();
ndarray::Zip::from(&mut output.data)
.and(&image.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold { T::default() } else { inp };
});
Ok(output)
}
#[cfg(test)]
mod tests {
use crate::image::{Image, ImageSize};
#[test]
fn threshold_binary() {
let data = vec![100u8, 200, 50, 150, 200, 250];
let data_expected = [0u8, 255, 0, 255, 255, 255];
let image = Image::<_, 1>::new(
ImageSize {
width: 2,
height: 3,
},
data,
)
.unwrap();
let thresholded = super::threshold_binary(&image, 100, 255).unwrap();
assert_eq!(thresholded.num_channels(), 1);
assert_eq!(thresholded.size().width, 2);
assert_eq!(thresholded.size().height, 3);
thresholded
.data
.iter()
.zip(data_expected.iter())
.for_each(|(x, y)| {
assert_eq!(x, y);
});
}
#[test]
fn threshold_binary_inverse() {
let data = vec![100u8, 200, 50, 150, 200, 250];
let data_expected = [255u8, 0, 255, 0, 0, 0];
let image = Image::<_, 1>::new(
ImageSize {
width: 2,
height: 3,
},
data,
)
.unwrap();
let thresholded = super::threshold_binary_inverse(&image, 100, 255).unwrap();
assert_eq!(thresholded.num_channels(), 1);
assert_eq!(thresholded.size().width, 2);
assert_eq!(thresholded.size().height, 3);
thresholded
.data
.iter()
.zip(data_expected.iter())
.for_each(|(x, y)| {
assert_eq!(x, y);
});
}
#[test]
fn threshold_truncate() {
let data = vec![100u8, 200, 50, 150, 200, 250];
let data_expected = [100u8, 150, 50, 150, 150, 150];
let image = Image::<_, 1>::new(
ImageSize {
width: 2,
height: 3,
},
data,
)
.unwrap();
let thresholded = super::threshold_truncate(&image, 150).unwrap();
assert_eq!(thresholded.num_channels(), 1);
assert_eq!(thresholded.size().width, 2);
assert_eq!(thresholded.size().height, 3);
thresholded
.data
.iter()
.zip(data_expected.iter())
.for_each(|(x, y)| {
assert_eq!(x, y);
});
}
#[test]
fn threshold_to_zero() {
let data = vec![100u8, 200, 50, 150, 200, 250];
let data_expected = [0u8, 200, 0, 0, 200, 250];
let image = Image::<_, 3>::new(
ImageSize {
width: 2,
height: 1,
},
data,
)
.unwrap();
let thresholded = super::threshold_to_zero(&image, 150).unwrap();
assert_eq!(thresholded.num_channels(), 3);
assert_eq!(thresholded.size().width, 2);
assert_eq!(thresholded.size().height, 1);
thresholded
.data
.iter()
.zip(data_expected.iter())
.for_each(|(x, y)| {
assert_eq!(x, y);
});
}
#[test]
fn threshold_to_zero_inverse() {
let data = vec![100u8, 200, 50, 150, 200, 250];
let data_expected = [100u8, 0, 50, 150, 0, 0];
let image = Image::<_, 3>::new(
ImageSize {
width: 2,
height: 1,
},
data,
)
.unwrap();
let thresholded = super::threshold_to_zero_inverse(&image, 150).unwrap();
assert_eq!(thresholded.num_channels(), 3);
assert_eq!(thresholded.size().width, 2);
assert_eq!(thresholded.size().height, 1);
thresholded
.data
.iter()
.zip(data_expected.iter())
.for_each(|(x, y)| {
assert_eq!(x, y);
});
}
}