wip: use DitherMethod instead of individual ditherers
This commit is contained in:
parent
608ebe9ec5
commit
624ad1f101
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -167,6 +167,7 @@ checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
|
|
@ -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"}
|
||||
|
|
21
src/api.rs
21
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<Context> {
|
|||
.route("/process_image", post(process_image))
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug)]
|
||||
pub struct ImageRequest {
|
||||
image: Box<DynamicImage>,
|
||||
|
||||
}
|
||||
|
||||
#[instrument(skip(ctx))]
|
||||
#[axum::debug_handler]
|
||||
async fn set_image(
|
||||
State(ctx): State<Context>,
|
||||
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);
|
||||
{
|
||||
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(())
|
||||
|
|
|
@ -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<dyn Ditherer> {
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -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)?;
|
||||
|
|
Loading…
Reference in a new issue