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
基于PaddleClas实现垃圾分类,并转换为inference格式用PaddleHub服务端部署

百度网盘链接及提取码: 链接:https://pan.baidu.com/s/1HKpgakNx1hNlOuZJuW6T1w 提取码:wylx 一个垃圾分类项目带你玩转飞桨多个产品(1) 基于PaddleClas实现垃圾分类,导出inference模型并利用PaddleHub Serving进行服务

thomas-yanxin 22 Jul 12, 2022
PyTorch implementation of "Dataset Knowledge Transfer for Class-Incremental Learning Without Memory" (WACV2022)

Dataset Knowledge Transfer for Class-Incremental Learning Without Memory [Paper] [Slides] Summary Introduction Installation Reproducing results Citati

Habib Slim 5 Dec 05, 2022
Trustworthy AI related projects

Trustworthy AI This repository aims to include trustworthy AI related projects from Huawei Noah's Ark Lab. Current projects include: Causal Structure

HUAWEI Noah's Ark Lab 589 Dec 30, 2022
Code for our NeurIPS 2021 paper: Sparsely Changing Latent States for Prediction and Planning in Partially Observable Domains

GateL0RD This is a lightweight PyTorch implementation of GateL0RD, our RNN presented in "Sparsely Changing Latent States for Prediction and Planning i

Autonomous Learning Group 16 Nov 03, 2022
Classification of ecg datas for disease detection

ecg_classification Classification of ecg datas for disease detection

Atacan ÖZKAN 5 Sep 09, 2022
Data Engineering ZoomCamp

Data Engineering ZoomCamp I'm partaking in a Data Engineering Bootcamp / Zoomcamp and will be tracking my progress here. I can't promise these notes w

Aaron 61 Jan 06, 2023
RuDOLPH: One Hyper-Modal Transformer can be creative as DALL-E and smart as CLIP

[Paper] [Хабр] [Model Card] [Colab] [Kaggle] RuDOLPH 🦌 🎄 ☃️ One Hyper-Modal Tr

Sber AI 230 Dec 31, 2022
Official page of Patchwork (RA-L'21 w/ IROS'21)

Patchwork Official page of "Patchwork: Concentric Zone-based Region-wise Ground Segmentation with Ground Likelihood Estimation Using a 3D LiDAR Sensor

Hyungtae Lim 254 Jan 05, 2023
Yolov5 + Deep Sort with PyTorch

딥소트 수정중 Yolov5 + Deep Sort with PyTorch Introduction This repository contains a two-stage-tracker. The detections generated by YOLOv5, a family of obj

1 Nov 26, 2021
Real-time 3D multi-person detection made easy with OpenPose and the ZED

OpenPose ZED This sample show how to simply use the ZED with OpenPose, the deep learning framework that detects the skeleton from a single 2D image. T

blanktec 5 Nov 06, 2020
Self-Supervised Pre-Training for Transformer-Based Person Re-Identification

Self-Supervised Pre-Training for Transformer-Based Person Re-Identification [pdf] The official repository for Self-Supervised Pre-Training for Transfo

Hao Luo 116 Jan 04, 2023
PyTorch Implementation of Fully Convolutional Networks. (Training code to reproduce the original result is available.)

pytorch-fcn PyTorch implementation of Fully Convolutional Networks. Requirements pytorch = 0.2.0 torchvision = 0.1.8 fcn = 6.1.5 Pillow scipy tqdm

Kentaro Wada 1.6k Jan 07, 2023
PyTorch implementation of: Michieli U. and Zanuttigh P., "Continual Semantic Segmentation via Repulsion-Attraction of Sparse and Disentangled Latent Representations", CVPR 2021.

Continual Semantic Segmentation via Repulsion-Attraction of Sparse and Disentangled Latent Representations This is the official PyTorch implementation

Multimedia Technology and Telecommunication Lab 42 Nov 09, 2022
Heterogeneous Temporal Graph Neural Network

Heterogeneous Temporal Graph Neural Network This repository contains the datasets and source code of HTGNN. run_mag.ipynb is the training and testing

15 Dec 22, 2022
Drslmarkov - Distributionally Robust Structure Learning for Discrete Pairwise Markov Networks

Distributionally Robust Structure Learning for Discrete Pairwise Markov Networks

1 Nov 24, 2022
Contains modeling practice materials and homework for the Computational Neuroscience course at Okinawa Institute of Science and Technology

A310 Computational Neuroscience - Okinawa Institute of Science and Technology, 2022 This repository contains modeling practice materials and homework

Sungho Hong 1 Jan 24, 2022
Streamlit component for TensorBoard, TensorFlow's visualization toolkit

streamlit-tensorboard This is a work-in-progress, providing a function to embed TensorBoard, TensorFlow's visualization toolkit, in Streamlit apps. In

Snehan Kekre 27 Nov 13, 2022
Efficient Two-Step Networks for Temporal Action Segmentation (Neurocomputing 2021)

Efficient Two-Step Networks for Temporal Action Segmentation This repository provides a PyTorch implementation of the paper Efficient Two-Step Network

8 Apr 16, 2022
Implementation detail for paper "Multi-level colonoscopy malignant tissue detection with adversarial CAC-UNet"

Multi-level-colonoscopy-malignant-tissue-detection-with-adversarial-CAC-UNet Implementation detail for our paper "Multi-level colonoscopy malignant ti

CVSM Group - email: <a href=[email protected]"> 84 Nov 22, 2022
Dynamic Head: Unifying Object Detection Heads with Attentions

Dynamic Head: Unifying Object Detection Heads with Attentions dyhead_video.mp4 This is the official implementation of CVPR 2021 paper "Dynamic Head: U

Microsoft 550 Dec 21, 2022