image-light-freq-conv

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

main.rs (4596B)


      1 use image::{DynamicImage, GenericImageView, Luma, Pixel, Rgba};
      2 use std::env;
      3 use std::fs::File;
      4 use std::path::Path;
      5 
      6 fn load_image(path: &Path) -> Result<Vec<Vec<Rgba<u8>>>, image::ImageError> {
      7     let img: DynamicImage = image::open(path)?;
      8     let (width, height) = img.dimensions();
      9 
     10     let mut pixel_array: Vec<Vec<Rgba<u8>>> = Vec::with_capacity(height as usize);
     11     for y in 0..height {
     12         let mut row: Vec<Rgba<u8>> = Vec::with_capacity(width as usize);
     13         for x in 0..width {
     14             let pixel: Rgba<u8> = img.get_pixel(x, y).to_rgba();
     15             row.push(pixel);
     16         }
     17         pixel_array.push(row);
     18     }
     19     Ok(pixel_array)
     20 }
     21 
     22 const MIN_WAVELENGTH_NM: f64 = 400.0; // 紫の端
     23 const MAX_WAVELENGTH_NM: f64 = 700.0; // 赤の端
     24 const GREEN_WAVELENGTH_NM: f64 = 550.0; // 緑色の中央付近
     25 //
     26 fn rgba_to_approx_wavelength(pixel: Rgba<u8>) -> f64 {
     27     let r_u8 = pixel.0[0] as f64;
     28     let g_u8 = pixel.0[1] as f64;
     29     let b_u8 = pixel.0[2] as f64;
     30 
     31     let r = r_u8 / 255.0;
     32     let g = g_u8 / 255.0;
     33     let b = b_u8 / 255.0;
     34 
     35     let max = r.max(g).max(b);
     36     let min = r.min(g).min(b);
     37     let chroma = max - min;
     38 
     39     let hue_deg = if chroma == 0.0 {
     40         return GREEN_WAVELENGTH_NM;
     41     } else {
     42         let mut h_prime = if max == r {
     43             (g - b) / chroma
     44         } else if max == g {
     45             (b - r) / chroma + 2.0
     46         } else {
     47             (r - g) / chroma + 4.0
     48         };
     49 
     50         if h_prime < 0.0 {
     51             h_prime += 6.0;
     52         }
     53 
     54         h_prime * 60.0
     55     };
     56 
     57     let h_norm = hue_deg / 360.0;
     58 
     59     let wavelength_nm;
     60 
     61     if h_norm <= 1.0 / 6.0 {
     62         // 赤〜黄 (0°〜60°) : 700nm から 590nm
     63         let t = h_norm * 6.0; // tは 0.0 から 1.0
     64         wavelength_nm = MAX_WAVELENGTH_NM - t * (MAX_WAVELENGTH_NM - 590.0);
     65     } else if h_norm <= 2.0 / 6.0 {
     66         // 黄〜緑 (60°〜120°) : 590nm から 550nm
     67         let t = (h_norm - 1.0 / 6.0) * 6.0; // tは 0.0 から 1.0
     68         wavelength_nm = 590.0 - t * (590.0 - GREEN_WAVELENGTH_NM);
     69     } else if h_norm <= 4.0 / 6.0 {
     70         // 緑〜青 (120°〜240°) : 550nm から 450nm
     71         let t = (h_norm - 2.0 / 6.0) * 3.0; // tは 0.0 から 1.0
     72         wavelength_nm = GREEN_WAVELENGTH_NM - t * (GREEN_WAVELENGTH_NM - 450.0);
     73     } else {
     74         // 青〜赤 (240°〜360°) : 450nm から 700nm
     75 
     76         let t_total = h_norm - 4.0 / 6.0; // 240度 (4/6) から 1.0 までの範囲
     77         let t_norm = t_total * 3.0; // t_norm は 0.0 から 1.0
     78 
     79         wavelength_nm = 450.0 - t_norm * (450.0 - MIN_WAVELENGTH_NM);
     80 
     81         return wavelength_nm.clamp(MIN_WAVELENGTH_NM, MAX_WAVELENGTH_NM);
     82     }
     83 
     84     wavelength_nm.clamp(MIN_WAVELENGTH_NM, MAX_WAVELENGTH_NM)
     85 }
     86 
     87 fn wavelength_to_grayscale(wavelength_nm: f64) -> u8 {
     88     let clamped_wavelength_nm = wavelength_nm.clamp(MIN_WAVELENGTH_NM, MAX_WAVELENGTH_NM);
     89 
     90     let range_length = MAX_WAVELENGTH_NM - MIN_WAVELENGTH_NM;
     91 
     92     let normalized_position = (clamped_wavelength_nm - MIN_WAVELENGTH_NM) / range_length;
     93 
     94     let grayscale_f64 = normalized_position * 255.0;
     95 
     96     grayscale_f64.round() as u8
     97 }
     98 
     99 fn main() -> Result<(), Box<dyn std::error::Error>> {
    100     let args: Vec<String> = env::args().collect();
    101     if args.len() == 1 || args.len() > 4 {
    102         println!(
    103             "args are not collect. use this 'image-light-freq-con <inputFilePath> <outputFilePath>'"
    104         );
    105         return Ok(());
    106     }
    107     let path = Path::new(&args[1]);
    108     let pixels = match load_image(path) {
    109         Ok(pixels) => pixels,
    110         Err(e) => {
    111             println!("Could not load image. {:?}", e);
    112             return Ok(());
    113         }
    114     };
    115 
    116     let height = pixels.len() as u32;
    117     let width = pixels[0].len() as u32;
    118 
    119     let mut grascale_img: Vec<Vec<u8>> = Vec::with_capacity(height as usize);
    120 
    121     for row in pixels.iter() {
    122         let mut grascale_img_row: Vec<u8> = Vec::with_capacity(width as usize);
    123         for pixel in row.iter() {
    124             grascale_img_row.push(wavelength_to_grayscale(rgba_to_approx_wavelength(*pixel)));
    125         }
    126         grascale_img.push(grascale_img_row);
    127     }
    128 
    129     let grayscale_data_flat: Vec<u8> = grascale_img
    130         .into_iter()
    131         .flat_map(|row| row.into_iter())
    132         .collect();
    133 
    134     let output_img =
    135         image::ImageBuffer::<Luma<u8>, _>::from_raw(width, height, grayscale_data_flat)
    136             .ok_or("Failed to create image buffer")?;
    137 
    138     let output_filename = Path::new(&args[2]);
    139 
    140     let mut output_file = File::create(output_filename)?;
    141     output_img.write_to(&mut output_file, image::ImageFormat::Png)?;
    142 
    143     Ok(())
    144 }