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 }