This proposes an example colormap that has
This is meant as a relatively quick and dirty example of the basic idea, and shouldn't be taken as an official proposal.
Originally written by Michael Waskom on February 16, 2015
%matplotlib inline
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from colormath.color_objects import sRGBColor, LCHabColor, LabColor
from colormath.color_conversions import convert_color
Set up three ramps for the lightness, chroma, and hue components of H$_{CL}$ color space.
We want lightness to linearly increase, and we want to sweep through hues from blue to yellow through purple and red.
It's less clear what we want to do with chroma (saturation). This just has a linear increase, which might not be ideal. It's hard to tell visually what is happening in parula, but it appears as if chroma is strongest at the extremes, which might be a way to trying to sneak some diverging properties into what is otherwise a sequential colormap. Whether that's a good idea or not is unclear...
l_ramp = np.linspace(20, 90, 256)
c_ramp = np.linspace(30, 50, 256)
h_ramp = np.linspace(240, 450, 256) % 360
Convert to rgb using Python-colormath
(This doesn't do anything special to stay in-gamut, and just clamps the RGB values, which is likely not optimal).
rgb_colors = []
for i in xrange(256):
lch = LCHabColor(l_ramp[i], c_ramp[i], h_ramp[i])
rgb = convert_color(lch, sRGBColor)
rgb_colors.append((rgb.clamped_rgb_r, rgb.clamped_rgb_g, rgb.clamped_rgb_b))
cmap = mpl.colors.LinearSegmentedColormap.from_list("hcl_colormap", rgb_colors)
Plot a colorbar with this palette
f, ax = plt.subplots(figsize=(10, .7))
x = np.linspace(0, 1, 100)
ax.pcolormesh(np.array([x]), cmap=cmap)
ax.set_axis_off()
Show a simple example that Jet is known to fail on.
x = np.linspace(0, 6)
y = np.linspace(0, 3)[:, np.newaxis]
z = 10 * np.cos(x ** 2) * np.exp(-y)
plt.imshow(z, cmap=cmap)
plt.colorbar()
<matplotlib.colorbar.Colorbar instance at 0x106286d88>
Use Jake Vanderplas' grayify-colormap function to check the perceptual properties
def grayify_cmap(cmap):
"""Return a grayscale version of the colormap"""
#cmap = plt.cm.get_cmap(cmap)
colors = cmap(np.arange(cmap.N))
# convert RGBA to perceived greyscale luminance
# cf. http://alienryderflex.com/hsp.html
RGB_weight = [0.299, 0.587, 0.114]
luminance = np.sqrt(np.dot(colors[:, :3] ** 2, RGB_weight))
colors[:, :3] = luminance[:, np.newaxis]
return cmap.from_list(cmap.name + "_grayscale", colors, cmap.N)
plt.imshow(z, cmap=grayify_cmap(cmap))
plt.colorbar()
<matplotlib.colorbar.Colorbar instance at 0x1065c0fc8>
Do the conversion again, but instead of clamping the values to stay within gamut, use a gray for out of gamut colors so we can see how successful we've been at staying in gamut.
This is a good point at which to mention that human color vision is incredibly vexing.
Also convert the colors to Lab space so that we can more easily visulize the colormap in three dimensions.
rgb_colors = []
lab_colors = []
for i in xrange(0, 256, 3):
lch = LCHabColor(l_ramp[i], c_ramp[i], h_ramp[i])
rgb = convert_color(lch, sRGBColor)
rgb_vals = np.array(rgb.get_value_tuple())
in_gamut = ((0 < rgb_vals) & (rgb_vals < 1)).all()
if not in_gamut:
rgb_vals = np.array([.4, .4, .4])
rgb_colors.append(rgb_vals)
lab = convert_color(lch, LabColor)
lab_colors.append(lab.get_value_tuple())
L, a, b = np.array(lab_colors).T
Now plot the colormap values in Lab space
from mpl_toolkits.mplot3d import Axes3D
f = plt.figure(figsize=(8, 7))
ax = f.add_subplot(111, projection="3d")
ax.scatter3D(a, b, L, s=80, c=rgb_colors, depthshade=False)
ax.set(xlim=(-60, 60), ylim=(-60, 60),
xlabel="a", ylabel="b", zlabel="L");
Also squash the lightness dimension and plot the a, b coordinates as a normal scatter plot
f, ax = plt.subplots(figsize=(6, 6))
ax.scatter(a, b, s=80, c=rgb_colors)
ax.set(xlim=(-60, 60), ylim=(-60, 60),
xlabel="a", ylabel="b");
!cat new_matplotlib_colormap.ipynb > pb