107 |
return LABclamp(rgb) |
return LABclamp(rgb) |
108 |
|
|
109 |
|
|
110 |
|
def inUnit(vec): |
111 |
|
"""Tests whether the vector is inside the unit cube [0,1]^n""" |
112 |
|
return len(filter((lambda x: x<0 or x>1), vec)) == 0 |
113 |
|
|
114 |
|
|
115 |
def LABclamp(rgb): |
def LABclamp(rgb): |
116 |
"""Clamp a RGB color into [0,1]^3 towards the CIELAB L-axis |
"""Clamp a RGB color into [0,1]^3 towards the CIELAB L-axis |
117 |
|
|
118 |
rgb: the color in RGB709 |
rgb: the color in RGB709 |
119 |
returns: the clamped color in RGB709 |
returns: the clamped color in RGB709 |
120 |
""" |
""" |
121 |
if len(filter((lambda x: x<0 or x>1), rgb)) == 0: |
if inUnit(rgb): |
122 |
return rgb |
return rgb |
123 |
|
|
124 |
(L,a,b) = RGBtoLAB(rgb) |
(L,a,b) = RGBtoLAB(rgb) |
127 |
bit = .5 |
bit = .5 |
128 |
for iter in range(0,10): |
for iter in range(0,10): |
129 |
rgb = LABtoRGB([L,(r+bit)*a,(r+bit)*b]) |
rgb = LABtoRGB([L,(r+bit)*a,(r+bit)*b]) |
130 |
if len(filter((lambda x: x<0 or x>1), rgb)) == 0: |
if inUnit(rgb): |
131 |
r = r + bit |
r = r + bit |
132 |
bit = bit * .5 |
bit = bit * .5 |
133 |
return LABtoRGB([L,r*a,r*b]) |
return LABtoRGB([L,r*a,r*b]) |
134 |
|
|
135 |
|
|
136 |
|
# The YST color space below is a linear color space with |
137 |
|
# a lightness component and a color plane vector whose angle and |
138 |
|
# radius specify the hue and saturation, respectively. |
139 |
|
# Opposite colors are 180 degrees apart. |
140 |
|
# |
141 |
|
# The Y component is the CIE Y lightness (but the weights can be changed) and |
142 |
|
# the ST-plane has the RGB primaries 120 (R = 0, G = 120, B = 240) degrees apart |
143 |
|
# at radius 1. |
144 |
|
# |
145 |
|
# Lightness weights of the RGB primaries used in YST color space functions |
146 |
|
Wr = 0.212671 |
147 |
|
Wg = 0.715160 |
148 |
|
Wb = 0.072169 |
149 |
|
|
150 |
|
def YSTtoRGB(v): |
151 |
|
n = 1.0 / (Wr+Wg+Wb) |
152 |
|
mat = [ [n, n*(Wg+Wb), n*(Wb - Wg) / math.sqrt(3) ], |
153 |
|
[n, n*(-Wr), n*(2*Wb + Wr) / math.sqrt(3) ], |
154 |
|
[n, n*(-Wr), n*-(2*Wg + Wr) / math.sqrt(3) ] ] |
155 |
|
|
156 |
|
return [ mat[0][0] * v[0] + mat[0][1] * v[1] + mat[0][2] * v[2], |
157 |
|
mat[1][0] * v[0] + mat[1][1] * v[1] + mat[1][2] * v[2], |
158 |
|
mat[2][0] * v[0] + mat[2][1] * v[1] + mat[2][2] * v[2] ] |
159 |
|
|
160 |
|
def RGBtoYST(v): |
161 |
|
mat = [[ Wr, Wg, Wb ], |
162 |
|
[ 1, -.5, -.5], |
163 |
|
[ 0, .5*math.sqrt(3), -.5*math.sqrt(3) ]] |
164 |
|
|
165 |
|
return [ mat[0][0] * v[0] + mat[0][1] * v[1] + mat[0][2] * v[2], |
166 |
|
mat[1][0] * v[0] + mat[1][1] * v[1] + mat[1][2] * v[2], |
167 |
|
mat[2][0] * v[0] + mat[2][1] * v[1] + mat[2][2] * v[2] ] |
168 |
|
|
169 |
|
def maxYSTsat(YST): |
170 |
|
"""Return the maximum saturation factor in RGB cube of the given color""" |
171 |
|
|
172 |
|
# Split into "lightness" and "color" components |
173 |
|
Y = YSTtoRGB((YST[0],0,0)) |
174 |
|
vec = YSTtoRGB((0,YST[1],YST[2])) |
175 |
|
|
176 |
|
assert 0 <= Y[0] == Y[1] == Y[2] <= 1 |
177 |
|
|
178 |
|
return min( ((vec[0] > 0) - Y[0]) / vec[0], |
179 |
|
((vec[1] > 0) - Y[1]) / vec[1], |
180 |
|
((vec[2] > 0) - Y[2]) / vec[2] ) |
181 |
|
|
182 |
|
|
183 |
|
def clampSat(rgb): |
184 |
|
"""Clamp an RGB color keeping hue and lightness constant""" |
185 |
|
|
186 |
|
if inUnit(rgb): |
187 |
|
return rgb |
188 |
|
|
189 |
|
(Y,S,T) = RGBtoYST(rgb) |
190 |
|
|
191 |
|
r = maxYSTsat((Y,S,T)) |
192 |
|
|
193 |
|
return YSTtoRGB((Y,r*S,r*T)) |
194 |
|
|
195 |
|
|
196 |
|
|
197 |
|
def YSThue(RGB): |
198 |
|
YST = RGBtoYST(RGB) |
199 |
|
return math.atan2(YST[2],YST[1]) |
200 |
|
|
201 |
|
|
202 |
|
|
203 |
|
|
204 |
|
""" |
205 |
|
# Kluge: Emulate LAB color space using YST hues |
206 |
|
angles = map(YSThue, colors) |
207 |
|
angles = map(lambda a: a + (a < 0) * 2 * math.pi, angles) |
208 |
|
angles.sort() |
209 |
|
angles += [angles[0] + 2 * math.pi] |
210 |
|
|
211 |
|
LABclamp = clampSat |
212 |
|
LABtoRGB = lambda lab: YSTtoRGB((lab[0]*.01,lab[1]*.01,lab[2]*.01)) |
213 |
|
|
214 |
|
def RGBtoLAB(rgb): |
215 |
|
yst = RGBtoYST(rgb) |
216 |
|
return [ -16 + 116 * pow(yst[0], 1./3), 100 * yst[1], 100 * yst[2] ] |
217 |
|
|
218 |
|
def LABtoRGB(lab): |
219 |
|
yst = [ pow((lab[0] + 16.0) / 116, 3), lab[1] / 100.0, lab[2] / 100.0 ] |
220 |
|
return YSTtoRGB(yst) |
221 |
|
""" |