Crate rscolorq[][src]

A library and command line tool for spatial color quantization.


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. 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. See the for image examples, output, and usage.

version = "0.1"
default-features = false


The following example shows the mapping of an image buffer in Rgb from [u8; 3] to [f64; 3], performing the color quantization, then filling a buffer with u8 to be saved as an image.

use rscolorq::{color::Rgb, spatial_color_quant, Matrix2d, Params};

// Create the output buffer and quantized palette index buffer
let mut imgbuf = Vec::with_capacity(width * height * 3);
let mut quantized_image = Matrix2d::new(width, height);

// Build the quantization parameters, verify if accepting user input
let mut conditions = Params::new();

// Convert the input image buffer from Rgb<u8> to Rgb<f64>
let image = Matrix2d::from_vec(
        .map(|&c| Rgb {
            red: c[0] as f64 / 255.0,
            green: c[1] as f64 / 255.0,
            blue: c[2] as f64 / 255.0,

let mut palette = Vec::with_capacity(palette_size as usize);

spatial_color_quant(&image, &mut quantized_image, &mut palette, &conditions)?;

// Convert the Rgb<f64> palette to Rgb<u8>
let palette = palette
    .map(|&c| {
        let color = 255.0 * c;
   as u8,
   as u8,
   as u8,
    .collect::<Vec<[u8; 3]>>();

// Create the final image by color lookup from the palette
for &c in quantized_image.iter() {
    let color = palette
        .get(c as usize)
        .ok_or("Could not retrieve color from palette")?;


  • use RGB or Lab color space for calculations
  • can dither based on fixed color palette
  • seedable RNG for reproducible results


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.



Colors that can be used for spatial color quantization.



Two-dimensional matrix.


Three-dimensional matrix.


Input parameter struct for spatial color quantization and simulated annealing. The parameters can be validated with verify_parameters before quantization.



Square filter size for the quantization.


Indicates that an error has occurred in the quantization calculation.



A trait for calculating the inverse of a matrix.


A trait required to calculate the spatial quantization on a color type.



Perform the spatial color quantization. The mutated input parameters are a 2-dimensional matrix with the quantized color palette indices and the quantized color palette.