Skip to content

Commit

Permalink
feat(preprocessors): implemented quantization operation
Browse files Browse the repository at this point in the history
  • Loading branch information
SalOne22 committed Mar 1, 2024
1 parent 7aa016e commit e527d69
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ opt-level = "z"
anyhow = "1.0.80"
clap = { version = "4.5.1", features = ["cargo", "string"] }
fast_image_resize = "3.0.4"
imagequant = "4.3.0"
indoc = "2.0.4"
log = "0.4.20"
pretty_env_logger = "0.5.0"
Expand Down
1 change: 1 addition & 0 deletions src/preprocessors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use resize::ResizeValue;
use crate::preprocessors::resize::{ResizeFilter, ResizeFit};

pub mod pipeline;
mod quantization;
mod resize;

impl Preprocessors for Command {
Expand Down
8 changes: 7 additions & 1 deletion src/preprocessors/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use clap::ArgMatches;
use zune_image::traits::OperationsTrait;

use super::resize::Resize;
use super::{quantization::Quantization, resize::Resize};

pub struct PreprocessorPipeline(BTreeMap<usize, Box<dyn OperationsTrait>>);

Expand All @@ -17,6 +17,12 @@ impl PreprocessorPipeline {
});
}

if let Some(quantization) = Quantization::from_matches(matches) {
quantization.for_each(|(quantization, index)| {
pipeline.insert(index, Box::new(quantization));
});
}

Self(pipeline)
}
}
125 changes: 125 additions & 0 deletions src/preprocessors/quantization/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::io::Write;

use clap::ArgMatches;
use rayon::iter::{
IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelBridge, ParallelIterator,
};
use zune_core::{bit_depth::BitType, colorspace::ColorSpace};
use zune_image::{channel::Channel, errors::ImageErrors, traits::OperationsTrait};

pub struct Quantization {
quality: u8,
dithering: u8,
}

impl Quantization {
pub fn from_matches(matches: &ArgMatches) -> Option<impl Iterator<Item = (Self, usize)> + '_> {
if let Some(quantization_processors) = matches.get_occurrences::<u8>("quantization") {
let dithering = matches.get_one::<u8>("dithering");

return Some(
quantization_processors
.flatten()
.zip(matches.indices_of("quantization").unwrap())
.map(move |(value, index)| {
(
Self {
quality: *value,
dithering: *dithering.unwrap_or(&75),
},
index,
)
}),
);
}

None
}
}

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

fn execute_impl(
&self,
image: &mut zune_image::image::Image,
) -> Result<(), zune_image::errors::ImageErrors> {
if image.colorspace() != ColorSpace::RGBA {
return Err(zune_image::errors::ImageErrors::OperationsError(
zune_image::errors::ImageOperationsErrors::WrongColorspace(
ColorSpace::RGBA,
image.colorspace(),
),
));
}

let (width, height) = image.dimensions();
let depth = image.depth().bit_type();

let mut liq = imagequant::new();
liq.set_speed(5).unwrap();
liq.set_quality(0, self.quality).map_err(|e| {
ImageErrors::OperationsError(zune_image::errors::ImageOperationsErrors::GenericString(
e.to_string(),
))
})?;

image.channels_mut(false).par_iter_mut().try_for_each(
|old_channel| -> Result<(), ImageErrors> {
let mut new_channel = Channel::new_with_bit_type(old_channel.len(), depth);

{
let mut img = liq
.new_image_borrowed(old_channel.reinterpret_as()?, width, height, 0.0)
.map_err(|e| {
ImageErrors::OperationsError(
zune_image::errors::ImageOperationsErrors::GenericString(
e.to_string(),
),
)
})?;

let mut res = liq.quantize(&mut img).map_err(|e| {
ImageErrors::OperationsError(
zune_image::errors::ImageOperationsErrors::GenericString(e.to_string()),
)
})?;

res.set_dithering_level(self.dithering as f32 / 100.)
.map_err(|e| {
ImageErrors::OperationsError(
zune_image::errors::ImageOperationsErrors::GenericString(
e.to_string(),
),
)
})?;

let (palette, pixels) = res.remapped(&mut img).map_err(|e| {
ImageErrors::OperationsError(
zune_image::errors::ImageOperationsErrors::GenericString(e.to_string()),
)
})?;

new_channel.reinterpret_as_mut()?.write(
&pixels
.par_iter()
.flat_map(|pix| palette[*pix as usize].iter().par_bridge())
.collect::<Vec<u8>>(),
)?;
}

**old_channel = new_channel;

Ok(())
},
)?;

todo!()
}

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

0 comments on commit e527d69

Please sign in to comment.