Add more color spaces
This commit is contained in:
parent
f27252760e
commit
07385419a7
2 changed files with 49 additions and 28 deletions
|
|
@ -5,14 +5,11 @@
|
||||||
//! compares two colors. Instead, a version of each algorithm should be compiled
|
//! compares two colors. Instead, a version of each algorithm should be compiled
|
||||||
//! for each color space and difference combination.
|
//! for each color space and difference combination.
|
||||||
|
|
||||||
use std::{
|
use std::marker::PhantomData;
|
||||||
marker::PhantomData,
|
|
||||||
ops::{Add, Mul, Sub},
|
|
||||||
};
|
|
||||||
|
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
use palette::{
|
use palette::{
|
||||||
color_difference::{Ciede2000, EuclideanDistance, HyAb},
|
color_difference::{Ciede2000, HyAb},
|
||||||
Clamp, IntoColor, Lab, Srgb,
|
Clamp, IntoColor, Lab, Srgb,
|
||||||
};
|
};
|
||||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||||
|
|
@ -39,9 +36,12 @@ impl<C: Clamp, D: Difference<C>> Difference<C> for DiffClamp<D> {
|
||||||
|
|
||||||
pub struct DiffEuclid;
|
pub struct DiffEuclid;
|
||||||
|
|
||||||
impl<C: EuclideanDistance<Scalar = f32>> Difference<C> for DiffEuclid {
|
impl<C: AsRef<[f32; 3]>> Difference<C> for DiffEuclid {
|
||||||
fn diff(a: C, b: C) -> f32 {
|
fn diff(a: C, b: C) -> f32 {
|
||||||
a.distance(b)
|
let [a1, a2, a3] = a.as_ref();
|
||||||
|
let [b1, b2, b3] = b.as_ref();
|
||||||
|
let squared = (a1 - b1).powi(2) + (a2 - b2).powi(2) + (a3 - b3).powi(2);
|
||||||
|
squared.sqrt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,11 +167,36 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add<C: AsMut<[f32; 3]>>(mut a: C, mut b: C) -> C {
|
||||||
|
let [a1, a2, a3] = a.as_mut();
|
||||||
|
let [b1, b2, b3] = b.as_mut();
|
||||||
|
*a1 += *b1;
|
||||||
|
*a2 += *b2;
|
||||||
|
*a3 += *b3;
|
||||||
|
a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub<C: AsMut<[f32; 3]>>(mut a: C, mut b: C) -> C {
|
||||||
|
let [a1, a2, a3] = a.as_mut();
|
||||||
|
let [b1, b2, b3] = b.as_mut();
|
||||||
|
*a1 -= *b1;
|
||||||
|
*a2 -= *b2;
|
||||||
|
*a3 -= *b3;
|
||||||
|
a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul<C: AsMut<[f32; 3]>>(mut a: C, b: f32) -> C {
|
||||||
|
let [a1, a2, a3] = a.as_mut();
|
||||||
|
*a1 *= b;
|
||||||
|
*a2 *= b;
|
||||||
|
*a3 *= b;
|
||||||
|
a
|
||||||
|
}
|
||||||
|
|
||||||
fn diffuse_error<C>(image: &mut RgbaImage, error: C, x: u32, y: u32, dx: i32, dy: i32, factor: f32)
|
fn diffuse_error<C>(image: &mut RgbaImage, error: C, x: u32, y: u32, dx: i32, dy: i32, factor: f32)
|
||||||
where
|
where
|
||||||
C: Add<Output = C>,
|
C: AsMut<[f32; 3]>,
|
||||||
C: IntoColor<Srgb>,
|
C: IntoColor<Srgb>,
|
||||||
C: Mul<f32, Output = C>,
|
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
{
|
{
|
||||||
if x == 0 && dx < 0 {
|
if x == 0 && dx < 0 {
|
||||||
|
|
@ -184,7 +209,7 @@ where
|
||||||
let y = (y as i32 + dy) as u32;
|
let y = (y as i32 + dy) as u32;
|
||||||
let Some(pixel) = image.get_pixel_mut_checked(x, y) else{ return; };
|
let Some(pixel) = image.get_pixel_mut_checked(x, y) else{ return; };
|
||||||
let color: C = util::pixel_to_color(*pixel);
|
let color: C = util::pixel_to_color(*pixel);
|
||||||
let color = color + error * factor;
|
let color = add(color, mul(error, factor));
|
||||||
util::update_pixel_with_color(pixel, color);
|
util::update_pixel_with_color(pixel, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,11 +217,9 @@ pub struct AlgoFloydSteinberg;
|
||||||
|
|
||||||
impl<C, D> Algorithm<C, D> for AlgoFloydSteinberg
|
impl<C, D> Algorithm<C, D> for AlgoFloydSteinberg
|
||||||
where
|
where
|
||||||
C: Add<Output = C>,
|
C: AsMut<[f32; 3]>,
|
||||||
C: Copy,
|
C: Copy,
|
||||||
C: IntoColor<Srgb>,
|
C: IntoColor<Srgb>,
|
||||||
C: Mul<f32, Output = C>,
|
|
||||||
C: Sub<Output = C>,
|
|
||||||
D: Difference<C>,
|
D: Difference<C>,
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
{
|
{
|
||||||
|
|
@ -206,7 +229,7 @@ where
|
||||||
let pixel = image.get_pixel(x, y);
|
let pixel = image.get_pixel(x, y);
|
||||||
let before: C = util::pixel_to_color(*pixel);
|
let before: C = util::pixel_to_color(*pixel);
|
||||||
let after = palette.nearest::<D>(before);
|
let after = palette.nearest::<D>(before);
|
||||||
let error = before - after;
|
let error = sub(before, after);
|
||||||
|
|
||||||
util::update_pixel_with_color(image.get_pixel_mut(x, y), after);
|
util::update_pixel_with_color(image.get_pixel_mut(x, y), after);
|
||||||
diffuse_error(&mut image, error, x, y, 1, 0, 7.0 / 16.0);
|
diffuse_error(&mut image, error, x, y, 1, 0, 7.0 / 16.0);
|
||||||
|
|
@ -224,11 +247,9 @@ pub struct AlgoStucki;
|
||||||
|
|
||||||
impl<C, D> Algorithm<C, D> for AlgoStucki
|
impl<C, D> Algorithm<C, D> for AlgoStucki
|
||||||
where
|
where
|
||||||
C: Add<Output = C>,
|
C: AsMut<[f32; 3]>,
|
||||||
C: Copy,
|
C: Copy,
|
||||||
C: IntoColor<Srgb>,
|
C: IntoColor<Srgb>,
|
||||||
C: Mul<f32, Output = C>,
|
|
||||||
C: Sub<Output = C>,
|
|
||||||
D: Difference<C>,
|
D: Difference<C>,
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
{
|
{
|
||||||
|
|
@ -238,7 +259,7 @@ where
|
||||||
let pixel = image.get_pixel(x, y);
|
let pixel = image.get_pixel(x, y);
|
||||||
let before: C = util::pixel_to_color(*pixel);
|
let before: C = util::pixel_to_color(*pixel);
|
||||||
let after = palette.nearest::<D>(before);
|
let after = palette.nearest::<D>(before);
|
||||||
let error = before - after;
|
let error = sub(before, after);
|
||||||
|
|
||||||
util::update_pixel_with_color(image.get_pixel_mut(x, y), after);
|
util::update_pixel_with_color(image.get_pixel_mut(x, y), after);
|
||||||
|
|
||||||
|
|
|
||||||
20
src/main.rs
20
src/main.rs
|
|
@ -14,7 +14,6 @@ use std::{
|
||||||
fmt,
|
fmt,
|
||||||
io::{Cursor, Read, Write},
|
io::{Cursor, Read, Write},
|
||||||
num::ParseIntError,
|
num::ParseIntError,
|
||||||
ops::{Add, Mul, Sub},
|
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
@ -25,10 +24,10 @@ use mark::{
|
||||||
bw,
|
bw,
|
||||||
dither::{
|
dither::{
|
||||||
AlgoFloydSteinberg, AlgoRandom, AlgoStucki, AlgoThreshold, Algorithm, DiffCiede2000,
|
AlgoFloydSteinberg, AlgoRandom, AlgoStucki, AlgoThreshold, Algorithm, DiffCiede2000,
|
||||||
DiffClamp, DiffEuclid, DiffHyAb, DiffManhattan, DiffManhattanSquare, Difference, Palette,
|
DiffClamp, DiffEuclid, DiffHyAb, DiffManhattan, Difference, Palette,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use palette::{color_difference::EuclideanDistance, Clamp, IntoColor, Lab, LinSrgb, Oklab, Srgb};
|
use palette::{Clamp, IntoColor, Lab, Lch, LinSrgb, Luv, Okhsl, Okhsv, Oklab, Srgb};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
enum BwMethod {
|
enum BwMethod {
|
||||||
|
|
@ -80,7 +79,11 @@ enum DitherColorSpace {
|
||||||
Srgb,
|
Srgb,
|
||||||
LinSrgb,
|
LinSrgb,
|
||||||
Cielab,
|
Cielab,
|
||||||
|
Cieluv,
|
||||||
|
Cielch,
|
||||||
Oklab,
|
Oklab,
|
||||||
|
Okhsl,
|
||||||
|
Okhsv,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
|
|
@ -153,22 +156,22 @@ impl DitherCmd {
|
||||||
DitherColorSpace::Srgb => self.run_c::<Srgb>(image),
|
DitherColorSpace::Srgb => self.run_c::<Srgb>(image),
|
||||||
DitherColorSpace::LinSrgb => self.run_c::<LinSrgb>(image),
|
DitherColorSpace::LinSrgb => self.run_c::<LinSrgb>(image),
|
||||||
DitherColorSpace::Cielab => self.run_c::<Lab>(image),
|
DitherColorSpace::Cielab => self.run_c::<Lab>(image),
|
||||||
|
DitherColorSpace::Cieluv => self.run_c::<Lch>(image),
|
||||||
|
DitherColorSpace::Cielch => self.run_c::<Luv>(image),
|
||||||
DitherColorSpace::Oklab => self.run_c::<Oklab>(image),
|
DitherColorSpace::Oklab => self.run_c::<Oklab>(image),
|
||||||
|
DitherColorSpace::Okhsl => self.run_c::<Okhsl>(image),
|
||||||
|
DitherColorSpace::Okhsv => self.run_c::<Okhsv>(image),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_c<C>(self, image: RgbaImage) -> RgbaImage
|
fn run_c<C>(self, image: RgbaImage) -> RgbaImage
|
||||||
where
|
where
|
||||||
C: Add<C, Output = C>,
|
|
||||||
C: AsMut<[f32; 3]>,
|
C: AsMut<[f32; 3]>,
|
||||||
C: AsRef<[f32; 3]>,
|
C: AsRef<[f32; 3]>,
|
||||||
C: Clamp,
|
C: Clamp,
|
||||||
C: Copy,
|
C: Copy,
|
||||||
C: EuclideanDistance<Scalar = f32>,
|
|
||||||
C: IntoColor<Lab>,
|
C: IntoColor<Lab>,
|
||||||
C: IntoColor<Srgb>,
|
C: IntoColor<Srgb>,
|
||||||
C: Mul<f32, Output = C>,
|
|
||||||
C: Sub<C, Output = C>,
|
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
{
|
{
|
||||||
use DitherDifference::*;
|
use DitherDifference::*;
|
||||||
|
|
@ -186,13 +189,10 @@ impl DitherCmd {
|
||||||
|
|
||||||
fn run_cd<C, D>(self, image: RgbaImage) -> RgbaImage
|
fn run_cd<C, D>(self, image: RgbaImage) -> RgbaImage
|
||||||
where
|
where
|
||||||
C: Add<C, Output = C>,
|
|
||||||
C: AsMut<[f32; 3]>,
|
C: AsMut<[f32; 3]>,
|
||||||
C: Clamp,
|
C: Clamp,
|
||||||
C: Copy,
|
C: Copy,
|
||||||
C: IntoColor<Srgb>,
|
C: IntoColor<Srgb>,
|
||||||
C: Mul<f32, Output = C>,
|
|
||||||
C: Sub<C, Output = C>,
|
|
||||||
D: Difference<C>,
|
D: Difference<C>,
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue