Added a bunch of comments explaining things

This commit is contained in:
G2-Games 2023-10-26 17:02:17 -05:00
parent 934ef1b606
commit a618f5b23f

View file

@ -1,7 +1,7 @@
use std::env; use std::env;
use image::io::Reader as ImageReader; use image::io::Reader as ImageReader;
use palette::color_difference::HyAb; use palette::color_difference::{HyAb, EuclideanDistance};
use palette::{LinSrgb, Srgb, Luv, IntoColor}; use palette::{LinSrgb, Srgb, Luv, IntoColor};
use color_thief::ColorFormat; use color_thief::ColorFormat;
@ -38,7 +38,7 @@ fn main() {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
let colors = get_palette( let colors = get_palette(
&img_bytes, &img_bytes,
20, 10,
num, num,
&(img.width() as usize), &(img.width() as usize),
); );
@ -61,61 +61,97 @@ struct Color {
b: u8, b: u8,
} }
const MIN_DIST: f32 = 30.0;
struct ColorData {
luv: Luv,
rgb: [u8; 4],
position: (usize, usize),
weight: usize
}
fn get_palette( fn get_palette(
bytes: &[u8], bytes: &[u8],
step_by: usize, step_by: usize,
target_len: &usize, target_len: &usize,
width: &usize, width: &usize,
) -> Vec<Color> { ) -> Vec<Color> {
const MIN_DIST: f32 = 30.0;
// Generate a Vec to store the color data // Generate a Vec to store the color data
let mut colors: Vec<(Luv, usize, [u8; 4], (usize, usize))> = Vec::new(); let mut colors: Vec<ColorData> = Vec::new();
// Hold the previous color // Hold the previous color
let mut prev_color: Luv = LinSrgb::from_components((0.0, 0.0, 0.0)).into_color(); let mut prev_color: Luv = LinSrgb::from_components((0.0, 0.0, 0.0)).into_color();
// Take chunks of 4 bytes (1 pixel), skipping `step_by` pixels
'main_img: for (i, pixel) in bytes.chunks_exact(4).enumerate().step_by(step_by) { 'main_img: for (i, pixel) in bytes.chunks_exact(4).enumerate().step_by(step_by) {
// If the pixel is all black or entirely transparent, skip it
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) || pixel[3] == 0 { if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) || pixel[3] == 0 {
continue; continue;
} }
// Create a new `srgb` and `luv` mapped color for the pixel
let pixel_srgb: LinSrgb<f32> = Srgb::new(pixel[0], pixel[1], pixel[2]).into(); let pixel_srgb: LinSrgb<f32> = Srgb::new(pixel[0], pixel[1], pixel[2]).into();
let pixel_luv: Luv = pixel_srgb.into_color(); let pixel_luv: Luv = pixel_srgb.into_color();
//println!("{}", pixel_luv.distance(prev_color)); // Get the distance to the previous color, if it's too close, skip it because we don't care
if pixel_luv.hybrid_distance(prev_color) < 50.0 { if pixel_luv.hybrid_distance(prev_color) < 60.0 {
prev_color += (pixel_luv + prev_color) / 2.0; //prev_color = pixel_luv;
continue; continue;
} }
// Get the X and Y position in the image, all we have with `i` is
// the position within the bytes of the image
let geo_pos = (i % width, i / width); let geo_pos = (i % width, i / width);
// Loop over all collected colors
for color in &mut colors { for color in &mut colors {
let dist = pixel_luv.hybrid_distance(color.0);
//println!("{}", dist); // Get the distance to the other color in colorspace `Luv`
let geo_dist = geometric_distance(geo_pos, color.3); // and then the distance between the pixels on the X Y plane of the image
let dist = pixel_luv.hybrid_distance(color.luv);
let geo_dist = geometric_distance(geo_pos, color.position);
// If the colorspace distance is below MIN_DIST...
if dist < MIN_DIST { if dist < MIN_DIST {
color.0 = (pixel_luv + color.0) / 2.0;
color.2 = average(color.2, pixel.try_into().unwrap()); // Take the previous pixel's color and the current one
color.1 += ((100.0 * dist) as f64 + (100.0 * geo_dist.recip())) as usize; // and add them together, then divide by 2 (to get a rough average)
prev_color = color.0; color.luv = (pixel_luv + color.luv) / 2.0;
continue 'main_img; color.rgb = average(color.rgb, pixel.try_into().unwrap());
// Calculate the weight using this math that I just experimented with
// until it worked
color.weight += ((100.0 * dist) as f64 + (100.0 * geo_dist.recip())) as usize;
prev_color = color.luv;
continue 'main_img; // skips the outer loop
} }
} }
colors.push((pixel_luv, 1, pixel.try_into().unwrap(), geo_pos))
// If the loop above does not `continue`, then the color wasn't found
// so add it to the vec!
colors.push(ColorData {
luv: pixel_luv,
rgb: pixel.try_into().unwrap(),
position: geo_pos,
weight: 1
})
} }
colors.par_sort_by_key(|x| x.1); // Sorts by the weight
colors.sort_by_key(|color| color.weight);
colors.reverse(); colors.reverse();
let mut colors_vec: Vec<Color> = colors let mut colors_vec: Vec<Color> = colors
.par_iter() .iter()
.map(|x| .map(|color|
{ {
//println!("\x1b[38;2;{};{};{}m██\x1b[0m - {}", x.2[0], x.2[1], x.2[2], x.1); //println!("\x1b[38;2;{};{};{}m██\x1b[0m - {}", x.2[0], x.2[1], x.2[2], x.1);
Color { Color {
r: x.2[0], r: color.rgb[0],
g: x.2[1], g: color.rgb[1],
b: x.2[2], b: color.rgb[2],
} }
}) })
.collect(); .collect();