From 452428ce5f3e46d91fd98f6933391bfac0bd2afd Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 30 Jul 2023 20:37:18 +0200 Subject: [PATCH] Add bad random dithering --- Cargo.lock | 22 ++++++++++++++++++++++ Cargo.toml | 1 + src/dither.rs | 41 ++++++++++++++++++++++++++++++++++++++++- src/main.rs | 12 +++++++++--- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f9edb8..f31242c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,6 +457,7 @@ dependencies = [ "clap", "image", "palette", + "rand", ] [[package]] @@ -631,6 +632,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -664,6 +671,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 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", ] @@ -672,6 +691,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "rayon" diff --git a/Cargo.toml b/Cargo.toml index 8d995b7..b58b81b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" clap = { version = "4.3.19", features = ["derive", "deprecated"] } image = "0.24.6" palette = "0.7.2" +rand = { version = "0.8.5", features = ["small_rng"] } diff --git a/src/dither.rs b/src/dither.rs index a6ebc0d..10e16c2 100644 --- a/src/dither.rs +++ b/src/dither.rs @@ -12,6 +12,7 @@ use palette::{ color_difference::{Ciede2000, EuclideanDistance, HyAb}, Clamp, IntoColor, Lab, Srgb, }; +use rand::{rngs::SmallRng, Rng, SeedableRng}; use crate::util; @@ -122,7 +123,8 @@ pub struct AlgoThreshold; impl Algorithm for AlgoThreshold where Srgb: IntoColor, - C: IntoColor + Copy, + C: Copy, + C: IntoColor , D: Difference, { fn run(mut image: RgbaImage, palette: Palette) -> RgbaImage { @@ -134,3 +136,40 @@ where 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 Algorithm for AlgoRandom +where + Srgb: IntoColor, + C: AsMut<[f32; 3]>, + C: Copy, + C: IntoColor, + D: Difference, +{ + fn run(mut image: RgbaImage, palette: Palette) -> 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::(color); + util::update_pixel_with_color(pixel, color); + } + image + } +} diff --git a/src/main.rs b/src/main.rs index ec39d37..0eb80a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,8 @@ use image::{ImageFormat, RgbaImage}; use mark::{ bw, dither::{ - AlgoThreshold, Algorithm, DiffCiede2000, DiffClamp, DiffEuclid, DiffHyAb, DiffManhattan, - DiffManhattanSquare, Difference, Palette, + AlgoRandom, AlgoThreshold, Algorithm, DiffCiede2000, DiffClamp, DiffEuclid, DiffHyAb, + DiffManhattan, DiffManhattanSquare, Difference, Palette, }, }; use palette::{color_difference::EuclideanDistance, Clamp, IntoColor, Lab, LinSrgb, Oklab, Srgb}; @@ -69,6 +69,7 @@ impl BwCmd { #[derive(Debug, Clone, Copy, clap::ValueEnum)] enum DitherAlgorithm { Threshold, + Random, } #[derive(Debug, Clone, Copy, clap::ValueEnum)] @@ -158,6 +159,7 @@ impl DitherCmd { fn run_c(self, image: RgbaImage) -> RgbaImage where Srgb: IntoColor, + C: AsMut<[f32; 3]>, C: AsRef<[f32; 3]>, C: Clamp, C: Copy, @@ -184,11 +186,15 @@ impl DitherCmd { fn run_cd(self, image: RgbaImage) -> RgbaImage where Srgb: IntoColor, - C: IntoColor + Clamp + Copy, + C: AsMut<[f32; 3]>, + C: Clamp, + C: Copy, + C: IntoColor, D: Difference, { match self.algorithm { DitherAlgorithm::Threshold => self.run_acd::(image), + DitherAlgorithm::Random => self.run_acd::(image), } }