Spatial color quantization in Rust

Overview

rscolorq

Build Status Crates.io Docs.rs

dithered mountains

Rust port of Derrick Coetzee's scolorq, based on the 1998 paper "On spatial quantization of color images" by Jan Puzicha, Markus Held, Jens Ketterer, Joachim M. Buhmann, & Dieter Fellner. Spatial quantization is defined as simultaneously performing halftoning (dithering) and color quantization (limiting the colors in an image). For more information, visit the original implementation's website.

The algorithm is excellent for retaining image detail and minimizing visual distortions for color palettes in the neighborhood of 4, 8, or 16 colors, especially as the image size is reduced. It combines limiting the color palette and dithering the image into a simultaneous process as opposed to sequentially limiting the colors then dithering. Colors are chosen based on their context in the image, hence the "spatial" aspect of spatial color quantization. As in Pointillism, the colors are selected based on their neighbors to mix as an average illusory color in the human eye.

To use as a library, add the following to your Cargo.toml; add the palette_color feature to enable Lab color quantization. Executable builds can be found at https://github.com/okaneco/rscolorq/releases.

[dependencies.rscolorq]
version = "0.2"
default-features = false

Examples

Images are best viewed at 100% magnification.

1) Mandrill

4 quantized mandrills
Top row: Original image, RGB 2 colors
Bottom row: RGB 4 colors, RGB 8 colors

rscolorq -i mandrill.jpg -o mandrill-rgb2.png -n 2 --auto -s 0 --iters 5
rscolorq -i mandrill.jpg -o mandrill-rgb4.png -n 4 --auto -s 0 --repeats 3
rscolorq -i mandrill.jpg -o mandrill-rgb8.png -n 8 --auto -s 0 --iters 5

The --iters and --repeats options can be used to increase their values over the default to improve the quality of output. --auto sets the dithering level based on the image size and desired palette size. The --seed or -s option sets the random number generator seed; otherwise, it's seeded randomly.

2) Palette swatches and fixed palette

Palette swatches can be generated by passing --op plus a filename. --width and --height can be passed to specify the width and height of the resulting palette image. The following swatches are the colors that comprise 4 and 8 color dithered images in the bottom row of the previous image.

4 color swatch
8 color swatch

rscolorq -i mandrill-resize.jpg --op mandrill-rgb4-pal.png -n 4 --auto -s 0 --repeats 3
rscolorq -i mandrill-resize.jpg --op mandrill-rgb8-pal.png -n 8 --auto -s 0 --iters 5 -p

Passing the --print or -p flag will print the hexadecimal colors to the terminal as seen in the second example above. If no --output or -o is passed, the dithered image will not be saved to a file.

b5c970,191821,b7cbe7,6d7f7b,5db7f0,4e5936,f05131,939bcc

Custom color palette

You can supply your own palette to dither with by passing --colors or -c followed by a list of hexadecimal colors as in the following example.

2 tone mandrill
Original image on the left, fixed palette on the right.

rscolorq -i scenic.jpg -o mountain-pal.png -c FFBF82,09717E --auto -s 0 --iters 5`

3) Gradients

4 quantized rainbow gradients
Top row: Original image, RGB 4 colors, RGB 8 colors.
Bottom row: Lab 4 colors, Lab 8 colors.

rscolorq -i rainbow.png -o rainbow-rgb4.png -n 4
rscolorq -i rainbow.png -o rainbow-rgb8.png -n 8 --iters 8 --repeats 2
rscolorq -i rainbow.png -o rainbow-lab4.png -n 4 --lab
rscolorq -i rainbow.png -o rainbow-lab8.png -n 8 --lab --iters 8 --repeats 2

3 greyscale gradients
Left to right: Original image, 2 colors filter size 3, 2 colors filter size 5.

Features

  • use RGB or Lab color space for calculations
  • option to dither based on fixed color palette supplied by the user
  • seedable RNG for reproducible results
  • print the palette colors to the command line in hexadecimal
  • create a palette swatch image from the dither colors

Limitations

It's "slow"

  • Larger images or images with smooth transitions/gradients will take longer. Higher palette sizes will take longer.
  • The algorithm is suited towards retaining detail with smaller color palettes. You can still use it on larger images but be aware it's not close to real-time unless the image is small.

Filter size 1x1

  • Doesn't produce an image resembling the input, nor does the original.

Filter size 5x5

  • Doesn't always converge.
  • I'm unsure if this is an error in this implementation or a problem with the random number generator being used. The original implementation may take a while but eventually completes with filter size 5.
  • Any help on this would be appreciated.

Troubleshooting

If you get an invalid color error or hex color length error with the command line tool, try enclosing the color string in quotes.

For example, instead of -c 000000,ffffff use -c '000000,ffffff'.

License

This crate is licensed under either

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Copyright of the original images is property of their respective owners.

Comments
  • Generate Windows executable on release tag push

    Generate Windows executable on release tag push

    Dear @okaneco, Could you be so kind to generate .exe for the rest of us who are mere Windows users w/o compiler? Originally posted by @sergeevabc in https://github.com/okaneco/rscolorq/issues/2#issuecomment-802295509

    I'd be interested in doing this, however I don't know how to configure GH Actions to build a Rust binary for release.

    enhancement help wanted binary 
    opened by okaneco 5
  • Executable build workflow

    Executable build workflow

    CD workflow for building Rust binaries and publishing to crates.io as adapted from here and here.

    See my test release run here (fails because no crates.io publish token in the repo secrets)

    Rust targets with executables built include:

    • x86_64-apple-darwin
    • x86_64-unknown-linux-gnu
    • x86_64-pc-windows-msvc
    • aarch64-unknown-linux-gnu
    • i686-unknown-linux-gnu

    It's easy to add different architectures and os versions in the matrix, but I believe this covers all of what github has to offer currently.

    I'd suggest to test an alpha release or something, and if anything goes wrong with the cargo publish, add back the "--allow-dirty" flag as seen at the end of both of the examples you shared. I removed the flag because I felt it was extraneous (how is the ref gonna be dirty if we're working off a commit checked into github). There also needs to be a repository secret called CARGO_API_KEY from crates.io.

    Resolves #12

    enhancement 
    opened by ksmoore17 3
  • thread '<unnamed>' panicked at 'attempt to add with overflow' when using as lib

    thread '' panicked at 'attempt to add with overflow' when using as lib

    Hi,

    This package is awesome, thanks for porting it. I'm trying to wrap it in python and getting an overflow panic when using it as a lib despite no problems using the cli.

    I've used the flow described in lib.rs and compared to main.rs and it seems like I'm passing the correct objects to the spatial_color_quantization function, but there is an overflow error caused by the utility::b_value and matrix::get functions. I can't tell exactly at which place in spatial_color_quantization this is being triggered.

    The problem is that b_value calculates indices to use in a get call to a matrix, and the indices that it calculates can sometimes be negative.

    https://github.com/okaneco/rscolorq/blob/8cce9488289cb81645e3f3f2a813066285d6027e/src/quant/utility.rs#L85-L94

    They are converted to usize to index the array and if negative they wraparound. The overflow occurs when this index i at the max value is added to the calculation for the index in the flattened row.

    https://github.com/okaneco/rscolorq/blob/8cce9488289cb81645e3f3f2a813066285d6027e/src/matrix.rs#L48-L50

    I've added some print statements to watch this and it seems like in the cli program, the wraparound occurs but the panic and overflow doesn't seem to. I don't know why my use of the library doesn't work.

    The fix to it that I found was changing the get functions on the Matrix

    https://github.com/pierogis/rscolorq/blob/e833cb7cdb6a509361308c6b0577644a4302dbee/src/matrix.rs#L47-L55

    The wrapping methods on the usize objects work with the overflow and the output images are the same.

    Something else kinda tangential to this issue: I think it would be good to swap the Matrix for the ndarray crate as it provides a more stable and well documented implementation of the same thing. ndarray also makes it really easy to do parallel with rayon if the algorithm could benefit from that anywhere. Would you be interested in a PR for that if I can get it working?

    bug 
    opened by ksmoore17 3
  • Cannot install from cargo

    Cannot install from cargo

    Upstream changes in image cause the crate to fail to build when installing from crates.io.

    See https://github.com/okaneco/kmeans-colors/issues/35 https://github.com/okaneco/kmeans-colors/pull/36

    What happens

    error[E0433]: failed to resolve: could not find `CompressionType` in `png`
      --> C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\rscolorq-0.1.0\src\bin\rscolorq\utils.rs:76:21
       |
    76 |         image::png::CompressionType::Best,
       |                     ^^^^^^^^^^^^^^^ could not find `CompressionType` in `png`
    
    error[E0433]: failed to resolve: could not find `FilterType` in `png`
      --> C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\rscolorq-0.1.0\src\bin\rscolorq\utils.rs:77:21
       |
    77 |         image::png::FilterType::NoFilter,
       |                     ^^^^^^^^^^ could not find `FilterType` in `png`
    
    error: aborting due to 2 previous errors
    

    How to fix it

    The image dependency should be bumped along with the lines of code that don't work. A new version of the crate should be published.

    opened by okaneco 2
  • add safe wrapping arithmetic on usize for matrix indexing

    add safe wrapping arithmetic on usize for matrix indexing

    Fixes undefined indexing behavior described in #4. Fix involves using wrapping_add and wrapping_mul methods on usize to safely perform the calculation of the index without overflowing.

    opened by ksmoore17 1
  • Bump rand to 0.8, prepare for 0.2 release

    Bump rand to 0.8, prepare for 0.2 release

    Bump rand_pcg to 0.3 Update changelog Add note in readme about executables being found on the release page Update rand ranges to inclusive ranges in gen_range calls Update cargo dependencies

    breaking update-deps update 
    opened by okaneco 0
  • Add chunks_exact[_mut] methods for accessing Matrix2d rows, `no_file` flag to binary

    Add chunks_exact[_mut] methods for accessing Matrix2d rows, `no_file` flag to binary

    Add no_file flag to binary args to disable saving file Add rows and rows_mut methods to Matrix2d using chunks_exact Change Mul<Vec> for Matrix2d to use rows method Use rows_mut in spatial_color_quant function Use split_at_mut and chunks in add_row_multiple for Matrix2d Use iterators/chunks in utility.rs|color.rs:

    • compute_b_array
    • sum_coarsen
    • compute_a_image
    • compute_initial_j_palette_sum
    • Rgb and Lab color filter_weights, refine_palette
    enhancement binary library 
    opened by okaneco 0
  • Migrate to Github Actions

    Migrate to Github Actions

    Add Github Action workflow to build and test crate Add allow-fail rustfmt and clippy job to CI Test only the stable mac toolchain Add workflow_dispatch to manually trigger actions Add note in README.md examples to view images at 100% magnification Remove old CI badge from README

    closes #7

    enhancement 
    opened by okaneco 0
  • Add checked usize arithmetic for Matrix indexing, make clippy fixes

    Add checked usize arithmetic for Matrix indexing, make clippy fixes

    Change get/get_mut for Matrix2d and Matrix3d to use checked math, this alters the behavior of the algorithm Change ok_or_else for Error to ok_or Remove unwraps Update cargo dependencies Update Cargo.toml exclude Bump crate version to 0.1.2 Update changelog Add build status icon to readme Change main.rs to use non-deprecated image function

    Note on expects:

    • some exist in Matrix2d and the SpatialQuant trait
    • will be removed on the next breaking version change
    bug library update-deps update 
    opened by okaneco 0
  • Investigate possible performance gains

    Investigate possible performance gains

    It was brought up in #4 about using ndarray and a parallelization library to speed up calculations. It makes sense to use an external crate for matrix features instead of rolling our own. I'm not sure how much performance there is to gain so I'd like to see measurements; the algorithms may not be amenable to easy parallelization.

    Benching/Testing

    I'm fine with using the nightly cargo bench instead of bringing in criterion for now. Experimentation will need to be done wiring up the benches and figuring out what size image makes sense to iterate on (CC0 images preferred). The benches shouldn't take too long to run but hopefully run long enough to make reasonable deductions on performance changes.

    Many of the calculations are operating on nested loop indices and not the matrix collection elements themselves. Based on that detail, I'm not sure if an external matrix library would add more overhead or improve performance. For multi-threading, we need to make sure we're not changing calculations that rely on being computed in an order.

    The crate could use some better test coverage but the quantization functions are fairly opaque to me.

    Things to do

    Avenues of exploration

    • Add benchmarks, add image file(s), tests
      • Exclude the image data folder from Cargo.toml
    • External crates like ndarray and rayon should be behind optional feature gates at first
    • Investigate where parallelization would help in the quant or quant::utility functions

    This comment will be updated with any changes or suggestions.

    enhancement library 
    opened by okaneco 2
  • Filter 5 infinite loop

    Filter 5 infinite loop

    After a few temperature drops, the visit queue doesn't empty when running with filter size 5. There's a step counter and a debug print!() that can be uncommented to monitor the length. The black to white gradient that's 135x135 converges depending on the rng seed, and I've had the rainbow gradient converge as well. From watching the queue lengths that print out, it seems like the rng may stop adequately shuffling since it gets called so many times. The while loop clears out the queue length when it gets too large but the queue never seems to empty. I've gone over the code many times comparing it to the original and nothing obvious stood out but I assume the bug is in my implementation. I tried chacha and hc but those had the same issue. I'm not sure what criteria to use to reseed the rng or if that would even help.

    step counter https://github.com/okaneco/rscolorq/blob/ce4205323ecbd45c3056678352780596cb497dc5/src/quant.rs#L321 visit_queue https://github.com/okaneco/rscolorq/blob/ce4205323ecbd45c3056678352780596cb497dc5/src/quant.rs#L332 print debug https://github.com/okaneco/rscolorq/blob/ce4205323ecbd45c3056678352780596cb497dc5/src/quant.rs#L458-L462

    bug help wanted 
    opened by okaneco 0
Owner
Collyn O'Kane
Collyn O'Kane
DiffQ performs differentiable quantization using pseudo quantization noise. It can automatically tune the number of bits used per weight or group of weights, in order to achieve a given trade-off between model size and accuracy.

Differentiable Model Compression via Pseudo Quantization Noise DiffQ performs differentiable quantization using pseudo quantization noise. It can auto

Facebook Research 145 Dec 30, 2022
Quantization library for PyTorch. Support low-precision and mixed-precision quantization, with hardware implementation through TVM.

HAWQ: Hessian AWare Quantization HAWQ is an advanced quantization library written for PyTorch. HAWQ enables low-precision and mixed-precision uniform

Zhen Dong 293 Dec 30, 2022
Nonuniform-to-Uniform Quantization: Towards Accurate Quantization via Generalized Straight-Through Estimation. In CVPR 2022.

Nonuniform-to-Uniform Quantization This repository contains the training code of N2UQ introduced in our CVPR 2022 paper: "Nonuniform-to-Uniform Quanti

Zechun Liu 60 Dec 28, 2022
Adversarial Color Enhancement: Generating Unrestricted Adversarial Images by Optimizing a Color Filter

ACE Please find the preliminary version published at BMVC 2020 in the folder BMVC_version, and its extended journal version in Journal_version. Datase

null 28 Dec 25, 2022
Rust bindings for the C++ api of PyTorch.

tch-rs Rust bindings for the C++ api of PyTorch. The goal of the tch crate is to provide some thin wrappers around the C++ PyTorch api (a.k.a. libtorc

Laurent Mazare 2.3k Dec 30, 2022
This demo showcase the use of onnxruntime-rs with a GPU on CUDA 11 to run Bert in a data pipeline with Rust.

Demo BERT ONNX pipeline written in rust This demo showcase the use of onnxruntime-rs with a GPU on CUDA 11 to run Bert in a data pipeline with Rust. R

Xavier Tao 14 Dec 17, 2022
RLBot Python bindings for the Rust crate rl_ball_sym

RLBot Python bindings for rl_ball_sym 0.6 Prerequisites: Rust & Cargo Build Tools for Visual Studio RLBot - Verify that the file %localappdata%\RLBotG

Eric Veilleux 2 Nov 25, 2022
Python/Rust implementations and notes from Proofs Arguments and Zero Knowledge

What is this? This is where I'll be collecting resources related to the Study Group on Dr. Justin Thaler's Proofs Arguments And Zero Knowledge Book. T

Thor 66 Jan 4, 2023
QKeras: a quantization deep learning library for Tensorflow Keras

QKeras github.com/google/qkeras QKeras 0.8 highlights: Automatic quantization using QKeras; Stochastic behavior (including stochastic rouding) is disa

Google 437 Jan 3, 2023
Code for our paper at ECCV 2020: Post-Training Piecewise Linear Quantization for Deep Neural Networks

PWLQ Updates 2020/07/16 - We are working on getting permission from our institution to release our source code. We will release it once we are granted

null 54 Dec 15, 2022
I-BERT: Integer-only BERT Quantization

I-BERT: Integer-only BERT Quantization HuggingFace Implementation I-BERT is also available in the master branch of HuggingFace! Visit the following li

Sehoon Kim 139 Dec 27, 2022
FID calculation with proper image resizing and quantization steps

clean-fid: Fixing Inconsistencies in FID Project | Paper The FID calculation involves many steps that can produce inconsistencies in the final metric.

Gaurav Parmar 606 Jan 6, 2023
Degree-Quant: Quantization-Aware Training for Graph Neural Networks.

Degree-Quant This repo provides a clean re-implementation of the code associated with the paper Degree-Quant: Quantization-Aware Training for Graph Ne

null 35 Oct 7, 2022
This is the pytorch implementation for the paper: Generalizable Mixed-Precision Quantization via Attribution Rank Preservation, which is accepted to ICCV2021.

GMPQ: Generalizable Mixed-Precision Quantization via Attribution Rank Preservation This is the pytorch implementation for the paper: Generalizable Mix

null 18 Sep 2, 2022
MQBench: Towards Reproducible and Deployable Model Quantization Benchmark

MQBench: Towards Reproducible and Deployable Model Quantization Benchmark We propose a benchmark to evaluate different quantization algorithms on vari

null 494 Dec 29, 2022
QTool: A Low-bit Quantization Toolbox for Deep Neural Networks in Computer Vision

This project provides abundant choices of quantization strategies (such as the quantization algorithms, training schedules and empirical tricks) for quantizing the deep neural networks into low-bit counterparts.

Monash Green AI Lab 51 Dec 10, 2022
This is an official implementation of the paper "Distance-aware Quantization", accepted to ICCV2021.

PyTorch implementation of DAQ This is an official implementation of the paper "Distance-aware Quantization", accepted to ICCV2021. For more informatio

CV Lab @ Yonsei University 36 Nov 4, 2022