use crate::error::{Result, VisionError};
use image::{DynamicImage, GrayImage, Luma};
#[allow(dead_code)]
pub fn gamma_correction(img: &DynamicImage, gamma: f32) -> Result<DynamicImage> {
if gamma <= 0.0 {
return Err(VisionError::InvalidParameter(
"Gamma must be positive".to_string(),
));
}
let lut: Vec<u8> = (0..256)
.map(|i| {
let normalized = i as f32 / 255.0;
let corrected = normalized.powf(gamma);
(corrected * 255.0).clamp(0.0, 255.0) as u8
})
.collect();
match img {
DynamicImage::ImageLuma8(gray) => {
let mut result = GrayImage::new(gray.width(), gray.height());
for (x, y, pixel) in gray.enumerate_pixels() {
result.put_pixel(x, y, Luma([lut[pixel[0] as usize]]));
}
Ok(DynamicImage::ImageLuma8(result))
}
_ => {
let rgb = img.to_rgb8();
let mut result = image::RgbImage::new(rgb.width(), rgb.height());
for (x, y, pixel) in rgb.enumerate_pixels() {
result.put_pixel(
x,
y,
image::Rgb([
lut[pixel[0] as usize],
lut[pixel[1] as usize],
lut[pixel[2] as usize],
]),
);
}
Ok(DynamicImage::ImageRgb8(result))
}
}
}
#[allow(dead_code)]
pub fn auto_gamma_correction(img: &DynamicImage, targetbrightness: f32) -> Result<DynamicImage> {
if targetbrightness <= 0.0 || targetbrightness >= 1.0 {
return Err(VisionError::InvalidParameter(
"Target _brightness must be between 0.0 and 1.0".to_string(),
));
}
let gray = img.to_luma8();
let mut sum = 0u64;
for pixel in gray.pixels() {
sum += pixel[0] as u64;
}
let pixel_count = (gray.width() * gray.height()) as f32;
let mean_brightness = (sum as f32) / (pixel_count * 255.0);
if mean_brightness < 0.001 {
return gamma_correction(img, 0.5); }
let gamma = mean_brightness.ln() / targetbrightness.ln();
let gamma = gamma.clamp(0.1, 10.0);
gamma_correction(img, gamma)
}
#[allow(dead_code)]
pub fn adaptive_gamma_correction(
img: &DynamicImage,
window_size: u32,
gamma_range: (f32, f32),
) -> Result<DynamicImage> {
let (min_gamma, max_gamma) = gamma_range;
if min_gamma <= 0.0 || max_gamma <= 0.0 || min_gamma > max_gamma {
return Err(VisionError::InvalidParameter(
"Invalid gamma _range".to_string(),
));
}
if window_size == 0 {
return Err(VisionError::InvalidParameter(
"Window _size must be positive".to_string(),
));
}
let gray = img.to_luma8();
let (width, height) = (gray.width(), gray.height());
let mut result = GrayImage::new(width, height);
let half_window = window_size / 2;
for y in 0..height {
for x in 0..width {
let x_start = x.saturating_sub(half_window);
let x_end = (x + half_window + 1).min(width);
let y_start = y.saturating_sub(half_window);
let y_end = (y + half_window + 1).min(height);
let mut sum = 0u32;
let mut count = 0u32;
for ly in y_start..y_end {
for lx in x_start..x_end {
sum += gray.get_pixel(lx, ly)[0] as u32;
count += 1;
}
}
let local_mean = (sum as f32) / (count as f32 * 255.0);
let gamma = min_gamma + (max_gamma - min_gamma) * local_mean;
let pixel_value = gray.get_pixel(x, y)[0];
let normalized = pixel_value as f32 / 255.0;
let corrected = normalized.powf(gamma);
let output_value = (corrected * 255.0).clamp(0.0, 255.0) as u8;
result.put_pixel(x, y, Luma([output_value]));
}
}
Ok(DynamicImage::ImageLuma8(result))
}
#[cfg(test)]
mod tests {
use super::*;
use image::DynamicImage;
#[test]
fn test_gamma_correction_identity() {
let img = DynamicImage::new_luma8(10, 10);
let result = gamma_correction(&img, 1.0).expect("Operation failed");
match (img, result) {
(DynamicImage::ImageLuma8(orig), DynamicImage::ImageLuma8(res)) => {
for (o, r) in orig.pixels().zip(res.pixels()) {
assert_eq!(o[0], r[0]);
}
}
_ => panic!("Unexpected image types"),
}
}
#[test]
fn test_gamma_correction_invalid() {
let img = DynamicImage::new_luma8(10, 10);
assert!(gamma_correction(&img, 0.0).is_err());
assert!(gamma_correction(&img, -1.0).is_err());
}
#[test]
fn test_gamma_correction_brightening() {
let mut img = GrayImage::new(10, 10);
for pixel in img.pixels_mut() {
*pixel = Luma([50]); }
let dynamic_img = DynamicImage::ImageLuma8(img);
let result = gamma_correction(&dynamic_img, 0.5).expect("Operation failed");
match result {
DynamicImage::ImageLuma8(res) => {
let first_pixel = res.get_pixel(0, 0)[0];
assert!(first_pixel > 50);
}
_ => panic!("Unexpected image type"),
}
}
#[test]
fn test_auto_gamma_correction() {
let mut img = GrayImage::new(10, 10);
for pixel in img.pixels_mut() {
*pixel = Luma([50]);
}
let dynamic_img = DynamicImage::ImageLuma8(img);
let result = auto_gamma_correction(&dynamic_img, 0.5).expect("Operation failed");
assert!(result.as_luma8().is_some());
}
#[test]
fn test_auto_gamma_invalid_target() {
let img = DynamicImage::new_luma8(10, 10);
assert!(auto_gamma_correction(&img, 0.0).is_err());
assert!(auto_gamma_correction(&img, 1.0).is_err());
assert!(auto_gamma_correction(&img, -0.5).is_err());
assert!(auto_gamma_correction(&img, 1.5).is_err());
}
#[test]
fn test_adaptive_gamma_correction() {
let img = DynamicImage::new_luma8(20, 20);
let result = adaptive_gamma_correction(&img, 5, (0.5, 2.0)).expect("Operation failed");
assert!(result.as_luma8().is_some());
assert_eq!(result.width(), 20);
assert_eq!(result.height(), 20);
}
#[test]
fn test_adaptive_gamma_invalid_params() {
let img = DynamicImage::new_luma8(10, 10);
assert!(adaptive_gamma_correction(&img, 0, (0.5, 2.0)).is_err());
assert!(adaptive_gamma_correction(&img, 5, (0.0, 2.0)).is_err());
assert!(adaptive_gamma_correction(&img, 5, (2.0, 1.0)).is_err());
}
}