diff options
author | Devon Sawatsky <novedevo@gmail.com> | 2021-09-27 20:00:12 -0700 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2021-09-28 23:05:18 +0000 |
commit | 9afc0408a36fc887167eb95b206a127104a11ea8 (patch) | |
tree | 497f4839dbda0c0f1c4b140d521eeab86aa19382 /src | |
parent | 296beeac7bb725f463b847ebea9e2c4df283c407 (diff) | |
download | colorsquash-9afc0408a36fc887167eb95b206a127104a11ea8.tar.gz colorsquash-9afc0408a36fc887167eb95b206a127104a11ea8.zip |
factor out the quantizer
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 134 |
1 files changed, 62 insertions, 72 deletions
diff --git a/src/main.rs b/src/main.rs index c421f48..b72e912 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,18 @@ use std::{collections::HashMap, env::args, time::Instant}; use image::io::Reader as ImageReader; -use image::Rgb; +use image::{buffer::Pixels, Rgb}; + +const MAX_COLORS: usize = 256; + +const TOLERANCE: f32 = 0.6; +const RGB_TOLERANCE: f32 = 10.0 * TOLERANCE; fn main() { let before = Instant::now(); let filename = args().nth(1).unwrap(); let outname = args().nth(2).unwrap(); // The percent of RGB value difference a color has to surpass to be considered unique - let tolerance = 0.6; - let rgb_tolerance = 10.0 * tolerance; - let max_colors = 256; println!("File is {}", &filename); @@ -23,80 +25,23 @@ fn main() { println!("Decoded!"); let before_algo = Instant::now(); - let mut colors: HashMap<Rgb<u8>, usize> = HashMap::new(); - - //count pixels - for pixel in image.pixels() { - match colors.get_mut(pixel) { - None => { - colors.insert(*pixel, 1); - } - Some(n) => *n += 1, - } - } - - println!( - "{} has {} colors in it. Sorting most occuring to least...", - filename, - colors.len() - ); - - let mut sorted: Vec<(Rgb<u8>, usize)> = colors.into_iter().collect(); - sorted.sort_by(|(colour1, freq1), (colour2, freq2)| { - freq2 - .cmp(freq1) - .then(colour2[0].cmp(&colour1[0])) - .then(colour2[1].cmp(&colour1[1])) - .then(colour2[2].cmp(&colour1[2])) - }); - - println!("Sorted! Selecting colors..."); - - for (color, count) in sorted.iter().take(10) { - println!("{:?} count {}", color, count); - } - - for (color, count) in sorted.iter().rev().take(10) { - println!("rev {:?} count {}", color, count); - } - - let mut sorted_iter = sorted.iter(); - - let mut selected_colors: Vec<Rgb<u8>> = Vec::with_capacity(max_colors); - selected_colors.push(sorted_iter.next().unwrap().0); - - for (key, _value) in sorted_iter { - if selected_colors.len() < max_colors { - for selected_color in selected_colors.iter() { - if rgb_difference(key, selected_color) > rgb_tolerance { - selected_colors.push(*key); - break; - } - } - } else { - break; - } - } - - for color in selected_colors.iter().take(10) { - println!("selected {:?}", color); - } + let selected_colors = quantize(image.pixels()); println!("Selected {} colors! Creating map...", selected_colors.len()); - let mut color_map: HashMap<Rgb<u8>, Rgb<u8>> = HashMap::with_capacity(sorted.len()); + let mut color_map: HashMap<Rgb<u8>, Rgb<u8>> = HashMap::with_capacity(image.len() / 2); // Selected colors are themselves for color in selected_colors.iter() { color_map.insert(*color, *color); } // Max complexity is O(n * max_colors) - 'sorted_colors: for (key, _value) in sorted.iter() { - let mut min_difference = f64::MAX; - let mut min_difference_color = *key; + for color in image.pixels() { + let mut min_difference = f32::MAX; + let mut min_difference_color = *color; for index in 0..selected_colors.len() { - let difference = rgb_difference(key, unsafe { selected_colors.get_unchecked(index) }); + let difference = rgb_difference(color, unsafe { selected_colors.get_unchecked(index) }); /*if difference == 0.0 { continue 'sorted_colors; }*/ @@ -106,7 +51,7 @@ fn main() { } } - color_map.insert(*key, min_difference_color); + color_map.insert(*color, min_difference_color); } println!("Mapped! Filling in image..."); @@ -120,7 +65,7 @@ fn main() { Instant::now().duration_since(before_algo).as_millis() ); - let mut recounted_colors = Vec::with_capacity(max_colors); + let mut recounted_colors = Vec::with_capacity(MAX_COLORS); // Recount colors for pixel in image.pixels() { if !recounted_colors.contains(pixel) { @@ -131,7 +76,7 @@ fn main() { println!( "Aiming for a max of {} colors, got {}", - max_colors, + MAX_COLORS, recounted_colors.len() ); @@ -142,7 +87,52 @@ fn main() { ); } -fn rgb_difference(a: &Rgb<u8>, z: &Rgb<u8>) -> f64 { +fn quantize(pixels: Pixels<Rgb<u8>>) -> Vec<Rgb<u8>> { + let mut colors: HashMap<Rgb<u8>, usize> = HashMap::new(); + + //count pixels + for pixel in pixels { + match colors.get_mut(pixel) { + None => { + colors.insert(*pixel, 1); + } + Some(n) => *n += 1, + } + } + + let mut sorted: Vec<(Rgb<u8>, usize)> = colors.into_iter().collect(); + sorted.sort_by(|(colour1, freq1), (colour2, freq2)| { + freq2 + .cmp(freq1) + .then(colour2[0].cmp(&colour1[0])) + .then(colour2[1].cmp(&colour1[1])) + .then(colour2[2].cmp(&colour1[2])) + }); + + println!("Sorted! Selecting colors..."); + + let mut sorted_iter = sorted.iter(); + + let mut selected_colors: Vec<Rgb<u8>> = Vec::with_capacity(MAX_COLORS); + selected_colors.push(sorted_iter.next().unwrap().0); + + for (key, _value) in sorted_iter { + if selected_colors.len() < MAX_COLORS { + for selected_color in selected_colors.iter() { + if rgb_difference(key, selected_color) > RGB_TOLERANCE { + selected_colors.push(*key); + break; + } + } + } else { + break; + } + } + + selected_colors +} + +fn rgb_difference(a: &Rgb<u8>, z: &Rgb<u8>) -> f32 { //((a.0[0] as i16 - b.0[0] as i16).abs() + (a.0[1] as i16 - b.0[1] as i16).abs() +(a.0[2] as i16 - b.0[2] as i16).abs()) as u16 //(a.0[0] as i16 - b.0[0] as i16).abs().max((a.0[1] as i16 - b.0[1] as i16).abs().max(a.0[2] as i16 - b.0[2] as i16).abs()) as u16 //(a.0[0] as i16 - b.0[0] as i16).abs().max((a.0[1] as i16 - b.0[1] as i16).abs()).max((a.0[2] as i16 - b.0[2] as i16).abs()) as u16 @@ -153,7 +143,7 @@ fn rgb_difference(a: &Rgb<u8>, z: &Rgb<u8>) -> f64 { let (a, b, c) = pixel_rgb_to_hsv(a); let (d, e, f) = pixel_rgb_to_hsv(z); - (((c - f) * (c - f)) + ((a - d).abs() / 90.0) + (b - e).abs()) as f64 + (((c - f) * (c - f)) + ((a - d).abs() / 90.0) + (b - e).abs()) as f32 } #[warn(clippy::float_cmp)] |