#[cfg(feature = "rayon")]
use super::filter_clamped_parallel;
use super::{filter_clamped, gaussian_blur_f32};
use crate::{
definitions::{Clamp, Image},
kernel::Kernel,
map::{map_pixels2, map_subpixels},
};
use image::{GrayImage, Luma};
#[must_use = "the function does not modify the original image"]
pub fn sharpen3x3(image: &GrayImage) -> GrayImage {
let identity_minus_laplacian = Kernel::new(&[0, -1, 0, -1, 5, -1, 0, -1, 0], 3, 3);
filter_clamped(image, identity_minus_laplacian)
}
#[must_use = "the function does not modify the original image"]
#[cfg(feature = "rayon")]
#[doc = generate_parallel_doc_comment!("sharpen3x3")]
pub fn sharpen3x3_parallel(image: &GrayImage) -> GrayImage {
let identity_minus_laplacian = Kernel::new(&[0, -1, 0, -1, 5, -1, 0, -1, 0], 3, 3);
filter_clamped_parallel(image, identity_minus_laplacian)
}
#[must_use = "the function does not modify the original image"]
pub fn sharpen_gaussian(image: &GrayImage, sigma: f32, amount: f32) -> GrayImage {
let image = map_subpixels(image, |x| x as f32);
let smooth: Image<Luma<f32>> = gaussian_blur_f32(&image, sigma);
map_pixels2(&image, &smooth, |p, q| {
let v = (1.0 + amount) * p[0] - amount * q[0];
Luma([<u8 as Clamp<f32>>::clamp(v)])
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "rayon")]
fn test_sharpen3x3_parallel_matches_sharpen3x3() {
let image = gray_image!(
1, 2, 3;
4, 5, 6;
7, 8, 9);
let expected = sharpen3x3(&image);
let actual = sharpen3x3_parallel(&image);
assert_pixels_eq!(actual, expected);
}
}
#[cfg(not(miri))]
#[cfg(test)]
mod proptests {
use super::*;
use crate::proptest_utils::arbitrary_image;
use proptest::prelude::*;
proptest! {
#[test]
#[cfg(feature = "rayon")]
fn proptest_sharpen3x3_parallel_matches_sharpen3x3(
img in arbitrary_image::<Luma<u8>>(0..30, 0..30),
) {
let expected = sharpen3x3(&img);
let actual = sharpen3x3_parallel(&img);
prop_assert_eq!(actual, expected);
}
}
}