Last active
June 30, 2021 06:28
-
-
Save wareya/380e22def6baa563abd70519c4da259d to your computer and use it in GitHub Desktop.
hsv saturation-value 2d histographer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
[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