Skip to content

Instantly share code, notes, and snippets.

@bczhc
Created June 26, 2024 03:55
Show Gist options
  • Save bczhc/38e1d00c0e9e57485902a60e14521756 to your computer and use it in GitHub Desktop.
Save bczhc/38e1d00c0e9e57485902a60e14521756 to your computer and use it in GitHub Desktop.
Rust grayscale to binary FS dithering
use image::{GenericImageView, GrayImage};
fn main() -> anyhow::Result<()> {
let out_path = "/home/bczhc/Pictures/out.png";
let image = image::open("/home/bczhc/Pictures/唤冬兽.png")?;
let grayscale = image.grayscale().into_luma8();
let (width, height) = grayscale.dimensions();
let (width, height) = (width as usize, height as usize);
let mut pixels = vec![0_f64; width * height];
fn closest_palette_color(pixel: f64) -> f64 {
if pixel <= 127.0 {
0.0
} else {
255.0
}
}
macro_rules! pixel {
($x:expr, $y:expr) => {
pixels[$x + $y * width]
};
}
for (x, y, p) in grayscale.enumerate_pixels() {
pixel!(x as usize, y as usize) = p.0[0] as f64;
}
for y in 0..height {
for x in 0..width {
let old_pixel = pixel!(x, y);
let new_pixel = closest_palette_color(old_pixel);
pixel!(x, y) = new_pixel;
let quant_error = old_pixel - new_pixel;
if x + 1 < width {
pixel!(x + 1, y) += quant_error * 7.0 / 16.0;
}
if x > 1 && y + 1 < height {
pixel!(x - 1, y + 1) += quant_error * 3.0 / 16.0;
}
if y + 1 < height {
pixel!(x, y + 1) += quant_error * 5.0 / 16.0;
}
if x + 1 < width && y + 1 < height {
pixel!(x + 1, y + 1) += quant_error * 1.0 / 16.0;
}
}
}
let dithered = GrayImage::from_fn(width as u32, height as u32, |x, y| {
[pixel!(x as usize, y as usize) as u8].into()
});
dithered.save(out_path)?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment