use crate::utils::{
apply_convolution, apply_edge_detection, blend_images, calculate_luminance, clamp_u8,
gaussian_blur, EdgeMethod, HIGH_PASS_KERNEL,
};
use crate::{Image, Result};
use rayon::prelude::*;
use std::sync::Arc;
pub fn unsharp_mask(mut image: Image, radius: f32, amount: f32, threshold: u8) -> Result<Image> {
let original = Arc::new(image.data.get_ref().clone());
let blurred = Arc::new(gaussian_blur(&original, radius));
let threshold_f = threshold as f32;
let buffer = image.data.get_mut();
buffer
.enumerate_rows_mut()
.par_bridge()
.for_each(|(y, row)| {
for (x, _, pixel) in row {
let orig_pixel = original.get_pixel(x, y);
let blur_pixel = blurred.get_pixel(x, y);
for i in 0..3 {
let orig_val = orig_pixel[i] as f32;
let diff = orig_val - blur_pixel[i] as f32;
pixel[i] = if diff.abs() > threshold_f {
clamp_u8(orig_val + diff * amount)
} else {
orig_pixel[i]
};
}
}
});
Ok(image)
}
pub fn high_pass_sharpen(mut image: Image, strength: f32) -> Result<Image> {
let original = image.data.get_ref().clone();
let sharpened = apply_convolution(&original, &HIGH_PASS_KERNEL, 3);
let buffer = image.data.get_mut();
*buffer = blend_images(&original, &sharpened, strength);
Ok(image)
}
pub fn enhance_edges(mut image: Image, strength: f32, method: EdgeMethod) -> Result<Image> {
let original = Arc::new(image.data.get_ref().clone());
let edges = Arc::new(apply_edge_detection(&original, method));
let buffer = image.data.get_mut();
buffer
.enumerate_rows_mut()
.par_bridge()
.for_each(|(y, row)| {
for (x, _, pixel) in row {
let orig_pixel = original.get_pixel(x, y);
let edge_strength = calculate_luminance(edges.get_pixel(x, y)) / 255.0;
let enhancement = edge_strength * strength;
let edge_boost = edge_strength * 255.0 * enhancement;
for i in 0..3 {
pixel[i] = clamp_u8(orig_pixel[i] as f32 + edge_boost);
}
}
});
Ok(image)
}
pub fn clarity(mut image: Image, strength: f32, radius: f32) -> Result<Image> {
let original = Arc::new(image.data.get_ref().clone());
let (width, height) = original.dimensions();
let buffer = image.data.get_mut();
let window_size = (radius * 2.0).round() as usize;
let half_window = window_size / 2;
buffer
.enumerate_rows_mut()
.par_bridge()
.for_each(|(y, row)| {
for (x, _, pixel) in row {
let orig_pixel = original.get_pixel(x, y);
let orig_luminance = calculate_luminance(orig_pixel);
let mut local_sum = 0.0;
let mut count = 0;
for dy in -(half_window as i32)..=(half_window as i32) {
for dx in -(half_window as i32)..=(half_window as i32) {
let nx = (x as i32 + dx).clamp(0, width as i32 - 1) as u32;
let ny = (y as i32 + dy).clamp(0, height as i32 - 1) as u32;
local_sum += calculate_luminance(original.get_pixel(nx, ny));
count += 1;
}
}
let local_avg = local_sum / count as f32;
let contrast_diff = orig_luminance - local_avg;
let midtone_factor = if orig_luminance > 64.0 && orig_luminance < 192.0 {
1.0
} else {
0.5
};
let enhancement = contrast_diff * strength * midtone_factor * 0.5;
for i in 0..3 {
pixel[i] = clamp_u8(orig_pixel[i] as f32 + enhancement);
}
}
});
Ok(image)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Image;
use image::{Rgb, RgbImage};
fn create_test_image() -> Image {
let mut img = RgbImage::new(100, 100);
for y in 0..100 {
for x in 0..100 {
let value = if (x / 10 + y / 10) % 2 == 0 { 100 } else { 200 };
img.put_pixel(x, y, Rgb([value, value, value]));
}
}
Image::from_rgb(img).unwrap()
}
#[test]
fn test_unsharp_mask() {
let img = create_test_image();
let result = unsharp_mask(img, 1.0, 1.0, 0);
assert!(result.is_ok());
}
#[test]
fn test_high_pass_sharpen() {
let img = create_test_image();
let result = high_pass_sharpen(img, 0.5);
assert!(result.is_ok());
}
#[test]
fn test_enhance_edges() {
let img = create_test_image();
let result = enhance_edges(img, 1.0, EdgeMethod::Sobel);
assert!(result.is_ok());
}
#[test]
fn test_clarity() {
let img = create_test_image();
let result = clarity(img, 1.0, 2.0);
assert!(result.is_ok());
}
#[test]
fn test_chain_operations() {
let img = create_test_image();
let result = unsharp_mask(img, 0.5, 0.5, 0)
.and_then(|img| high_pass_sharpen(img, 0.3))
.and_then(|img| clarity(img, 0.5, 1.0));
assert!(result.is_ok());
}
}