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
[ICCV2021] Official Pytorch implementation for SDGZSL (Semantics Disentangling for Generalized Zero-Shot Learning)

Semantics Disentangling for Generalized Zero-shot Learning This is the official implementation for paper Zhi Chen, Yadan Luo, Ruihong Qiu, Zi Huang, J

25 Dec 06, 2022
A modular domain adaptation library written in PyTorch.

A modular domain adaptation library written in PyTorch.

Kevin Musgrave 225 Dec 29, 2022
An interactive DNN Model deployed on web that predicts the chance of heart failure for a patient with an accuracy of 98%

Heart Failure Predictor About A Web UI deployed Dense Neural Network Model Made using Tensorflow that predicts whether the patient is healthy or has c

Adit Ahmedabadi 0 Jan 09, 2022
Simple, efficient and flexible vision toolbox for mxnet framework.

MXbox: Simple, efficient and flexible vision toolbox for mxnet framework. MXbox is a toolbox aiming to provide a general and simple interface for visi

Ligeng Zhu 31 Oct 19, 2019
A collection of 100 Deep Learning images and visualizations

A collection of Deep Learning images and visualizations. The project has been developed by the AI Summer team and currently contains almost 100 images.

AI Summer 65 Sep 12, 2022
Official codes: Self-Supervised Learning by Estimating Twin Class Distribution

TWIST: Self-Supervised Learning by Estimating Twin Class Distributions Codes and pretrained models for TWIST: @article{wang2021self, title={Self-Sup

Bytedance Inc. 85 Dec 15, 2022
A lightweight deep network for fast and accurate optical flow estimation.

FastFlowNet: A Lightweight Network for Fast Optical Flow Estimation The official PyTorch implementation of FastFlowNet (ICRA 2021). Authors: Lingtong

Tone 161 Jan 03, 2023
Official implementation of ACMMM'20 paper 'Self-supervised Video Representation Learning Using Inter-intra Contrastive Framework'

Self-supervised Video Representation Learning Using Inter-intra Contrastive Framework Official code for paper, Self-supervised Video Representation Le

Li Tao 103 Dec 21, 2022
The official project of SimSwap (ACM MM 2020)

SimSwap: An Efficient Framework For High Fidelity Face Swapping Proceedings of the 28th ACM International Conference on Multimedia The official reposi

Six_God 2.6k Jan 08, 2023
Scalable Multi-Agent Reinforcement Learning

Scalable Multi-Agent Reinforcement Learning 1. Featured algorithms: Value Function Factorization with Variable Agent Sub-Teams (VAST) [1] 2. Implement

3 Aug 02, 2022
K-PLUG: Knowledge-injected Pre-trained Language Model for Natural Language Understanding and Generation in E-Commerce (EMNLP Founding 2021)

Introduction K-PLUG: Knowledge-injected Pre-trained Language Model for Natural Language Understanding and Generation in E-Commerce. Installation PyTor

Xu Song 21 Nov 16, 2022
Grow Function: Generate 3D Stacked Bifurcating Double Deep Cellular Automata based organisms which differentiate using a Genetic Algorithm...

Grow Function: A 3D Stacked Bifurcating Double Deep Cellular Automata which differentiates using a Genetic Algorithm... TLDR;High Def Trees that you can mint as NFTs on Solana

Nathaniel Gibson 4 Oct 08, 2022
Official implementation for the paper "Attentive Prototypes for Source-free Unsupervised Domain Adaptive 3D Object Detection"

Attentive Prototypes for Source-free Unsupervised Domain Adaptive 3D Object Detection PyTorch code release of the paper "Attentive Prototypes for Sour

Deepti Hegde 23 Oct 17, 2022
(CVPR 2022) A minimalistic mapless end-to-end stack for joint perception, prediction, planning and control for self driving.

LAV Learning from All Vehicles Dian Chen, Philipp Krähenbühl CVPR 2022 (also arXiV 2203.11934) This repo contains code for paper Learning from all veh

Dian Chen 300 Dec 15, 2022
Pretrained models for Jax/Flax: StyleGAN2, GPT2, VGG, ResNet.

Pretrained models for Jax/Flax: StyleGAN2, GPT2, VGG, ResNet.

Matthias Wright 169 Dec 26, 2022
Civsim is a basic civilisation simulation and modelling system built in Python 3.8.

Civsim Introduction Civsim is a basic civilisation simulation and modelling system built in Python 3.8. It requires the following packages: perlin_noi

17 Aug 08, 2022
A voice recognition assistant similar to amazon alexa, siri and google assistant.

kenyan-Siri Build an Artificial Assistant Full tutorial (video) To watch the tutorial, click on the image below Installation For windows users (run th

Alison Parker 3 Aug 19, 2022
Kaggle DSTL Satellite Imagery Feature Detection

Kaggle DSTL Satellite Imagery Feature Detection

Konstantin Lopuhin 206 Oct 29, 2022
Deep Reinforcement Learning based autonomous navigation for quadcopters using PPO algorithm.

PPO-based Autonomous Navigation for Quadcopters This repository contains an implementation of Proximal Policy Optimization (PPO) for autonomous naviga

Bilal Kabas 16 Nov 11, 2022
MiraiML: asynchronous, autonomous and continuous Machine Learning in Python

MiraiML Mirai: future in japanese. MiraiML is an asynchronous engine for continuous & autonomous machine learning, built for real-time usage. Usage In

Arthur Paulino 25 Jul 27, 2022