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
Prompts - Read a textfile of prompts and import into anki via ankiconnect

prompts read a textfile of prompts and import into anki via ankiconnect Usage In

Alexander Cobleigh 2 Jul 28, 2022
StarGAN - Official PyTorch Implementation (CVPR 2018)

StarGAN - Official PyTorch Implementation ***** New: StarGAN v2 is available at https://github.com/clovaai/stargan-v2 ***** This repository provides t

Yunjey Choi 5.1k Jan 04, 2023
LIMEcraft: Handcrafted superpixel selectionand inspection for Visual eXplanations

LIMEcraft LIMEcraft: Handcrafted superpixel selectionand inspection for Visual eXplanations The LIMEcraft algorithm is an explanatory method based on

MI^2 DataLab 4 Aug 01, 2022
[SIGGRAPH 2021 Asia] DeepVecFont: Synthesizing High-quality Vector Fonts via Dual-modality Learning

DeepVecFont This is the official Pytorch implementation of the paper: Yizhi Wang and Zhouhui Lian. DeepVecFont: Synthesizing High-quality Vector Fonts

Yizhi Wang 146 Dec 18, 2022
Parametric Contrastive Learning (ICCV2021)

Parametric-Contrastive-Learning This repository contains the implementation code for ICCV2021 paper: Parametric Contrastive Learning (https://arxiv.or

DV Lab 156 Dec 21, 2022
PySlowFast: video understanding codebase from FAIR for reproducing state-of-the-art video models.

PySlowFast PySlowFast is an open source video understanding codebase from FAIR that provides state-of-the-art video classification models with efficie

Meta Research 5.3k Jan 03, 2023
A symbolic-model-guided fuzzer for TLS

tlspuffin TLS Protocol Under FuzzINg A symbolic-model-guided fuzzer for TLS Master Thesis | Thesis Presentation | Documentation Disclaimer: The term "

69 Dec 20, 2022
Algorithmic trading with deep learning experiments

Deep-Trading Algorithmic trading with deep learning experiments. Now released part one - simple time series forecasting. I plan to implement more soph

Alex Honchar 1.4k Jan 02, 2023
TC-GNN with Pytorch integration

TC-GNN (Running Sparse GNN on Dense Tensor Core on Ampere GPU) Cite this project and paper. @inproceedings{TC-GNN, title={TC-GNN: Accelerating Spars

YUKE WANG 19 Dec 01, 2022
NeuralDiff: Segmenting 3D objects that move in egocentric videos

NeuralDiff: Segmenting 3D objects that move in egocentric videos Project Page | Paper + Supplementary | Video About This repository contains the offic

Vadim Tschernezki 14 Dec 05, 2022
Open Source Light Field Toolbox for Super-Resolution

BasicLFSR BasicLFSR is an open-source and easy-to-use Light Field (LF) image Super-Ressolution (SR) toolbox based on PyTorch, including a collection o

Squidward 50 Nov 18, 2022
Swapping face using Face Mesh with TensorFlow Lite

Swapping face using Face Mesh with TensorFlow Lite

iwatake 17 Apr 26, 2022
Code implementation for the paper 'Conditional Gaussian PAC-Bayes'.

CondGauss This repository contains PyTorch code for the paper Stochastic Gaussian PAC-Bayes. A novel PAC-Bayesian training method is implemented. Ther

0 Nov 01, 2021
Classification of EEG data using Deep Learning

Graduation-Project Classification of EEG data using Deep Learning Epilepsy is the most common neurological disease in the world. Epilepsy occurs as a

Osman Alpaydın 5 Jun 24, 2022
PyTorch code for EMNLP 2021 paper: Don't be Contradicted with Anything! CI-ToD: Towards Benchmarking Consistency for Task-oriented Dialogue System

Don’t be Contradicted with Anything!CI-ToD: Towards Benchmarking Consistency for Task-oriented Dialogue System This repository contains the PyTorch im

Libo Qin 25 Sep 06, 2022
Syntax-Aware Action Targeting for Video Captioning

Syntax-Aware Action Targeting for Video Captioning Code for SAAT from "Syntax-Aware Action Targeting for Video Captioning" (Accepted to CVPR 2020). Th

59 Oct 13, 2022
Base pretrained models and datasets in pytorch (MNIST, SVHN, CIFAR10, CIFAR100, STL10, AlexNet, VGG16, VGG19, ResNet, Inception, SqueezeNet)

This is a playground for pytorch beginners, which contains predefined models on popular dataset. Currently we support mnist, svhn cifar10, cifar100 st

Aaron Chen 2.4k Dec 28, 2022
Repository for paper "Non-intrusive speech intelligibility prediction from discrete latent representations"

Non-Intrusive Speech Intelligibility Prediction from Discrete Latent Representations Official repository for paper "Non-Intrusive Speech Intelligibili

Alex McKinney 5 Oct 25, 2022
A simple approach to emable dense segmentation with ViT.

Vision Transformer Segmentation Network This implementation of ViT in pytorch uses a super simple and straight-forward way of generating an output of

HReynaud 5 Jan 03, 2023
PyTorch code for EMNLP 2021 paper: Don't be Contradicted with Anything! CI-ToD: Towards Benchmarking Consistency for Task-oriented Dialogue System

PyTorch code for EMNLP 2021 paper: Don't be Contradicted with Anything! CI-ToD: Towards Benchmarking Consistency for Task-oriented Dialogue System

Libo Qin 25 Sep 06, 2022