From a9f4213952852d064779eaa1d24050dfe99460f1 Mon Sep 17 00:00:00 2001 From: Chen Date: Mon, 28 Aug 2017 15:39:35 -0400 Subject: [PATCH] add HSV support --- lib/damask/colormaps.py | 230 ++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 94 deletions(-) diff --git a/lib/damask/colormaps.py b/lib/damask/colormaps.py index 646950118..bc4f73c02 100644 --- a/lib/damask/colormaps.py +++ b/lib/damask/colormaps.py @@ -9,28 +9,29 @@ class Color(): Conversion of colors between different color-spaces. Colors should be given in the form Color('model',[vector]). - To convert or copy color from one space to other, use the methods + To convert or copy color from one space to other, use the methods convertTo('model') or expressAs('model'), respectively. """ - + __slots__ = [ 'model', 'color', '__dict__', ] - + # ------------------------------------------------------------------ def __init__(self, model = 'RGB', color = np.zeros(3,'d')): - + self.__transforms__ = \ - {'HSL': {'index': 0, 'next': self._HSL2RGB}, - 'RGB': {'index': 1, 'next': self._RGB2XYZ, 'prev': self._RGB2HSL}, - 'XYZ': {'index': 2, 'next': self._XYZ2CIELAB, 'prev': self._XYZ2RGB}, - 'CIELAB': {'index': 3, 'next': self._CIELAB2MSH, 'prev': self._CIELAB2XYZ}, - 'MSH': {'index': 4, 'prev': self._MSH2CIELAB}, + {'HSV': {'index': 0, 'next': self._HSV2HSL}, + 'HSL': {'index': 1, 'next': self._HSL2RGB, 'prev': self._HSL2HSV}, + 'RGB': {'index': 2, 'next': self._RGB2XYZ, 'prev': self._RGB2HSL}, + 'XYZ': {'index': 3, 'next': self._XYZ2CIELAB, 'prev': self._XYZ2RGB}, + 'CIELAB': {'index': 4, 'next': self._CIELAB2MSH, 'prev': self._CIELAB2XYZ}, + 'MSH': {'index': 5, 'prev': self._MSH2CIELAB}, } model = model.upper() @@ -46,24 +47,24 @@ class Color(): self.model = model self.color = np.array(color,'d') - - + + # ------------------------------------------------------------------ def __repr__(self): """Color model and values""" - return 'Model: %s Color: %s'%(self.model,str(self.color)) - - + return 'Model: %s Color: %s'%(self.model,str(self.color)) + + # ------------------------------------------------------------------ def __str__(self): """Color model and values""" return self.__repr__() - - + + # ------------------------------------------------------------------ def convertTo(self,toModel = 'RGB'): toModel = toModel.upper() - if toModel not in list(self.__transforms__.keys()): return + if toModel not in list(self.__transforms__.keys()): return sourcePos = self.__transforms__[self.model]['index'] targetPos = self.__transforms__[toModel]['index'] @@ -76,23 +77,64 @@ class Color(): self.__transforms__[self.model]['prev']() sourcePos -= 1 return self - - + + # ------------------------------------------------------------------ def expressAs(self,asModel = 'RGB'): - return self.__class__(self.model,self.color).convertTo(asModel) - - + return self.__class__(self.model,self.color).convertTo(asModel) + + + + def _HSV2HSL(self): + """ + Convert H(ue) S(aturation) V(alue or brightness) to H(ue) S(aturation) L(uminance) + + with all values in the range of 0 to 1 + http://codeitdown.com/hsl-hsb-hsv-color/ + """ + + if self.model != 'HSV': return + print "HSV: ",self.color + converted = Color('HSL',np.array([ + self.color[0], + 1. if self.color[2] == 0.0 or (self.color[1] == 0.0 and self.color[2] == 1.0) \ + else self.color[1]*self.color[2]/(1.-abs(self.color[2]*(2.-self.color[1])-1.)), + 0.5*self.color[2]*(2.-self.color[1]), + ])) + + self.model = converted.model + self.color = converted.color + + + def _HSL2HSV(self): + """ + Convert H(ue) S(aturation) L(uminance) to H(ue) S(aturation) V(alue or brightness) + + with all values in the range of 0 to 1 + http://codeitdown.com/hsl-hsb-hsv-color/ + """ + + if self.model != 'HSL': return + + h = self.color[0] + b = self.color[2]+0.5*(self.color[1]*(1.-abs(2*self.color[2]-1))) + s = 1.0 if b == 0.0 else 2.*(b-self.color[2])/b + + converted = Color('HSV',np.array([h,s,b])) + + self.model = converted.model + self.color = converted.color + def _HSL2RGB(self): """ - Convert H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue) - + Convert H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue) + with all values in the range of 0 to 1 from http://en.wikipedia.org/wiki/HSL_and_HSV """ if self.model != 'HSL': return - + sextant = self.color[0]*6.0 c = (1.0 - abs(2.0 * self.color[2] - 1.0))*self.color[1] x = c*(1.0 - abs(sextant%2 - 1.0)) @@ -108,15 +150,15 @@ class Color(): ][int(sextant)],'d')) self.model = converted.model self.color = converted.color - - + + def _RGB2HSL(self): """ Convert R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance) - + with all values in the range of 0 to 1 - from http://130.113.54.154/~monger/hsl-rgb.html - """ + from http://130.113.54.154/~monger/hsl-rgb.html + """ if self.model != 'RGB': return HSL = np.zeros(3,'d') @@ -141,43 +183,43 @@ class Color(): if (HSL[0] < 0.0): HSL[0] = HSL[0] + 360.0 for i in range(2): - HSL[i+1] = min(HSL[i+1],1.0) - HSL[i+1] = max(HSL[i+1],0.0) - - converted = Color('HSL', HSL) + HSL[i+1] = min(HSL[i+1],1.0) + HSL[i+1] = max(HSL[i+1],0.0) + + converted = Color('HSL', HSL) self.model = converted.model self.color = converted.color - - + + def _RGB2XYZ(self): """ Convert R(ed) G(reen) B(lue) to CIE XYZ - + with all values in the range of 0 to 1 from http://www.cs.rit.edu/~ncs/color/t_convert.html """ if self.model != 'RGB': return - XYZ = np.zeros(3,'d') + XYZ = np.zeros(3,'d') RGB_lin = np.zeros(3,'d') convert = np.array([[0.412453,0.357580,0.180423], [0.212671,0.715160,0.072169], [0.019334,0.119193,0.950227]]) - + for i in range(3): if (self.color[i] > 0.04045): RGB_lin[i] = ((self.color[i]+0.0555)/1.0555)**2.4 else: RGB_lin[i] = self.color[i] /12.92 XYZ = np.dot(convert,RGB_lin) for i in range(3): - - XYZ[i] = max(XYZ[i],0.0) - converted = Color('XYZ', XYZ) + XYZ[i] = max(XYZ[i],0.0) + + converted = Color('XYZ', XYZ) self.model = converted.model self.color = converted.color - + def _XYZ2RGB(self): """ @@ -199,17 +241,17 @@ class Color(): if (RGB_lin[i] > 0.0031308): RGB[i] = ((RGB_lin[i])**(1.0/2.4))*1.0555-0.0555 else: RGB[i] = RGB_lin[i] *12.92 for i in range(3): - RGB[i] = min(RGB[i],1.0) - RGB[i] = max(RGB[i],0.0) - + RGB[i] = min(RGB[i],1.0) + RGB[i] = max(RGB[i],0.0) + maxVal = max(RGB) # clipping colors according to the display gamut if (maxVal > 1.0): RGB /= maxVal - - converted = Color('RGB', RGB) + + converted = Color('RGB', RGB) self.model = converted.model self.color = converted.color - + def _CIELAB2XYZ(self): """ @@ -219,19 +261,19 @@ class Color(): from http://www.easyrgb.com/index.php?X=MATH&H=07#text7 """ if self.model != 'CIELAB': return - + ref_white = np.array([.95047, 1.00000, 1.08883],'d') # Observer = 2, Illuminant = D65 - XYZ = np.zeros(3,'d') + XYZ = np.zeros(3,'d') XYZ[1] = (self.color[0] + 16.0 ) / 116.0 XYZ[0] = XYZ[1] + self.color[1]/ 500.0 XYZ[2] = XYZ[1] - self.color[2]/ 200.0 - + for i in range(len(XYZ)): if (XYZ[i] > 6./29. ): XYZ[i] = XYZ[i]**3. else: XYZ[i] = 108./841. * (XYZ[i] - 4./29.) - - converted = Color('XYZ', XYZ*ref_white) + + converted = Color('XYZ', XYZ*ref_white) self.model = converted.model self.color = converted.color @@ -244,30 +286,30 @@ class Color(): http://www.cs.rit.edu/~ncs/color/t_convert.html """ if self.model != 'XYZ': return - + ref_white = np.array([.95047, 1.00000, 1.08883],'d') # Observer = 2, Illuminant = D65 XYZ = self.color/ref_white - + for i in range(len(XYZ)): if (XYZ[i] > 216./24389 ): XYZ[i] = XYZ[i]**(1.0/3.0) else: XYZ[i] = (841./108. * XYZ[i]) + 16.0/116.0 - + converted = Color('CIELAB', np.array([ 116.0 * XYZ[1] - 16.0, 500.0 * (XYZ[0] - XYZ[1]), 200.0 * (XYZ[1] - XYZ[2]) ])) self.model = converted.model self.color = converted.color - + def _CIELAB2MSH(self): """ - Convert CIE Lab to Msh colorspace - + Convert CIE Lab to Msh colorspace + from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls - """ + """ if self.model != 'CIELAB': return - - Msh = np.zeros(3,'d') + + Msh = np.zeros(3,'d') Msh[0] = math.sqrt(np.dot(self.color,self.color)) if (Msh[0] > 0.001): Msh[1] = math.acos(self.color[0]/Msh[0]) @@ -287,8 +329,8 @@ class Color(): from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls """ if self.model != 'MSH': return - - Lab = np.zeros(3,'d') + + Lab = np.zeros(3,'d') Lab[0] = self.color[0] * math.cos(self.color[1]) Lab[1] = self.color[0] * math.sin(self.color[1]) * math.cos(self.color[2]) Lab[2] = self.color[0] * math.sin(self.color[1]) * math.sin(self.color[2]) @@ -305,7 +347,7 @@ class Colormap(): 'left', 'right', 'interpolate', - ] + ] __predefined__ = { 'gray': {'left': Color('HSL',[0,1,1]), 'right': Color('HSL',[0,0,0.15]), @@ -329,7 +371,7 @@ class Colormap(): 'right': Color('HSL',[0.11,0.75,0.38]), 'interpolate': 'perceptualuniform'}, 'redgreen': {'left': Color('HSL',[0.97,0.96,0.36]), - 'right': Color('HSL',[0.33333,1.0,0.14]), + 'right': Color('HSL',[0.33333,1.0,0.14]), 'interpolate': 'perceptualuniform'}, 'bluered': {'left': Color('HSL',[0.65,0.53,0.49]), 'right': Color('HSL',[0.97,0.96,0.36]), @@ -347,8 +389,8 @@ class Colormap(): 'right': Color('RGB',[0.000002,0.000000,0.286275]), 'interpolate': 'perceptualuniform'}, } - - + + # ------------------------------------------------------------------ def __init__(self, left = Color('RGB',[1,1,1]), @@ -366,32 +408,32 @@ class Colormap(): left = Color() if right.__class__.__name__ != 'Color': right = Color() - + self.left = left self.right = right self.interpolate = interpolate - - + + # ------------------------------------------------------------------ def __repr__(self): """Left and right value of colormap""" return 'Left: %s Right: %s'%(self.left,self.right) - - + + # ------------------------------------------------------------------ def invert(self): (self.left, self.right) = (self.right, self.left) return self - - -# ------------------------------------------------------------------ + + +# ------------------------------------------------------------------ def color(self,fraction = 0.5): - + def interpolate_Msh(lo, hi, frac): - + def rad_diff(a,b): return abs(a[2]-b[2]) -# if saturation of one of the two colors is too less than the other, hue of the less +# if saturation of one of the two colors is too less than the other, hue of the less def adjust_hue(Msh_sat, Msh_unsat): if Msh_sat[0] >= Msh_unsat[0]: return Msh_sat[2] @@ -399,11 +441,11 @@ class Colormap(): hSpin = Msh_sat[1]/math.sin(Msh_sat[1])*math.sqrt(Msh_unsat[0]**2.0-Msh_sat[0]**2)/Msh_sat[0] if Msh_sat[2] < - math.pi/3.0: hSpin *= -1.0 return Msh_sat[2] + hSpin - + Msh1 = np.array(lo[:]) Msh2 = np.array(hi[:]) - - if (Msh1[1] > 0.05 and Msh2[1] > 0.05 and rad_diff(Msh1,Msh2) > math.pi/3.0): + + if (Msh1[1] > 0.05 and Msh2[1] > 0.05 and rad_diff(Msh1,Msh2) > math.pi/3.0): M_mid = max(Msh1[0],Msh2[0],88.0) if frac < 0.5: Msh2 = np.array([M_mid,0.0,0.0],'d') @@ -414,16 +456,16 @@ class Colormap(): if Msh1[1] < 0.05 and Msh2[1] > 0.05: Msh1[2] = adjust_hue(Msh2,Msh1) elif Msh1[1] > 0.05 and Msh2[1] < 0.05: Msh2[2] = adjust_hue(Msh1,Msh2) Msh = (1.0 - frac) * Msh1 + frac * Msh2 - + return Color('MSH',Msh) - + def interpolate_linear(lo, hi, frac): """Linear interpolation between lo and hi color at given fraction; output in model of lo color.""" interpolation = (1.0 - frac) * np.array(lo.color[:]) \ + frac * np.array(hi.expressAs(lo.model).color[:]) - + return Color(lo.model,interpolation) - + if self.interpolate == 'perceptualuniform': return interpolate_Msh(self.left.expressAs('MSH').color, self.right.expressAs('MSH').color,fraction) @@ -432,8 +474,8 @@ class Colormap(): self.right,fraction) else: raise NameError('unknown color interpolation method') - -# ------------------------------------------------------------------ + +# ------------------------------------------------------------------ def export(self,name = 'uniformPerceptualColorMap',\ format = 'paraview',\ steps = 2,\ @@ -461,21 +503,21 @@ class Colormap(): colormap = ['View.ColorTable = {'] \ + [',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])] \ + ['}'] - + elif format == 'gom': colormap = ['1 1 ' + str(name) \ + ' 9 ' + str(name) \ + ' 0 1 0 3 0 0 -1 9 \ 0 0 0 255 255 255 0 0 255 ' \ + '30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 ' + str(len(colors)) \ + ' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colors)])] - + elif format == 'raw': colormap = ['\t'.join(map(str,color)) for color in colors] - + elif format == 'list': colormap = colors - + else: raise NameError('unknown color export format') - + return '\n'.join(colormap) + '\n' if type(colormap[0]) is str else colormap