Code Monkey home page Code Monkey logo

glasbey's Introduction

When visualizing categorical data we often want to encode each category with a distinct color. The set of colors may be generated randomly, however more visually pleasing results can be achieved if special care is taken to generate a maximally distinct set of colors.

This repository contains an implementation of a method proposed by Glasbey et al. [1] that is capable of identifying such a set. Dissimilarity between colors is computed in the state-of-the-art perceptually uniform color space CAM02-UCS [2].

Examples

A palette with 30 colors, based on the "Set1" palette from ColorBrewer2, with colors similar to black excluded.

30 colors, based on "Set1"

A palette with 30 colors. First one was fixed to be white; no restrictions regarding similarity to black.

30 colors, starting with white

Installation

To install glasbey run

pip install path/to/glasbey

To show a progress bar during palette generation run

pip install path/to/glasbey[progressbar]

To be able to visualize generated palettes run

pip install path/to/glasbey[view_palette]

For both run

pip install path/to/glasbey[progressbar,view_palette]

Usage as command-line-tool

usage: glasbey.py [-h] [--base-palette BASE_PALETTE] [--no-black] [--view]
                  [--format FORMAT]
                  size output

    Generate a palette with maximally disticts colors using the sequential
    method of Glasbey et al.¹

    (Dis)similarity between colors is computed in the state-of-the-art
    perceptually uniform color space CAM02-UCS.²

    This script needs an RGB to CAM02-UCS color lookup table. Generation of
    this table is a time-consuming process, therefore the first run of this
    script will take some time. The generated table will be stored in the
    working directory of the script and automatically used in next invocations
    of the script. Note that the approximate size of the table is 363 Mb.

    The palette generation method allows the user to supply a base palette. The
    output palette will begin with the colors from the supplied set. If no base
    palette is given, then white will be used as the first base color. The base
    palette should be given as a text file where each line contains a color
    description in RGB255 format with components separated with commas. (See
    files in the 'palettes/' folder for an example.)

    If having black (and colors close to black) is undesired, then `--no-black`
    option may be used to prevent the algorithm from inserting such colors into
    the palette. In addition to that, the range of colors considered for
    inclusion in the palette can be limited by lightness, chroma, or hue.

    ¹) Glasbey, C., van der Heijden, G., Toh, V. F. K. and Gray, A. (2007),
       Colour Displays for Categorical Images.
       Color Research and Application, 32: 304-309

    ²) Luo, M. R., Cui, G. and Li, C. (2006),
       Uniform Colour Spaces Based on CIECAM02 Colour Appearance Model.
       Color Research and Application, 31: 320–330

positional arguments:
  size                  number of colors in the palette
  output                output palette filename

optional arguments:
  -h, --help            show this help message and exit
  --base-palette BASE_PALETTE
                        file with base palette
  --no-black            avoid black and similar colors
  --lightness-range LIGHTNESS_RANGE
                        set min and max for lightness (e.g. 0,90)
  --chroma-range CHROMA_RANGE
                        set min and max for chroma (e.g. 10,100)
  --hue-range HUE_RANGE
                        set start and end for hue (e.g. 315,45)
  --view                view generated palette
  --format {byte,float}
                        output format

Usage as class

>>> from glasbey import Glasbey
>>> gb = Glasbey(base_palette="palettes/set1.txt", overwrite_base_palette=True, lightness_range=(10,100), hue_range=(10,100), chroma_range=(10,100), no_black=True)  # complicated example (demonstrate syntax)
>>> gb = Glasbey(base_palette=[(255, 0, 0), (0, 255, 0), (0, 0, 255)])  # base_palette can also be rgb-list
>>> gb = Glasbey()  # simplest example, as all init parameters are optional
Generating color table: 100% |################################| Time:  0:00:34
>>> gb.generate_palette(size=3)
Generating palette: 100% |####################################| Time:  0:00:01
array([[ 1.00000000e+00,  1.00000000e+00,  1.00000000e+00],
       [ 5.88229881e-42,  5.64082875e-42,  5.59612427e-42],
       [ 8.43137255e-01,  1.43440815e-15, -6.27553565e-16]])
>>> gb.generate_palette(size=5)  # calculates colors 4-5
Generating palette: 100% |####################################| Time:  0:00:02
array([[ 1.00000000e+00,  1.00000000e+00,  1.00000000e+00],
       [ 5.88229881e-42,  5.64082875e-42,  5.59612427e-42],
       [ 8.43137255e-01,  1.43440815e-15, -6.27553565e-16],
       [ 5.49019608e-01,  2.35294118e-01,  1.00000000e+00],
       [ 7.84313725e-03,  5.33333333e-01, -6.05140937e-16]])
>>> p = gb.generate_palette(size=5)  # instantaneous because these colors were already calculated before
>>> gb.convert_palette_to_rgb(p)
[(255, 255, 255), (0, 0, 0), (215, 0, 0), (140, 60, 255), (2, 136, 0)]
>>> gb.save_palette(p, "out.txt")  # save palette to file
>>> gb.view_palette(p)  # opens imagemagick window

References

  1. Glasbey, C., van der Heijden, G., Toh, V. F. K. and Gray, A. (2007), Colour Displays for Categorical Images. Color Research and Application, 32: 304-309

  2. Luo, M. R., Cui, G. and Li, C. (2006), Uniform Colour Spaces Based on CIECAM02 Colour Appearance Model. Color Research and Application, 31: 320–330

glasbey's People

Contributors

aelzenaar avatar constantinpape avatar dgnsrekt avatar jsignell avatar sthoduka avatar taketwo avatar thebigfattony avatar windj007 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

glasbey's Issues

License ? Conditions of reuse

Hi,
Thanks for the code to generate glasbey LUTs.
Your code does not specify a license, which prevents its reuse in other projects, see e.g. here napari/napari#454
Would you be willing to clarify terms of reusing your code by adding a License.txt file of your choice ? (BSD-3 would be permissive for use in other projects)

Python 3.8 FileNotFoundError

On: gb = Glasbey()

Traceback (most recent call last):
File "XXX/glasbey/glasbey.py", line 160, in load_or_generate_color_table
colors = np.load(self.LUT)["lut"]
File "XXX/lib/python3.8/site-packages/numpy/lib/npyio.py", line 417, in load
fid = stack.enter_context(open(os_fspath(file), "rb"))
FileNotFoundError: [Errno 2] No such file or directory: 'XXX/glasbey/rgb_cam02ucs_lut.npz'

I am getting this on python 3.8.9 when using it as module. Any ideas?

Transformed code into object-oriented-format

Hi, I re-factored your code so that it can be imported as a package from anywhere: https://github.com/TheBigFatTony/glasbey
Now, it is possible to do this:

>>> from Glasbey import Glasbey
>>> gb = Glasbey(base_palette="palettes/set1.txt", lightness_range=(10,100), hue_range=(10,100), chroma_range=(10,100), no_black=True)  # complicated example (demonstrate syntax)
>>> gb = Glasbey(base_palette="palettes/set1.txt")  # simplest example
Generating color table: 100% |###############################| Time:  0:00:34
>>> gb.get_palette(size=5)  # the palette already contains 5 colors, so return them instantaneously
array([[8.94117647e-01, 1.01960784e-01, 1.09803922e-01],
       [2.15686275e-01, 4.94117647e-01, 7.21568627e-01],
       [3.01960784e-01, 6.86274510e-01, 2.90196078e-01],
       [5.96078431e-01, 3.05882353e-01, 6.39215686e-01],
       [1.00000000e+00, 4.98039216e-01, 8.96505092e-17]])
>>> gb.get_palette(size=15)  # calculates colors 9-15
Generating palette: 100% |###################################| Time:  0:00:07
array([[ 8.94117647e-01,  1.01960784e-01,  1.09803922e-01],
       [ 2.15686275e-01,  4.94117647e-01,  7.21568627e-01],
       [ 3.01960784e-01,  6.86274510e-01,  2.90196078e-01],
       [ 5.96078431e-01,  3.05882353e-01,  6.39215686e-01],
       [ 1.00000000e+00,  4.98039216e-01,  8.96505092e-17],
       [ 1.00000000e+00,  1.00000000e+00,  2.00000000e-01],
       [ 6.50980392e-01,  3.37254902e-01,  1.56862745e-01],
       [ 9.68627451e-01,  5.05882353e-01,  7.49019608e-01],
       [ 6.00000000e-01,  6.00000000e-01,  6.00000000e-01],
       [ 7.17204074e-16,  2.62745098e-01,  3.92156863e-03],
       [ 1.43440815e-14,  1.00000000e+00,  1.00000000e+00],
       [-7.17204074e-16,  4.48252546e-16,  5.60784314e-01],
       [ 3.72549020e-01,  1.79301018e-16,  1.60784314e-01],
       [ 1.00000000e+00,  9.60784314e-01,  1.00000000e+00],
       [ 3.09803922e-01,  3.05882353e-01,  3.17647059e-01]])
>>> my_palette = gb.get_palette(size=15)  # returns them instantaneously because they're already calculated
>>> gb.view_palette(my_palette)  # opens imagemagick window

I added the possibility to automatically overwrite the base_palette with the newly calculated colors. (This is off by default.) This would be done like this:

>>> gb = Glasbey(base_palette="palettes/set1.txt", overwrite_base_palette=True)
>>> gb.get_palette(size=15)
Generating palette: 100% |#########################################################| Time:  0:00:07
array([[ 8.94117647e-01,  1.01960784e-01,  1.09803922e-01],
       [ 2.15686275e-01,  4.94117647e-01,  7.21568627e-01],
       [ 3.01960784e-01,  6.86274510e-01,  2.90196078e-01],
       [ 5.96078431e-01,  3.05882353e-01,  6.39215686e-01],
       [ 1.00000000e+00,  4.98039216e-01,  8.96505092e-17],
       [ 1.00000000e+00,  1.00000000e+00,  2.00000000e-01],
       [ 6.50980392e-01,  3.37254902e-01,  1.56862745e-01],
       [ 9.68627451e-01,  5.05882353e-01,  7.49019608e-01],
       [ 6.00000000e-01,  6.00000000e-01,  6.00000000e-01],
       [ 5.88229881e-42,  5.64082875e-42,  5.59612427e-42],
       [-8.96505092e-16,  2.90196078e-01, -1.54086813e-16],
       [ 1.43440815e-14,  1.00000000e+00,  1.00000000e+00],
       [ 1.17647059e-02,  8.96505092e-16,  5.88235294e-01],
       [ 3.56862745e-01,  1.79301018e-16,  1.52941176e-01],
       [ 1.00000000e+00,  9.60784314e-01,  1.00000000e+00]])
>>> len(open("palettes/set1.txt", 'r').readlines())
15

At the same time, the argument-parsing should work as it did before.

Also, I added unit tests to the project.

I hope you find this useful.

Using CLI without base palette returns an AttributeError

Hi, just starting to use this great tool to build some large palettes...

If I call the CLI without the base palette, it throws this error:

Traceback (most recent call last):
  File "glasbey.py", line 335, in <module>
    gb = Glasbey(base_palette=args.base_palette.name,
AttributeError: 'NoneType' object has no attribute 'name'

This can easily be fixed by changing line 335 to:

gb = Glasbey(base_palette=getattr(args.base_palette, "name", None),

I'm too lazy to open a PR for this 😄
EDIT: actually, I also added a feature and I'm going to do a PR soon

cheers from Italy

submit to pypi

It would be great to be able to install this using pip.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.