make colour quantization more likely to include outliers
This commit is contained in:
parent
f547a21f00
commit
700bf9a9ef
1 changed files with 39 additions and 12 deletions
51
src/main.rs
51
src/main.rs
|
@ -20,8 +20,13 @@ fn main() {
|
||||||
let image = image.resize(w / 8, h / 8, FilterType::CatmullRom);
|
let image = image.resize(w / 8, h / 8, FilterType::CatmullRom);
|
||||||
image.save("scaled.png").unwrap();
|
image.save("scaled.png").unwrap();
|
||||||
println!("resized");
|
println!("resized");
|
||||||
// let image = quantize_image(image, 6);
|
|
||||||
let image = dither(image, 12);
|
const COLOR_COUNT: usize = 6;
|
||||||
|
|
||||||
|
let quantized_image = quantize_image(image.clone(), COLOR_COUNT);
|
||||||
|
quantized_image.save("q.png").unwrap();
|
||||||
|
|
||||||
|
let image = dither(image, COLOR_COUNT);
|
||||||
image.save("out.png").unwrap();
|
image.save("out.png").unwrap();
|
||||||
println!("saved");
|
println!("saved");
|
||||||
}
|
}
|
||||||
|
@ -34,10 +39,15 @@ fn generate_palette(image: &DynamicImage, count: usize) -> Vec<Rgb<u8>> {
|
||||||
.map(|(_x, _y, a)| a.to_rgb()) //
|
.map(|(_x, _y, a)| a.to_rgb()) //
|
||||||
.collect()];
|
.collect()];
|
||||||
|
|
||||||
|
struct Spread {
|
||||||
|
range: u8,
|
||||||
|
channel: usize,
|
||||||
|
mid_point: u8,
|
||||||
|
}
|
||||||
// divide buckets count-1 times
|
// divide buckets count-1 times
|
||||||
for _i in 0..(count - 1) {
|
for _i in 0..(count - 1) {
|
||||||
// spread amount and channel for each bucket
|
// spread amount and channel for each bucket
|
||||||
let mut spreads: Vec<(u8, usize)> = Vec::new();
|
let mut spreads: Vec<Spread> = Vec::new();
|
||||||
// calculate where each bucket would do its division if it's chosen
|
// calculate where each bucket would do its division if it's chosen
|
||||||
// todo: only calculate this when the bucket is created/modified
|
// todo: only calculate this when the bucket is created/modified
|
||||||
for bucket in &buckets {
|
for bucket in &buckets {
|
||||||
|
@ -54,23 +64,30 @@ fn generate_palette(image: &DynamicImage, count: usize) -> Vec<Rgb<u8>> {
|
||||||
let range_b = max[2] - min[2];
|
let range_b = max[2] - min[2];
|
||||||
let mut widest_channel = 0;
|
let mut widest_channel = 0;
|
||||||
let mut widest_amount = range_r;
|
let mut widest_amount = range_r;
|
||||||
|
let mut widest_middle = min[0] + range_r / 2;
|
||||||
if range_g > widest_amount {
|
if range_g > widest_amount {
|
||||||
widest_amount = range_g;
|
widest_amount = range_g;
|
||||||
widest_channel = 1;
|
widest_channel = 1;
|
||||||
|
widest_middle = min[1] + range_g / 2;
|
||||||
}
|
}
|
||||||
if range_b > widest_amount {
|
if range_b > widest_amount {
|
||||||
widest_amount = range_b;
|
widest_amount = range_b;
|
||||||
widest_channel = 2;
|
widest_channel = 2;
|
||||||
|
widest_middle = min[2] + range_b / 2;
|
||||||
}
|
}
|
||||||
spreads.push((widest_amount, widest_channel))
|
spreads.push(Spread {
|
||||||
|
range: widest_amount,
|
||||||
|
channel: widest_channel,
|
||||||
|
mid_point: widest_middle,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut most_spread_bucket = 0;
|
let mut most_spread_bucket = 0;
|
||||||
let mut highest_spread = 0;
|
let mut highest_spread = 0;
|
||||||
for (i, &(spread, _channel)) in spreads.iter().enumerate() {
|
for (i, spread) in spreads.iter().enumerate() {
|
||||||
if spread > highest_spread {
|
if spread.range > highest_spread {
|
||||||
most_spread_bucket = i;
|
most_spread_bucket = i;
|
||||||
highest_spread = spread;
|
highest_spread = spread.range;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +97,21 @@ fn generate_palette(image: &DynamicImage, count: usize) -> Vec<Rgb<u8>> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let channel = spreads[most_spread_bucket].1;
|
let channel = spreads[most_spread_bucket].channel;
|
||||||
bucket.sort_unstable_by_key(|pixel| pixel.0[channel]);
|
bucket.sort_unstable_by_key(|pixel| pixel.0[channel]);
|
||||||
let halfway = bucket.len() / 2;
|
let mid_value = spreads[most_spread_bucket].mid_point;
|
||||||
let new_bucket = bucket[halfway..].to_owned();
|
let pop_split = bucket.len() / 2;
|
||||||
bucket.truncate(halfway);
|
let pos_split = bucket
|
||||||
|
.iter()
|
||||||
|
.position(|c| c.0[channel] >= mid_value)
|
||||||
|
.unwrap();
|
||||||
|
let split_index = if spreads[most_spread_bucket].range > 150 {
|
||||||
|
pos_split
|
||||||
|
} else {
|
||||||
|
pop_split
|
||||||
|
};
|
||||||
|
let new_bucket = bucket[split_index..].to_owned();
|
||||||
|
bucket.truncate(split_index);
|
||||||
buckets.push(new_bucket);
|
buckets.push(new_bucket);
|
||||||
}
|
}
|
||||||
let mut colors = Vec::new();
|
let mut colors = Vec::new();
|
||||||
|
@ -168,7 +195,7 @@ fn dither(input: DynamicImage, count: usize) -> DynamicImage {
|
||||||
let by = y as usize % 4;
|
let by = y as usize % 4;
|
||||||
let bayer = bayer_f(bx, by);
|
let bayer = bayer_f(bx, by);
|
||||||
|
|
||||||
let ratio = ratio + bayer * 1.5;
|
let ratio = ratio + bayer;
|
||||||
let out_col = if ratio > 0. { best_col } else { second_col };
|
let out_col = if ratio > 0. { best_col } else { second_col };
|
||||||
|
|
||||||
out.put_pixel(x, y, out_col.to_rgba());
|
out.put_pixel(x, y, out_col.to_rgba());
|
||||||
|
|
Loading…
Reference in a new issue