use crate::image::Image;
use anyhow::Result;
pub fn threshold_binary<T, const CHANNELS: usize>(
src: &Image<T, CHANNELS>,
threshold: T,
max_value: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut dst = Image::<T, CHANNELS>::from_size_val(src.size(), T::default())?;
ndarray::Zip::from(&mut dst.data)
.and(&src.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold {
max_value
} else {
T::default()
};
});
Ok(dst)
}
pub fn threshold_binary_inverse<T, const CHANNELS: usize>(
src: &Image<T, CHANNELS>,
threshold: T,
max_value: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut dst = Image::<T, CHANNELS>::from_size_val(src.size(), T::default())?;
ndarray::Zip::from(&mut dst.data)
.and(&src.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold {
T::default()
} else {
max_value
};
});
Ok(dst)
}
pub fn threshold_truncate<T, const CHANNELS: usize>(
src: &Image<T, CHANNELS>,
threshold: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut dst = Image::<T, CHANNELS>::from_size_val(src.size(), T::default())?;
ndarray::Zip::from(&mut dst.data)
.and(&src.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold { threshold } else { inp };
});
Ok(dst)
}
pub fn threshold_to_zero<T, const CHANNELS: usize>(
src: &Image<T, CHANNELS>,
threshold: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut dst = Image::<T, CHANNELS>::from_size_val(src.size(), T::default())?;
ndarray::Zip::from(&mut dst.data)
.and(&src.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold { inp } else { T::default() };
});
Ok(dst)
}
pub fn threshold_to_zero_inverse<T, const CHANNELS: usize>(
src: &Image<T, CHANNELS>,
threshold: T,
) -> Result<Image<T, CHANNELS>>
where
T: Copy + Clone + Default + Send + Sync + std::cmp::PartialOrd,
{
let mut dst = Image::<T, CHANNELS>::from_size_val(src.size(), T::default())?;
ndarray::Zip::from(&mut dst.data)
.and(&src.data)
.par_for_each(|out, &inp| {
*out = if inp > threshold { T::default() } else { inp };
});
Ok(dst)
}
pub fn in_range<T, const CHANNELS: usize>(
src: &Image<T, CHANNELS>,
lower_bound: &[T; CHANNELS],
upper_bound: &[T; CHANNELS],
) -> Result<Image<u8, 1>>
where
T: Sync + std::cmp::PartialOrd,
{
let mut dst = Image::from_size_val(src.size(), 0)?;
ndarray::Zip::from(dst.data.rows_mut())
.and(src.data.rows())
.par_for_each(|mut out, inp| {
let mut is_in_range = true;
let mut i = 0;
while is_in_range && i < CHANNELS {
is_in_range &= inp[i] >= lower_bound[i] && inp[i] <= upper_bound[i];
i += 1;
}
out[0] = if is_in_range { 255 } else { 0 };
});
Ok(dst)
}
#[cfg(test)]
mod tests {
use crate::image::{Image, ImageSize};
use anyhow::Result;
#[test]
fn threshold_binary() -> Result<()> {
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,
)?;
let thresholded = super::threshold_binary(&image, 100, 255)?;
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);
});
Ok(())
}
#[test]
fn threshold_binary_inverse() -> Result<()> {
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,
)?;
let thresholded = super::threshold_binary_inverse(&image, 100, 255)?;
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);
});
Ok(())
}
#[test]
fn threshold_truncate() -> Result<()> {
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,
)?;
let thresholded = super::threshold_truncate(&image, 150)?;
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);
});
Ok(())
}
#[test]
fn threshold_to_zero() -> Result<()> {
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,
)?;
let thresholded = super::threshold_to_zero(&image, 150)?;
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);
});
Ok(())
}
#[test]
fn threshold_to_zero_inverse() -> Result<()> {
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,
)?;
let thresholded = super::threshold_to_zero_inverse(&image, 150)?;
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);
});
Ok(())
}
#[test]
fn test_in_range() -> Result<()> {
let data = vec![100u8, 200, 50, 150, 200, 250];
let image = Image::<_, 3>::new(
ImageSize {
width: 2,
height: 1,
},
data,
)?;
let thresholded = super::in_range(&image, &[100, 150, 0], &[200, 200, 200])?;
assert_eq!(thresholded.num_channels(), 1);
assert_eq!(thresholded.size().width, 2);
assert_eq!(thresholded.size().height, 1);
assert_eq!(thresholded.get_pixel(0, 0, 0)?, 255);
assert_eq!(thresholded.get_pixel(1, 0, 0)?, 0);
Ok(())
}
}