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
[CVPR 2021] Modular Interactive Video Object Segmentation: Interaction-to-Mask, Propagation and Difference-Aware Fusion

[CVPR 2021] Modular Interactive Video Object Segmentation: Interaction-to-Mask, Propagation and Difference-Aware Fusion

Rex Cheng 364 Jan 03, 2023
Code to reproduce the results for Statistically Robust Neural Network Classification, published in UAI 2021

Code to reproduce the results for Statistically Robust Neural Network Classification, published in UAI 2021

1 Jun 02, 2022
Public repository of the 3DV 2021 paper "Generative Zero-Shot Learning for Semantic Segmentation of 3D Point Clouds"

Generative Zero-Shot Learning for Semantic Segmentation of 3D Point Clouds Björn Michele1), Alexandre Boulch1), Gilles Puy1), Maxime Bucher1) and Rena

valeo.ai 15 Dec 22, 2022
A series of Python scripts to access measurements from Fluke 28X meters. Fluke IR Remote Interface required.

Fluke289_data_access A series of Python scripts to access measurements from Fluke 28X meters. Fluke IR Remote Interface required. Created from informa

3 Dec 08, 2022
AI-UPV at IberLEF-2021 DETOXIS task: Toxicity Detection in Immigration-Related Web News Comments Using Transformers and Statistical Models

AI-UPV at IberLEF-2021 DETOXIS task: Toxicity Detection in Immigration-Related Web News Comments Using Transformers and Statistical Models Description

Angel de Paula 0 Jun 08, 2022
基于Paddlepaddle复现yolov5,支持PaddleDetection接口

PaddleDetection yolov5 https://github.com/Sharpiless/PaddleDetection-Yolov5 简介 PaddleDetection飞桨目标检测开发套件,旨在帮助开发者更快更好地完成检测模型的组建、训练、优化及部署等全开发流程。 PaddleD

36 Jan 07, 2023
Official Tensorflow implementation of "M-LSD: Towards Light-weight and Real-time Line Segment Detection"

M-LSD: Towards Light-weight and Real-time Line Segment Detection Official Tensorflow implementation of "M-LSD: Towards Light-weight and Real-time Line

NAVER/LINE Vision 357 Jan 04, 2023
PyTorch implementation of EGVSR: Efficcient & Generic Video Super-Resolution (VSR)

This is a PyTorch implementation of EGVSR: Efficcient & Generic Video Super-Resolution (VSR), using subpixel convolution to optimize the inference speed of TecoGAN VSR model. Please refer to the offi

789 Jan 04, 2023
RL agent to play μRTS with Stable-Baselines3

Gym-μRTS with Stable-Baselines3/PyTorch This repo contains an attempt to reproduce Gridnet PPO with invalid action masking algorithm to play μRTS usin

Oleksii Kachaiev 24 Nov 11, 2022
Code for Paper Predicting Osteoarthritis Progression via Unsupervised Adversarial Representation Learning

Predicting Osteoarthritis Progression via Unsupervised Adversarial Representation Learning (c) Tianyu Han and Daniel Truhn, RWTH Aachen University, 20

Tianyu Han 7 Nov 22, 2022
PyTorch implementation of Deep HDR Imaging via A Non-Local Network (TIP 2020).

NHDRRNet-PyTorch This is the PyTorch implementation of Deep HDR Imaging via A Non-Local Network (TIP 2020). 0. Differences between Original Paper and

Yutong Zhang 1 Mar 01, 2022
Demo code for ICCV 2021 paper "Sensor-Guided Optical Flow"

Sensor-Guided Optical Flow Demo code for "Sensor-Guided Optical Flow", ICCV 2021 This code is provided to replicate results with flow hints obtained f

10 Mar 16, 2022
Code accompanying the paper "Knowledge Base Completion Meets Transfer Learning"

Knowledge Base Completion Meets Transfer Learning This code accompanies the paper Knowledge Base Completion Meets Transfer Learning published at EMNLP

14 Nov 27, 2022
Uses Open AI Gym environment to create autonomous cryptocurrency bot to trade cryptocurrencies.

Crypto_Bot Uses Open AI Gym environment to create autonomous cryptocurrency bot to trade cryptocurrencies. Steps to get started using the bot: Sign up

21 Oct 03, 2022
(CVPR2021) Kaleido-BERT: Vision-Language Pre-training on Fashion Domain

Kaleido-BERT: Vision-Language Pre-training on Fashion Domain Mingchen Zhuge*, Dehong Gao*, Deng-Ping Fan#, Linbo Jin, Ben Chen, Haoming Zhou, Minghui

248 Dec 04, 2022
Deep Reinforcement Learning by using an on-policy adaptation of Maximum a Posteriori Policy Optimization (MPO)

V-MPO Simple code to demonstrate Deep Reinforcement Learning by using an on-policy adaptation of Maximum a Posteriori Policy Optimization (MPO) in Pyt

Nugroho Dewantoro 9 Jun 06, 2022
f-BRS: Rethinking Backpropagating Refinement for Interactive Segmentation

f-BRS: Rethinking Backpropagating Refinement for Interactive Segmentation [Paper] [PyTorch] [MXNet] [Video] This repository provides code for training

Visual Understanding Lab @ Samsung AI Center Moscow 516 Dec 21, 2022
Reimplementation of the paper "Attention, Learn to Solve Routing Problems!" in jax/flax.

JAX + Attention Learn To Solve Routing Problems Reinplementation of the paper Attention, Learn to Solve Routing Problems! using Jax and Flax. Fully su

Gabriela Surita 7 Dec 01, 2022
This repo will contain code to reproduce and build upon understanding transfer learning

What is being transferred in transfer learning? This repo contains the code for the following paper: Behnam Neyshabur*, Hanie Sedghi*, Chiyuan Zhang*.

4 Jun 16, 2021
Neural Scene Flow Fields for Space-Time View Synthesis of Dynamic Scenes

Neural Scene Flow Fields PyTorch implementation of paper "Neural Scene Flow Fields for Space-Time View Synthesis of Dynamic Scenes", CVPR 2021 [Projec

Zhengqi Li 583 Dec 30, 2022