From 624ad1f101d26de716daf35a27e2fc56d720717d Mon Sep 17 00:00:00 2001 From: saji Date: Mon, 29 Jul 2024 16:39:49 -0500 Subject: [PATCH] wip: use DitherMethod instead of individual ditherers --- Cargo.lock | 1 + Cargo.toml | 2 +- src/api.rs | 23 ++++++++++--------- src/imageproc.rs | 59 ++++++++++++++++++++++++++++-------------------- src/main.rs | 6 ++--- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f458a8b..28a622f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,7 @@ checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bytes", "futures-util", "http", diff --git a/Cargo.toml b/Cargo.toml index c46bb1e..81c09f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.86" -axum = { version = "0.7.5", features = ["multipart"] } +axum = { version = "0.7.5", features = ["macros", "multipart"] } axum-macros = "0.4.1" clap = { version = "4.5.7", features = ["derive"] } epd-waveshare = { git = "https://github.com/caemor/epd-waveshare.git"} diff --git a/src/api.rs b/src/api.rs index 21867d5..2e794ff 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,13 +1,12 @@ -use crate::imageproc::{DiffusionMatrix, Ditherer, EInkImage, ErrorDiffusionDither}; +use crate::imageproc::{DitherMethod, EInkImage, }; use axum::extract::Multipart; use axum::http::StatusCode; use axum::response::IntoResponse; use axum::{extract::State, response::Response, routing::post, Router}; -use image::ImageReader; +use image::{DynamicImage, ImageReader}; use std::io::Cursor; use std::time::Duration; use tracing::{debug, error, info, instrument}; -use serde::{Serialize, Deserialize}; use crate::display::EInkPanel; use std::sync::Arc; @@ -90,13 +89,14 @@ pub fn router() -> Router { .route("/process_image", post(process_image)) } - - -#[derive(Serialize, Deserialize)] +#[derive(Debug)] pub struct ImageRequest { + image: Box, + } #[instrument(skip(ctx))] +#[axum::debug_handler] async fn set_image( State(ctx): State, mut parts: Multipart, @@ -111,14 +111,15 @@ async fn set_image( .expect("Cursor io never fails"); debug!("Guessed format: {:?}", reader.format()); - let image = reader.decode()?; let mut buf = EInkImage::default(); - let mut dither = ErrorDiffusionDither::new(DiffusionMatrix::Atkinson); - dither.dither(&image.into(), &mut buf); + { + let image = reader.decode()?; + let mut dither = DitherMethod::Atkinson.get_ditherer(); + dither.dither(&image.into(), &mut buf); + } let cmd = DisplaySetCommand { img: Box::new(buf) }; ctx.display_channel - .send_timeout(cmd, Duration::from_secs(10)) - .await?; + .send_timeout(cmd, Duration::from_secs(10)).await?; } } Ok(()) diff --git a/src/imageproc.rs b/src/imageproc.rs index f3484a9..0ccb289 100644 --- a/src/imageproc.rs +++ b/src/imageproc.rs @@ -1,5 +1,6 @@ use image::{GrayImage, ImageBuffer, Luma, RgbImage}; use palette::FromColor; +use serde::{Deserialize, Serialize}; use tracing::instrument; use image::Rgb as imgRgb; @@ -19,6 +20,30 @@ const DISPLAY_PALETTE: [Srgb; 7] = [ Srgb::new(0.757, 0.443, 0.165), // Orange ]; +pub enum DitherPalette {} + +#[derive(strum::EnumString, Serialize, Deserialize, PartialEq, Eq)] +pub enum DitherMethod { + NearestNeighbor, + FloydSteinberg, + Atkinson, + Stuki, + Sierra, +} + +impl DitherMethod { + #[must_use] + pub fn get_ditherer(&self) -> Box { + match self { + Self::NearestNeighbor => Box::new(NNDither {}), + Self::Atkinson => Box::new(ErrorDiffusionDither::new(ATKINSON_DITHER_POINTS)), + Self::FloydSteinberg => Box::new(ErrorDiffusionDither::new(FLOYD_STEINBERG_POINTS)), + Self::Stuki => Box::new(ErrorDiffusionDither::new(STUKI_DITHER_POINTS)), + Self::Sierra => Box::new(ErrorDiffusionDither::new(SIERRA_DITHER_POINTS)), + } + } +} + pub enum ProcessingError { DitherError, PaletteIndexError(usize), @@ -125,6 +150,7 @@ fn compute_error_adjusted_color(orig: &Lab, err: &Lab, weight: f32) -> Lab { /// ``DiffusionPoint`` is part of the diffusion matrix, represented by a shift in x and y and an error /// scaling factor. +#[derive(Debug)] struct DiffusionPoint { xshift: i32, yshift: i32, @@ -186,35 +212,19 @@ static STUKI_DITHER_POINTS: &[DiffusionPoint] = &[ DiffusionPoint::new(1, 2, 1.0 / 42.0), ]; -#[derive(Debug)] -pub enum DiffusionMatrix { - FloydSteinberg, - Atkinson, - Sierra, - Stuki, -} - -impl DiffusionMatrix { - fn value(&self) -> &'static [DiffusionPoint] { - match *self { - Self::FloydSteinberg => FLOYD_STEINBERG_POINTS, - Self::Atkinson => ATKINSON_DITHER_POINTS, - Self::Sierra => SIERRA_DITHER_POINTS, - Self::Stuki => STUKI_DITHER_POINTS, - } - } -} +pub type DiffusionMatrix<'a> = &'a [DiffusionPoint]; #[derive(Debug)] -pub struct ErrorDiffusionDither(DiffusionMatrix); -impl ErrorDiffusionDither { +pub struct ErrorDiffusionDither<'a>(&'a [DiffusionPoint]); + +impl<'a> ErrorDiffusionDither<'a> { #[must_use] - pub const fn new(dm: DiffusionMatrix) -> Self { + pub const fn new(dm: DiffusionMatrix<'a>) -> Self { Self(dm) } } -impl Ditherer for ErrorDiffusionDither { +impl<'a> Ditherer for ErrorDiffusionDither<'a> { #[instrument] fn dither(&mut self, img: &RgbImage, output: &mut EInkImage) { // create a copy of the image in Lab space, mutable. @@ -226,8 +236,8 @@ impl Ditherer for ErrorDiffusionDither { for pix in srgb { temp_img.push(pix.into_format().into_color()); } - // now we take our units. + // TODO: rework this to make more sense. for y in 0..ysize { for x in 0..xsize { let index = coord_to_idx(x, y, xsize); @@ -236,7 +246,8 @@ impl Ditherer for ErrorDiffusionDither { // set the color in the output buffer. *output.buf.get_mut(index).unwrap() = nearest; // take the error, and propagate it. - for point in self.0.value() { + for point in self.0 { + // bounds checking. let Some(target_x) = x.checked_add_signed(point.xshift) else { continue; }; diff --git a/src/main.rs b/src/main.rs index a388653..8db90dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ pub mod display; pub mod errors; pub mod imageproc; -use crate::display::{FakeEInk, EInkPanel, Wrapper}; -use crate::imageproc::{DiffusionMatrix, Ditherer, EInkImage, ErrorDiffusionDither}; +use crate::display::{EInkPanel, FakeEInk, Wrapper}; +use crate::imageproc::{DitherMethod, EInkImage}; use clap::{Parser, Subcommand}; use image::RgbImage; use tracing::{error, info}; @@ -39,7 +39,7 @@ async fn main() -> anyhow::Result<()> { let mut display = FakeEInk {}; let mut eink_buf = EInkImage::default(); - let mut dither = ErrorDiffusionDither::new(DiffusionMatrix::Atkinson); + let mut dither = DitherMethod::Atkinson.get_ditherer(); dither.dither(&img, &mut eink_buf); display.display(&eink_buf)?;