made quantize generic for Image

This commit is contained in:
Sander Hautvast 2022-01-27 16:46:07 +01:00
parent 8287731952
commit e792087ebc
3 changed files with 271 additions and 33 deletions

217
Cargo.lock generated
View file

@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e"
[[package]]
name = "adler"
version = "1.0.2"
@ -50,6 +56,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "conv"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
dependencies = [
"custom_derive",
]
[[package]]
name = "crc32fast"
version = "1.3.0"
@ -103,6 +118,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "custom_derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
[[package]]
name = "deflate"
version = "0.8.6"
@ -119,6 +140,17 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gif"
version = "0.11.3"
@ -157,6 +189,32 @@ dependencies = [
"tiff",
]
[[package]]
name = "imageproc"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7923654f3ce7cb6849d5dc9e544aaeab49c508a90b56c721b046e7234c74ab53"
dependencies = [
"conv",
"image",
"itertools",
"num 0.3.1",
"rand",
"rand_distr",
"rayon",
"rulinalg",
"rusttype",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "jpeg-decoder"
version = "0.1.22"
@ -178,6 +236,15 @@ version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "matrixmultiply"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcad67dcec2d58ff56f6292582377e6921afdf3bfbd533e26fb8900ae575e002"
dependencies = [
"rawpointer",
]
[[package]]
name = "memoffset"
version = "0.6.5"
@ -206,6 +273,51 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
dependencies = [
"num-integer",
"num-iter",
"num-traits",
]
[[package]]
name = "num"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -234,6 +346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
@ -262,6 +375,16 @@ name = "octo"
version = "0.1.0"
dependencies = [
"image",
"imageproc",
]
[[package]]
name = "owned_ttf_parser"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3"
dependencies = [
"ttf-parser",
]
[[package]]
@ -276,6 +399,68 @@ dependencies = [
"miniz_oxide 0.3.7",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_distr"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
dependencies = [
"rand",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rawpointer"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019"
[[package]]
name = "rayon"
version = "1.5.1"
@ -301,6 +486,26 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "rulinalg"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04ada202c9685e1d72a7420c578e92b358dbf807d3dfabb676a3dab9cc3bb12f"
dependencies = [
"matrixmultiply",
"num 0.1.42",
]
[[package]]
name = "rusttype"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
@ -324,6 +529,18 @@ dependencies = [
"weezl",
]
[[package]]
name = "ttf-parser"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "weezl"
version = "0.1.5"

View file

@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
image="0.23.14"
imageproc="0.22.0"
[profile.release]
debug = true

View file

@ -1,10 +1,14 @@
use std::{cell::RefCell, rc::Rc};
use image::{Rgba, RgbaImage};
use image::{GenericImageView, Pixel, Rgba, RgbaImage};
use imageproc::definitions::Image;
const MAX_LEVEL: usize = 5;
pub fn quantize(image: &RgbaImage, num_colors:usize) -> RgbaImage{
pub fn quantize<P>(image: &Image<P>, num_colors: usize) -> RgbaImage
where
P: Pixel<Subpixel = u8> + 'static,
{
let mut quantizer = OctTreeQuantizer::new(num_colors);
quantizer.quantize(image)
}
@ -33,10 +37,14 @@ impl OctTreeQuantizer {
new_quantizer
}
pub fn quantize(&mut self, image: &RgbaImage) -> RgbaImage {
pub fn quantize<P>(&mut self, image: &Image<P>) -> RgbaImage
where
P: Pixel<Subpixel = u8> + 'static,
{
for y in 0..image.height() {
for x in 0..image.width() {
self.insert_color(image.get_pixel(x, y), Rc::clone(&self.root));
let p = image.get_pixel(x, y);
self.insert_color(p, Rc::clone(&self.root));
if self.colors > self.reduce_colors {
self.reduce_tree(self.reduce_colors);
@ -52,8 +60,9 @@ impl OctTreeQuantizer {
let mut out = RgbaImage::new(image.width(), image.height());
for y in 0..image.height() {
for x in 0..image.width() {
let pixel = image.get_pixel(x, y);
if let Some(index) = self.get_index_for_color(pixel, &self.root) {
unsafe { //safe because bounds are checked
let pixel = image.unsafe_get_pixel(x, y);
if let Some(index) = self.get_index_for_color(&pixel, &self.root) {
let color = table.get(index).unwrap();
if let Some(color) = color {
out.put_pixel(x, y, *color);
@ -61,20 +70,23 @@ impl OctTreeQuantizer {
}
}
}
}
out
}
fn get_index_for_color(
&self,
color: &Rgba<u8>,
node: &Rc<RefCell<OctTreeNode>>,
) -> Option<usize> {
fn get_index_for_color(
fn get_index_for_color<P>(&self, color: &P, node: &Rc<RefCell<OctTreeNode>>) -> Option<usize>
where
P: Pixel<Subpixel = u8> + 'static,
{
fn get_index_for_color<P>(
quantizer: &OctTreeQuantizer,
color: &Rgba<u8>,
color: &P,
level: usize,
node: &Rc<RefCell<OctTreeNode>>,
) -> Option<usize> {
) -> Option<usize>
where
P: Pixel<Subpixel = u8> + 'static,
{
if level > MAX_LEVEL {
return None;
}
@ -147,14 +159,19 @@ impl OctTreeQuantizer {
table
}
fn insert_color(&mut self, rgb: &Rgba<u8>, node: Rc<RefCell<OctTreeNode>>) {
fn insert_color<P>(&mut self, rgb: &P, node: Rc<RefCell<OctTreeNode>>)
where
P: Pixel<Subpixel = u8> + 'static,
{
//nested function that is called recursively
fn insert_color(
fn insert_color<P>(
quantizer: &mut OctTreeQuantizer,
color: &Rgba<u8>,
color: &P,
level: usize,
node: Rc<RefCell<OctTreeNode>>,
) {
) where
P: Pixel<Subpixel = u8> + 'static,
{
if level > MAX_LEVEL {
return;
}
@ -169,9 +186,9 @@ impl OctTreeQuantizer {
if level == MAX_LEVEL {
child.is_leaf = true;
child.count = 1;
child.total_red = color[0] as u32;
child.total_green = color[1] as u32;
child.total_blue = color[2] as u32;
child.total_red = color.channels()[0] as u32;
child.total_green = color.channels()[1] as u32;
child.total_blue = color.channels()[2] as u32;
child.level = level;
quantizer.colors += 1;
}
@ -212,9 +229,9 @@ impl OctTreeQuantizer {
.unwrap()
.borrow_mut();
child.count += 1;
child.total_red += color[0] as u32;
child.total_green += color[1] as u32;
child.total_blue += color[2] as u32;
child.total_red += color.channels()[0] as u32;
child.total_green += color.channels()[1] as u32;
child.total_blue += color.channels()[2] as u32;
return;
} else {
insert_color(
@ -325,17 +342,20 @@ impl OctTreeNode {
}
}
fn get_bitmask(color: &Rgba<u8>, level: &usize) -> usize {
fn get_bitmask<P>(color: &P, level: &usize) -> usize
where
P: Pixel<Subpixel = u8> + 'static,
{
let bit = 0x80 >> level;
let mut index = 0;
if (color[0] & bit) != 0 {
if (color.channels()[0] & bit) != 0 {
index += 4;
}
if (color[1] & bit) != 0 {
if (color.channels()[1] & bit) != 0 {
index += 2;
}
if (color[2] & bit) != 0 {
if (color.channels()[2] & bit) != 0 {
index += 1;
}
index