In [1]:
#import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
In [2]:
import viscm
In [3]:
# sRGB corners: a' goes from -37.4 to 45
# AP_LIM = (-38, 46)
# b' goes from -46.5 to 42
# BP_LIM = (-47, 43)
# J'/K goes from 0 to 100
# JP_LIM = (-1, 101)
In [4]:
import numpy as np
from matplotlib.colors import ListedColormap
from colorspacious import cspace_converter, deltaE
def divergent_monotonic_cmap(Jp_min=20, Jp_max=95, points=None, name='my_colormap',
max_iter = 100, clipping=False, verbose=True,
uniform_space='CAM02-UCS', N=256, step=0.01, return_deltaE=False):
"""
Jp_min, Jp_max = linear mapping for lightness
points = list of (a', b') pairs for (e.g 3 pairs for min, gray, max)
points = ((0, -25), (0, 0), (-25, 0))
step = change in lightness over last invalid value in Lp
"""
# convert from
UCS_to_sRGB1 = cspace_converter(uniform_space, 'sRGB1')
if points is None:
points = ((0, -25), (0, 0), (-25, 0))
# Check that N is divisible to m bins
m = len(points)-1
assert not N % m
bin_size = N // m
lt_0 = True
gt_1 = True
iter_round = 0
while (lt_0 or gt_1) and iter_round < max_iter:
Jp = np.linspace(Jp_min, Jp_max, N)
ap = []
bp = []
for ix, ((ai, bi), (aj, bj)) in enumerate(zip(points[:-1], points[1:])):
# Include endpoint for last step
endpoint = m == ix
ap.append(np.linspace(ai, aj, bin_size, endpoint=endpoint))
bp.append(np.linspace(bi, bj, bin_size, endpoint=endpoint))
ap = np.concatenate(ap)
bp = np.concatenate(bp)
Jpapbp = np.column_stack((Jp,ap,bp))
rgb = UCS_to_sRGB1(Jpapbp)
lt_0 = np.sum(rgb < 0)
if lt_0:
positions = np.flatnonzero(np.sum(rgb < 0, axis=1))
Jp_min = Jp[positions.max()]+step
gt_1 = np.sum(rgb > 1)
if gt_1:
positions = np.flatnonzero(np.sum(rgb > 1, axis=1))
Jp_max = Jp[positions.min()]-step
if Jp_min > Jp_max:
raise ValueError("No solution with current Lp ap and bp pairs")
iter_round += 1
if clipping and (lt_0 or gt_1):
if verbose:
print("Maximum iteration: No solution with current Lp ap and bp pairs\nReturning clipped cmap")
rgb = np.clip(rgb, 0, 1)
if verbose:
print("Number of iteration round(s) = {}".format(iter_round))
ptp_value = np.ptp(deltaE(rgb[:-1, :], rgb[1:, :]))
print("Peak to peak in deltaE = {:.2g}".format(ptp_value))
# Create colormap
assert rgb.min() > 0
assert rgb.max() < 1
cmap = ListedColormap(rgb, name=name)
if return_deltaE:
deltaE_values = deltaE(rgb[:-1, :], rgb[1:, :], uniform_space=uniform_space)
return cmap, deltaE_values
else:
return cmap
In [5]:
# For equal deltaE specify r equal for both 'colors'
r = 25
color1 = (0, -r) # Blue
gray = (0, 0)
# reddish = color with 0.7 of pure red and 0.3 pure yellow
# >> 1 pure red vs p=0.3/0.7 pure yellow
# r1 = r/sqrt(1+p^2)
# r2 = p * r1
p = 0.3/0.7
r1 = r/np.sqrt(1+p**2)
r2 = p * r1
color2 = (r1, r2) # Reddish
points = (color1, gray, color2)
my_cmap = divergent_monotonic_cmap(points=points)
In [6]:
plt.imshow(np.linspace(0,1,256)[np.newaxis, ...], cmap=my_cmap, aspect='auto')
plt.gca().set_axis_off()
In [7]:
viscm.viscm(my_cmap)
fig = plt.gcf()
fig.set_size_inches(10, 10)
In [8]:
# For equal deltaE specify r equal for both 'colors'
r = 25
color1 = (0, -r) # Blue
gray = (0, 0)
color2 = (-r, 0) # Greenish
points = (color1, gray, color2)
my_cmap2 = divergent_monotonic_cmap(points=points)
In [9]:
plt.imshow(np.linspace(0,1,256)[np.newaxis, ...], cmap=my_cmap2, aspect='auto')
plt.gca().set_axis_off()
In [10]:
viscm.viscm(my_cmap2)
fig = plt.gcf()
fig.set_size_inches(10, 10)
In [ ]: