Add bad random dithering
This commit is contained in:
parent
d332a2560e
commit
452428ce5f
4 changed files with 72 additions and 4 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
|
@ -457,6 +457,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"image",
|
"image",
|
||||||
"palette",
|
"palette",
|
||||||
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -631,6 +632,12 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.66"
|
version = "1.0.66"
|
||||||
|
|
@ -664,6 +671,18 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -672,6 +691,9 @@ name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
||||||
clap = { version = "4.3.19", features = ["derive", "deprecated"] }
|
clap = { version = "4.3.19", features = ["derive", "deprecated"] }
|
||||||
image = "0.24.6"
|
image = "0.24.6"
|
||||||
palette = "0.7.2"
|
palette = "0.7.2"
|
||||||
|
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use palette::{
|
||||||
color_difference::{Ciede2000, EuclideanDistance, HyAb},
|
color_difference::{Ciede2000, EuclideanDistance, HyAb},
|
||||||
Clamp, IntoColor, Lab, Srgb,
|
Clamp, IntoColor, Lab, Srgb,
|
||||||
};
|
};
|
||||||
|
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||||
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
|
|
@ -122,7 +123,8 @@ pub struct AlgoThreshold;
|
||||||
impl<C, D> Algorithm<C, D> for AlgoThreshold
|
impl<C, D> Algorithm<C, D> for AlgoThreshold
|
||||||
where
|
where
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
C: IntoColor<Srgb> + Copy,
|
C: Copy,
|
||||||
|
C: IntoColor<Srgb> ,
|
||||||
D: Difference<C>,
|
D: Difference<C>,
|
||||||
{
|
{
|
||||||
fn run(mut image: RgbaImage, palette: Palette<C>) -> RgbaImage {
|
fn run(mut image: RgbaImage, palette: Palette<C>) -> RgbaImage {
|
||||||
|
|
@ -134,3 +136,40 @@ where
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Fix probability calculation
|
||||||
|
//
|
||||||
|
// Choose probability for each color such that the expected value of a pixel is
|
||||||
|
// its actual color (or as close as possible).
|
||||||
|
//
|
||||||
|
// We want to represent a pixel as a linear combination of palette colors with
|
||||||
|
// factors in the range [0, 1] that sum up to 1. Then we can use those factors
|
||||||
|
// as probabilities. This may not work for every palette.
|
||||||
|
//
|
||||||
|
// As a secondary optimization target, we might want to miminize the amount of
|
||||||
|
// nonzero factors, if possible.
|
||||||
|
pub struct AlgoRandom;
|
||||||
|
|
||||||
|
impl<C, D> Algorithm<C, D> for AlgoRandom
|
||||||
|
where
|
||||||
|
Srgb: IntoColor<C>,
|
||||||
|
C: AsMut<[f32; 3]>,
|
||||||
|
C: Copy,
|
||||||
|
C: IntoColor<Srgb>,
|
||||||
|
D: Difference<C>,
|
||||||
|
{
|
||||||
|
fn run(mut image: RgbaImage, palette: Palette<C>) -> RgbaImage {
|
||||||
|
let mut rng = SmallRng::seed_from_u64(0);
|
||||||
|
let range_radius = 1.0;
|
||||||
|
|
||||||
|
for pixel in image.pixels_mut() {
|
||||||
|
let mut color: C = util::pixel_to_color(*pixel);
|
||||||
|
color.as_mut()[0] += rng.gen_range(-range_radius..=range_radius);
|
||||||
|
color.as_mut()[1] += rng.gen_range(-range_radius..=range_radius);
|
||||||
|
color.as_mut()[2] += rng.gen_range(-range_radius..=range_radius);
|
||||||
|
let color = palette.nearest::<D>(color);
|
||||||
|
util::update_pixel_with_color(pixel, color);
|
||||||
|
}
|
||||||
|
image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -23,8 +23,8 @@ use image::{ImageFormat, RgbaImage};
|
||||||
use mark::{
|
use mark::{
|
||||||
bw,
|
bw,
|
||||||
dither::{
|
dither::{
|
||||||
AlgoThreshold, Algorithm, DiffCiede2000, DiffClamp, DiffEuclid, DiffHyAb, DiffManhattan,
|
AlgoRandom, AlgoThreshold, Algorithm, DiffCiede2000, DiffClamp, DiffEuclid, DiffHyAb,
|
||||||
DiffManhattanSquare, Difference, Palette,
|
DiffManhattan, DiffManhattanSquare, Difference, Palette,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use palette::{color_difference::EuclideanDistance, Clamp, IntoColor, Lab, LinSrgb, Oklab, Srgb};
|
use palette::{color_difference::EuclideanDistance, Clamp, IntoColor, Lab, LinSrgb, Oklab, Srgb};
|
||||||
|
|
@ -69,6 +69,7 @@ impl BwCmd {
|
||||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
enum DitherAlgorithm {
|
enum DitherAlgorithm {
|
||||||
Threshold,
|
Threshold,
|
||||||
|
Random,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
|
|
@ -158,6 +159,7 @@ impl DitherCmd {
|
||||||
fn run_c<C>(self, image: RgbaImage) -> RgbaImage
|
fn run_c<C>(self, image: RgbaImage) -> RgbaImage
|
||||||
where
|
where
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
|
C: AsMut<[f32; 3]>,
|
||||||
C: AsRef<[f32; 3]>,
|
C: AsRef<[f32; 3]>,
|
||||||
C: Clamp,
|
C: Clamp,
|
||||||
C: Copy,
|
C: Copy,
|
||||||
|
|
@ -184,11 +186,15 @@ impl DitherCmd {
|
||||||
fn run_cd<C, D>(self, image: RgbaImage) -> RgbaImage
|
fn run_cd<C, D>(self, image: RgbaImage) -> RgbaImage
|
||||||
where
|
where
|
||||||
Srgb: IntoColor<C>,
|
Srgb: IntoColor<C>,
|
||||||
C: IntoColor<Srgb> + Clamp + Copy,
|
C: AsMut<[f32; 3]>,
|
||||||
|
C: Clamp,
|
||||||
|
C: Copy,
|
||||||
|
C: IntoColor<Srgb>,
|
||||||
D: Difference<C>,
|
D: Difference<C>,
|
||||||
{
|
{
|
||||||
match self.algorithm {
|
match self.algorithm {
|
||||||
DitherAlgorithm::Threshold => self.run_acd::<AlgoThreshold, C, D>(image),
|
DitherAlgorithm::Threshold => self.run_acd::<AlgoThreshold, C, D>(image),
|
||||||
|
DitherAlgorithm::Random => self.run_acd::<AlgoRandom, C, D>(image),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue