use image::{DynamicImage, GrayImage};
use tracing::debug;
pub fn to_grayscale(image: &DynamicImage) -> GrayImage {
debug!(
"Converting {}×{} image to grayscale",
image.width(),
image.height()
);
let gray = image.to_luma8();
debug!("Grayscale conversion complete");
gray
}
#[cfg(test)]
mod tests {
use super::*;
use image::{DynamicImage, Rgb, RgbImage};
fn create_test_rgb_image(width: u32, height: u32, color: Rgb<u8>) -> DynamicImage {
let mut img = RgbImage::new(width, height);
for pixel in img.pixels_mut() {
*pixel = color;
}
DynamicImage::ImageRgb8(img)
}
#[test]
fn test_to_grayscale_pure_red() {
let img = create_test_rgb_image(10, 10, Rgb([255, 0, 0]));
let gray = to_grayscale(&img);
assert_eq!(gray.width(), 10);
assert_eq!(gray.height(), 10);
let pixel_value = gray.get_pixel(0, 0)[0];
assert!(
(54..=55).contains(&pixel_value),
"Expected ~54, got {}",
pixel_value
);
}
#[test]
fn test_to_grayscale_pure_green() {
let img = create_test_rgb_image(10, 10, Rgb([0, 255, 0]));
let gray = to_grayscale(&img);
let pixel_value = gray.get_pixel(0, 0)[0];
assert!(
(182..=183).contains(&pixel_value),
"Expected ~182, got {}",
pixel_value
);
}
#[test]
fn test_to_grayscale_pure_blue() {
let img = create_test_rgb_image(10, 10, Rgb([0, 0, 255]));
let gray = to_grayscale(&img);
let pixel_value = gray.get_pixel(0, 0)[0];
assert!(
(18..=19).contains(&pixel_value),
"Expected ~18, got {}",
pixel_value
);
}
#[test]
fn test_to_grayscale_white() {
let img = create_test_rgb_image(10, 10, Rgb([255, 255, 255]));
let gray = to_grayscale(&img);
let pixel_value = gray.get_pixel(0, 0)[0];
assert_eq!(pixel_value, 255, "White should convert to 255");
}
#[test]
fn test_to_grayscale_black() {
let img = create_test_rgb_image(10, 10, Rgb([0, 0, 0]));
let gray = to_grayscale(&img);
let pixel_value = gray.get_pixel(0, 0)[0];
assert_eq!(pixel_value, 0, "Black should convert to 0");
}
#[test]
fn test_to_grayscale_already_grayscale() {
use image::{GrayImage, Luma};
let mut gray_input = GrayImage::new(10, 10);
for pixel in gray_input.pixels_mut() {
*pixel = Luma([128]);
}
let img = DynamicImage::ImageLuma8(gray_input);
let gray_output = to_grayscale(&img);
let pixel_value = gray_output.get_pixel(0, 0)[0];
assert_eq!(pixel_value, 128, "Grayscale image should pass through");
}
#[test]
fn test_to_grayscale_dimensions_preserved() {
let img = create_test_rgb_image(100, 50, Rgb([128, 128, 128]));
let gray = to_grayscale(&img);
assert_eq!(gray.width(), 100, "Width should be preserved");
assert_eq!(gray.height(), 50, "Height should be preserved");
}
#[test]
fn test_to_grayscale_mixed_colors() {
let img = create_test_rgb_image(5, 5, Rgb([100, 150, 200]));
let gray = to_grayscale(&img);
let pixel_value = gray.get_pixel(0, 0)[0];
assert!(
(142..=144).contains(&pixel_value),
"Expected ~143, got {}",
pixel_value
);
}
}