Skip to content

Instantly share code, notes, and snippets.

@wareya
Last active June 30, 2021 06:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wareya/380e22def6baa563abd70519c4da259d to your computer and use it in GitHub Desktop.
Save wareya/380e22def6baa563abd70519c4da259d to your computer and use it in GitHub Desktop.
hsv saturation-value 2d histographer
/*
[dependencies]
image = "0.23.14"
palette = "0.5.0"
*/
use std::env::args as args;
use image::io::Reader as ImageReader;
use image::{DynamicImage, Rgba, Rgb, GenericImageView, Pixel};
use palette::{LinSrgba, Hsv};
fn unlerp(a : f32, mut b : f32, mut c : f32) -> f32
{
if a == b
{
return 0.0;
}
b -= a;
c -= a;
return c/b;
}
fn main()
{
let output_res : usize = 50;
let args = args().collect::<Vec<String>>();
let img = ImageReader::open(&args[1]).unwrap().decode().unwrap();
let mut out = DynamicImage::new_rgb8(output_res as u32, output_res as u32);
let mut count = vec!(vec!((0.0, 0.0, 0.0, 0.0); output_res); output_res);
for y in 0..img.height()
{
for x in 0..img.width()
{
let tuple_mult = |source : (f32, f32, f32, f32), f : f32| (source.0*f, source.1*f, source.2*f, source.3*f);
let add_tuple = |dest : &mut (f32, f32, f32, f32), source : (f32, f32, f32, f32)|
{
dest.0 += source.0;
dest.1 += source.1;
dest.2 += source.2;
dest.3 += source.3;
};
let rgba : Rgba<u8> = img.get_pixel(x, y);
let srgba = LinSrgba::new(rgba[0] as f32/255.0, rgba[1] as f32/255.0, rgba[2] as f32/255.0, rgba[3] as f32/255.0);
let hsv = Hsv::from(srgba);
let s = hsv.saturation*(output_res as f32 - 1.0);
let v = (1.0-hsv.value)*(output_res as f32 - 1.0);
let s_lerp = unlerp(s.floor(), s.ceil(), s);
let v_lerp = unlerp(v.floor(), v.ceil(), v);
let tuple = (srgba.red*srgba.alpha, srgba.green*srgba.alpha, srgba.blue*srgba.alpha, srgba.alpha);
add_tuple(&mut count[v.floor() as usize][s.floor() as usize], tuple_mult(tuple, (1.0-v_lerp)*(1.0-s_lerp)));
add_tuple(&mut count[v.round() as usize][s.floor() as usize], tuple_mult(tuple, (0.0+v_lerp)*(1.0-s_lerp)));
add_tuple(&mut count[v.floor() as usize][s.round() as usize], tuple_mult(tuple, (1.0-v_lerp)*(0.0+s_lerp)));
add_tuple(&mut count[v.round() as usize][s.round() as usize], tuple_mult(tuple, (0.0+v_lerp)*(0.0+s_lerp)));
}
}
let mut max_count = 0.0;
let mut avg_count = 0.0;
for y in 0..out.height() as usize
{
for x in 0..out.width() as usize
{
avg_count += count[x][y].3;
if count[x][y].3 > max_count
{
max_count = count[x][y].3;
}
}
}
avg_count /= (out.width()*out.height()) as f32 * max_count;
let exp = -(2.0_f32.log(avg_count));
for y in 0..out.height()
{
for x in 0..out.width()
{
let (mut r, mut g, mut b, mut a) = count[y as usize][x as usize];
let mut max = r;
if g > max { max = g }
if b > max { max = b }
if max > 0.0
{
r /= max;
g /= max;
b /= max;
}
a /= max_count;
a = a.powf(exp);
let r = (r*a*255.0).round() as u8;
let g = (g*a*255.0).round() as u8;
let b = (b*a*255.0).round() as u8;
*out.as_mut_rgb8().unwrap().get_pixel_mut(x, y) = *Rgb::from_slice(&[r, g, b]);
}
}
out.resize_exact(output_res as u32*5, output_res as u32*5, image::imageops::FilterType::Nearest).save(&args[2]).unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment