CMS 3D CMS Logo

DivergingColor.py
Go to the documentation of this file.
1 import os
2 import math
3 import numpy as np
4 
5 # reference: https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf
6 
7 # RGB to XYZ matrix
8 # 0.4124 | 0.2126 | 0.0193
9 # 0.3576 | 0.7152 | 0.1192
10 # 0.1805 | 0.0722 | 0.9505
11 
12 # Inverse
13 # 3.24063 | -0.968931 | 0.0557101
14 # -1.53721 | 1.87576 | -0.204021
15 # -0.498629 | 0.0415175 | 1.057
16 
17 
18 
19 
20 def fRGB(x):
21  if x > 0.00313080495356037152: val = math.pow(x, 1./2.4)*1.055 - 0.055
22  else: val = x*12.92
23  return val*255.
24 
25 def fRGBinv(x):
26  if x > 0.04045: val = math.pow((x + 0.055)/1.055, 2.4)
27  else: val = (x+0.)/12.92
28  return val*100
29 
30 def lRGB2sRGB(r, g, b):
31  #def rgb_lin(x):
32  # if x > 0.00313080495356037152: val = math.pow(x, 1./2.4)*1.055 - 0.055
33  # else: val = x*12.92
34  # return val*255.
35  #return rgb_lin((r+0.)/100.), rgb_lin((g+0.)/100.), rgb_lin((b+0.)/100.)
36  return fRGB((r+0.)/100.), fRGB((g+0.)/100.), fRGB((b+0.)/100.)
37 
38 def sRGB2lRGB(r, g, b):
39  #def srgb_lrgb(x):
40  # if x > 0.04045: val = math.pow((x + 0.055)/1.055, 2.4)
41  # else: val = (x+0.)/12.92
42  # return val*100
43  #return srgb_lrgb((r+0.)/255.), srgb_lrgb((g+0.)/255.), srgb_lrgb((b+0.)/255.)
44  return fRGBinv((r+0.)/255.), fRGBinv((g+0.)/255.), fRGBinv((b+0.)/255.)
45 
46 
47 def rgb2xyz(r, g, b):
48  sr, sg, sb = sRGB2lRGB(r, g, b)
49  x = 0.4124*sr + 0.3576*sg + 0.1805*sb
50  y = 0.2126*sr + 0.7152*sg + 0.0722*sb
51  z = 0.0193*sr + 0.1192*sg + 0.9505*sb
52  return x, y, z
53 
54 def xyz2rgb(x, y, z):
55  r = 3.24063*x - 1.53721*y - 0.498629*z
56  g = -0.968931*x + 1.87576*y + 0.0415175*z
57  b = 0.0557101*x - 0.204021*y + 1.057*z
58  #m = max(max(r, g), b)
59  #if m > 1.:
60  # r = (r+0.)/(m+0.)
61  # g = (g+0.)/(m+0.)
62  # b = (b+0.)/(m+0.)
63  return lRGB2sRGB(r, g, b)
64 
65 
66 
67 def F(v):
68  if v > 0.008856: return math.pow(v, 1./3.)
69  else: return 7.787*v + 16./116.
70 
71 def Finv(v):
72  if v > 0.20689270648: return math.pow(v, 3)
73  else: return (v - 16./116.)/7.787
74 
75 
76 def xyz2Lab(x, y, z, refW):
77  xn = refW[0]
78  yn = refW[1]
79  zn = refW[2]
80  #xn, yn, zn = [95.047, 100.0, 108.883]
81  #def F(v):
82  # if v > 0.008856: return math.pow(v, 1./3.)
83  # else: return 7.787*v + 16./116.
84  L = 116*(F((y+0.)/(yn +0.)) - 16./116.)
85  a = 500*(F((x+0.)/(xn+0.)) - F((y+0.)/(yn+0.)))
86  b = 200*(F((y+0.)/(yn+0.)) - F((z+0.)/(zn+0.)))
87  return L, a, b
88 
89 def Lab2xyz(L, a, b, refW):
90  xn = refW[0]
91  yn = refW[1]
92  zn = refW[2]
93  #xn, yn, zn = [95.047, 100.0, 108.883]
94  #def Finv(v):
95  # if v > 0.20689270648: return math.pow(v, 3)
96  # else: return (v - 16./116.)/7.787
97  x = Finv((a+0.)/500. + (L + 16.)/116.)*xn
98  y = Finv((L + 16.)/116.)*yn
99  z = Finv((L + 16.)/116. - (b+0.)/200.)*zn
100  return x, y, z
101 
102 def Lab2Msh(L, a, b):
103  M = math.sqrt(math.pow(L,2) + math.pow(a,2) + math.pow(b,2))
104  s = math.acos((L+0.)/(M+0.))
105  h = math.atan2(b,a)
106  return M, s, h
107 
108 def Msh2Lab(M, s, h):
109  L = M*math.cos(s)
110  a = M*math.sin(s)*math.cos(h)
111  b = M*math.sin(s)*math.sin(h)
112  return L, a, b
113 
114 def rgb2Msh(r, g, b, refW):
115  x, y, z = rgb2xyz(r, g, b)
116  xr, yr, zr = rgb2xyz(refW[0], refW[1], refW[2])
117  L, a, b = xyz2Lab(x, y, z, [xr, yr, zr])
118  return Lab2Msh(L, a, b)
119 
120 def Msh2rgb(M, s, h, refW):
121  xr, yr, zr = rgb2xyz(refW[0], refW[1], refW[2])
122  L, a, b = Msh2Lab(M, s, h)
123  x, y, z = Lab2xyz(L, a, b, [xr, yr, zr])
124  return xyz2rgb(x, y, z)
125 
126 def AdjustHue(Ms, ss, hs, Mu):
127  #print('Adjusting Hue')
128  if Ms >= Mu: return hs
129  h = ss*math.sqrt(math.pow(Mu, 2.) - math.pow(Ms, 2.))/(Ms*math.sin(ss)+0.)
130  if hs > -math.pi/3.: return hs + h
131  else: return hs - h
132 
133 def radDiff(a1, a2):
134  diff = abs(a1 - a2)
135  if diff > math.pi: return abs(diff - 2*math.pi)
136  else: return diff
137 
138 #('red: ', (117.34353643868656, 1.099939672641069, 0.698178814103516))
139 #('blue: ', (137.64998152940237, 1.333915268336423, -0.9374394027523394))
140 
141 def DivergingColor(col1, col2, white, frac):
142  M1, s1, h1 = rgb2Msh(col1[0], col1[1], col1[2], white)
143  M2, s2, h2 = rgb2Msh(col2[0], col2[1], col2[2], white)
144 
145  #if s1 > 0.05 and s2 > 0.05 and radDiff(h1,h2) > math.pi/3.:
146  if s1 > 0.05 and s2 > 0.05 and abs(h1 - h2) > math.pi/3.:
147  Mmid = max(max(M1,M2),88.)
148  if frac < .5:
149  M2 = Mmid
150  s2 = 0.
151  h2 = 0.
152  frac = 2*frac
153  else:
154  M1 = Mmid
155  s1 = 0.
156  h1 = 0.
157  frac = 2*frac - 1
158  if s1 < 0.05 and s2 > 0.05: h1 = AdjustHue(M2, s2, h2, M1)
159  elif s2 < 0.05 and s1 > 0.05: h2 = AdjustHue(M1, s1, h1, M2)
160 
161  M = (1 - frac)*M1 + frac*M2
162  s = (1 - frac)*s1 + frac*s2
163  h = (1 - frac)*h1 + frac*h2
164  #print('temp', M, s, h, h1, h2, frac)
165  return Msh2rgb(M, s, h, white)
166 
167 
168 if __name__ == '__main__':
169  Msh = [83.9912098 , 0.54009147, -0.18776355]
170  Msh_np = np.array(Msh)
171  #red = [243.59789395465015, 146.5213165050506, 192.51678151291404]
172  red = [59, 76, 192]
173  blue = [180, 4, 38]
174  white = [1, 1, 1]
175  frac = 0.75
176  print('my val: ', DivergingColor(blue, red, white, frac))
177  print(xyz2rgb(95.047, 100.0, 108.883))
178 
def lRGB2sRGB(r, g, b)
def radDiff(a1, a2)
def xyz2rgb(x, y, z)
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
def Lab2Msh(L, a, b)
def Msh2rgb(M, s, h, refW)
def Msh2Lab(M, s, h)
def DivergingColor(col1, col2, white, frac)
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
def sRGB2lRGB(r, g, b)
def rgb2xyz(r, g, b)
def xyz2Lab(x, y, z, refW)
def AdjustHue(Ms, ss, hs, Mu)
def Lab2xyz(L, a, b, refW)
def rgb2Msh(r, g, b, refW)