Skip to content

Commit

Permalink
feat: added quantization operation
Browse files Browse the repository at this point in the history
  • Loading branch information
SalOne22 committed Mar 10, 2024
1 parent 7d76f07 commit 25d0d78
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 0 deletions.
91 changes: 91 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ opt-level = "z"

[dependencies]
fast_image_resize = "3.0.4"
imagequant = "4.3.0"
rgb = "0.8.37"
zune-core = "0.4.12"
zune-image = "0.4.15"
zune-imageprocs = "0.4.15"
1 change: 1 addition & 0 deletions src/operations/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod quantize;
pub mod resize;
115 changes: 115 additions & 0 deletions src/operations/quantize/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use imagequant::Histogram;
use rgb::FromSlice;
use zune_core::{bit_depth::BitType, colorspace::ColorSpace};
use zune_image::{
channel::Channel,
errors::{ImageErrors, ImageOperationsErrors},
traits::OperationsTrait,
};

pub struct Quantize {
quality: u8,
dithering: Option<f32>,
}

impl Quantize {
#[must_use]
pub fn new(quality: u8, dithering: Option<f32>) -> Self {
Self { quality, dithering }
}
}

impl OperationsTrait for Quantize {
fn name(&self) -> &'static str {
"quantize"
}

fn execute_impl(&self, image: &mut zune_image::image::Image) -> Result<(), ImageErrors> {
let (src_width, src_height) = image.dimensions();
let channel_len = src_width * src_height * image.depth().size_of();

let mut liq = imagequant::new();

liq.set_quality(0, self.quality)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

let mut histogram = Histogram::new(&liq);

let mut frames = image
.frames_ref()
.iter()
.map(|frame| {
let mut img = liq
.new_image(
frame.flatten(image.colorspace()).as_rgba(),
src_width,
src_height,
0.0,
)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

histogram
.add_image(&liq, &mut img)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

Ok::<imagequant::Image, ImageErrors>(img)
})
.collect::<Result<Vec<imagequant::Image>, ImageErrors>>()?;

let mut res = histogram
.quantize(&liq)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

if let Some(dithering) = self.dithering {
res.set_dithering_level(dithering)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;
}

frames
.iter_mut()
.zip(image.frames_mut())
.try_for_each(|(img, frame)| {
let (palette, pixels) = res
.remapped(img)
.map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;

let channels = pixels
.iter()
.map(|px| {
let px = palette[*px as usize];
(px.r, px.g, px.b, px.a)
})
.enumerate()
.fold(
vec![Channel::new_with_bit_type(channel_len, BitType::U8); 4],
|mut acc, (idx, px)| {
unsafe {
acc[0].alias_mut()[idx] = px.0;
acc[1].alias_mut()[idx] = px.1;
acc[2].alias_mut()[idx] = px.2;
acc[3].alias_mut()[idx] = px.3;
}

acc
},
);

frame.set_channels(channels);

Ok::<(), ImageErrors>(())
})?;

Ok(())
}

fn supported_types(&self) -> &'static [BitType] {
&[BitType::U8]
}

fn supported_colorspaces(&self) -> &'static [ColorSpace] {
&[ColorSpace::RGBA]
}
}

#[cfg(test)]
mod tests;
27 changes: 27 additions & 0 deletions src/operations/quantize/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::test_utils::*;

use super::*;

#[test]
fn quantize_u8() {
let quantize = Quantize::new(75, None);
let mut image = create_test_image_u8(200, 200, ColorSpace::RGBA);

let result = quantize.execute(&mut image);

dbg!(&result);

assert!(result.is_ok());
}

#[test]
fn dither_u8() {
let quantize = Quantize::new(75, Some(0.75));
let mut image = create_test_image_u8(200, 200, ColorSpace::RGBA);

let result = quantize.execute(&mut image);

dbg!(&result);

assert!(result.is_ok());
}

0 comments on commit 25d0d78

Please sign in to comment.