More perceptually uniform colormaps?
Asked Answered
S

3

14

I am an advocate of using perceptually uniform colormaps when plotting scientific data as grayscale images and applying false colorings. I don't know who invented these, but these colormaps are fantastic and I would not use anything else.

Anyways to be honest, I've gotten a bit bored of the 5 colormaps (viridis, plasma, inferno, magma, cividis) which have been implemented in many popular graphing softwares (R-ggplot, python-matplotlib, matlab, JMP, etc.). I'm sure some of you also feel the same monotony...

So in addition to those 5 colormaps, what are some other colormaps which are perceptually uniform?

BONUS: Is there some algorithm to derive colormaps with perceptually uniform qualities (maybe not since color perception has a psychological aspect)? but if so, what is it?

Some examples & refs: https://matplotlib.org/tutorials/colors/colormaps.html https://matplotlib.org/tutorials/colors/colormaps.html

https://www.youtube.com/watch?v=xAoljeRJ3lU

Smallsword answered 28/4, 2020 at 18:10 Comment(2)
Interesting as this question is, it's not quite Stackoverflow topic. Have a look at colorbrewer2.orgRuzich
Thx for the link to version 2.0 of colorbrewer! All the same, I was asking more about 'perceptual uniformity' as a scientific concept, not as colloquial jargon interpreted literally. @Tjebo You are right I guess this isn't so mainstream in general programming community.Smallsword
B
9

If you follow this page: http://bids.github.io/colormap/, you will find all the details required to produce Viridis, Magma, Inferno and Plasma. All the details are too long to enumerate as an answer but using the aforementioned page and viscm, you can regenerate them and some more interactively.

Alternatively, and using Colour:

import colour
import numpy as np

CAM16UCS = colour.convert(['#ff0000', '#00ff00'], 'Hexadecimal', 'CAM16UCS')
gradient = colour.algebra.lerp(
    np.linspace(0, 1, 20)[..., np.newaxis],
    CAM16UCS[0][np.newaxis],
    CAM16UCS[1][np.newaxis],
)
RGB = colour.convert(gradient, 'CAM16UCS', 'Output-Referred RGB')

colour.plotting.plot_multi_colour_swatches(
    [colour.plotting.ColourSwatch(RGB=np.clip(x, 0, 1)) for x in RGB])

print(colour.convert(RGB, 'Output-Referred RGB', 'Hexadecimal'))

['#fe0000' '#fb3209' '#f74811' '#f35918' '#ef671e' '#ea7423' '#e67f28'
 '#e18a2c' '#dc9430' '#d79e34' '#d1a738' '#cbb03b' '#c4b93d' '#bcc23e'
 '#b2cc3d' '#a6d53a' '#97df36' '#82e92e' '#62f321' '#00ff00']

PUG

Note that the two boundary colours are given as hexadecimal values but you could obviously choose any relevant colourspace. Likewise, CAM16 could be swapped for JzAzBz or alike.

You can try that online with this Google Colab notebook.

Berey answered 1/5, 2020 at 1:5 Comment(5)
This is great. I suppose I must concede that the core answer to my question is no, as of today. I thought perceptually uniform colormaps would be less niche after seeing them in so many popular softwares, but it looks like that njsmith guy from BIDS was the last big proliferator on this subject!Smallsword
How to convert it to triples like (255,0,0) instead of hexadecimal '#ff0000'?Buhrstone
Where the line says RGB = colour.convert(gradient, 'CAM16UCS', 'Output-Referred RGB') , RGB is triples.Berey
It seems that colour has made a few breaking changes with v0.4: the lerp function has been moved and the order of its arguments has been swapped. To get the same behavior as in the original answer, you now need to use gradient = colour.algebra.lerp(np.linspace(0, 1, 20)[..., np.newaxis], CAM16UCS[0][np.newaxis], CAM16UCS[1][np.newaxis]).Charlatan
Thanks for the note, code amended accordingly!Berey
O
11

Bit late I suppose, but my CMasher package provides a large collection (I think it is 42 at the time of writing) of scientific colormaps that are all perceptually uniform sequential. Below is an overview of all colormaps that are currently available in CMasher at the time of writing. CMasher colormap overview

In the online documentation, I describe every single colormap individually; discuss the main ways to improve colormap usage and much more. It also provides a collection of utility functions that can be used to manipulate colormaps in various ways.

Out answered 26/3, 2021 at 5:42 Comment(3)
forgive me, i am trying to learn. I did not see your documentations in detail to see how a "diverging" type of colormap can be perceptually uniform? Is that not forbidden by fundamental axiom?Smallsword
What do you mean? Diverging colormaps can be perceptually uniform by making sure either side is. Of course it cannot be fully perceptually uniform, as it has a split in the middle.Out
@Out That's definitely not trivially the case since there's a chance that those color maps could have overlapping hues. I think Kardo may be pointing out is that if you want a map that works for all types of colorblindness and is also diverging, you're out of luck. I'm not 100% sure if this is true but it seems very likely since I THINK that basically limits you to 1 dimension (lightness) for monotonic change.Indehiscent
B
9

If you follow this page: http://bids.github.io/colormap/, you will find all the details required to produce Viridis, Magma, Inferno and Plasma. All the details are too long to enumerate as an answer but using the aforementioned page and viscm, you can regenerate them and some more interactively.

Alternatively, and using Colour:

import colour
import numpy as np

CAM16UCS = colour.convert(['#ff0000', '#00ff00'], 'Hexadecimal', 'CAM16UCS')
gradient = colour.algebra.lerp(
    np.linspace(0, 1, 20)[..., np.newaxis],
    CAM16UCS[0][np.newaxis],
    CAM16UCS[1][np.newaxis],
)
RGB = colour.convert(gradient, 'CAM16UCS', 'Output-Referred RGB')

colour.plotting.plot_multi_colour_swatches(
    [colour.plotting.ColourSwatch(RGB=np.clip(x, 0, 1)) for x in RGB])

print(colour.convert(RGB, 'Output-Referred RGB', 'Hexadecimal'))

['#fe0000' '#fb3209' '#f74811' '#f35918' '#ef671e' '#ea7423' '#e67f28'
 '#e18a2c' '#dc9430' '#d79e34' '#d1a738' '#cbb03b' '#c4b93d' '#bcc23e'
 '#b2cc3d' '#a6d53a' '#97df36' '#82e92e' '#62f321' '#00ff00']

PUG

Note that the two boundary colours are given as hexadecimal values but you could obviously choose any relevant colourspace. Likewise, CAM16 could be swapped for JzAzBz or alike.

You can try that online with this Google Colab notebook.

Berey answered 1/5, 2020 at 1:5 Comment(5)
This is great. I suppose I must concede that the core answer to my question is no, as of today. I thought perceptually uniform colormaps would be less niche after seeing them in so many popular softwares, but it looks like that njsmith guy from BIDS was the last big proliferator on this subject!Smallsword
How to convert it to triples like (255,0,0) instead of hexadecimal '#ff0000'?Buhrstone
Where the line says RGB = colour.convert(gradient, 'CAM16UCS', 'Output-Referred RGB') , RGB is triples.Berey
It seems that colour has made a few breaking changes with v0.4: the lerp function has been moved and the order of its arguments has been swapped. To get the same behavior as in the original answer, you now need to use gradient = colour.algebra.lerp(np.linspace(0, 1, 20)[..., np.newaxis], CAM16UCS[0][np.newaxis], CAM16UCS[1][np.newaxis]).Charlatan
Thanks for the note, code amended accordingly!Berey
P
1

For deriving colormaps with perceptually uniform qualities, please refer to this answer.

The following python packages offer (perceptually uniform) colormaps:

Prager answered 4/4, 2022 at 19:30 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.