.. _color_maps: *********************** Color Map Utilities *********************** Matplotlib provides numerous built-in colormaps and an excellent tutorial on `Choosing Colormaps `_. Various `colormaps and styles `_ third party packages are available that extend Matplotlib. Familiarity with these built-in color maps is suggested. The *sequential* built-in maps are particularly useful for 3D surface visualizations. Choosing colormaps for surfaces in 3D has the additional requirement of enhancing the geometric visualization along with defining functional values. S3Dlib provides multiple easy-to-use methods for creating custom colormaps which are particularly applicable for displaying 3D surfaces. These methods are particularly useful when transparent and mirrored colormaps are needed for clear visualizations of 3D surfaces, or simply for a more graphic display. All colormaps using these functions are derived from `matplotlib.colors.Colormap `_. These functions are available in the module:: import s3dlib.cmap_utilities as cmu In the following discussion, example colormaps are provided to illustrate the usage of the S3Dlib methods. RGB Linear Gradient ========================================================================================= Simple color maps, linear in RGB space, are created using the function .. code-block:: python cmu.rgb_cmap_gradient(lowColor,highColor,name=None,mirrored=False) which returns an instance of a registered matplotlib.colors.Colormap with name. When *name* is None, the name is assigned an eight-character string of random upper-case letters. The color arguments are in `Matplotlib Colors `_ format. The colormap monotonically increases from the *lowColor* to the *highColor* in RGB color space. The *mirrored* argument is a boolean and if set *True*, the color map is mirrored with the *highcolor* at the center. **Mirrored colormaps are particularly useful for non-orientable surfaces.** See :ref:`open_surf_non_orientable`. Several examples using this function are: .. literalinclude:: source/gu_colormaps.py :language: python :lines: 19-35 Note that the colormap with assigned name 'rgbDefault' is the default colormap if no color arguments are provided. .. image:: images/cmap_rgb_grad.png :class: sphx-glr-single-img HSV Linear Gradient ========================================================================================= Simple color maps, linear in HSV space, are created using the function .. code-block:: python cmu.hsv_cmap_gradient(lowHSV,hiHSV,name=None,mirrored=False) which returns an instance of a registered matplotlib.colors.Colormap with name. When *name* is None, the name is assigned an eight-character string of random upper-case letters. The *lowHSV* and *hiHSV* are HSV or HSVA (hue, saturation, value, alpha) arrays of float values. The float values for SVA range in [0,1]. **The float values for hue range in [0,2]**, which is **not** the standard range for hue. This was defined in this manner to allow for direction in the cyclic definition of hue. This hue mapping from 0 to 2 is shown below. .. image:: images/hue_colorbar.png :class: sphx-glr-single-img For example, consider a color map going from green (low) to blue (high). Specifying green and blue as 0.33 and 0.67, the map will pass through cyan. However, specifying green as 1.33, the map will pass through yellow, red and magenta. The colormap still monotonically increases from the lowColor to the highColor in HSV space. The *mirrored* argument is applied in the same manner as previously described for the RGB gradient color maps. Hue ------------------------------------------------- The following are examples of varying the cyclic hue value of the color arguments in both the forward and backward directions. In these examples, the saturation and value for the low and high color arguments were held constant at 1. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 37-58 Note that the colormap with the assigned name 'hsvDefault' is the default colormap if no color arguments are provided. This colormap is equivalent to the Matplotlib built-in colormap named 'hsv.' .. image:: images/cmap_hue_grad.png :class: sphx-glr-single-img The effect of reversing the order of the low and high color arguments is most notable by comparing the maps 'RdToRd' versus 'RdFromRd'. Both start and end with red. Also, the difference between using RGB and HSV is seen by comparing the yellow to blue colormaps in these two-color spaces, 'YwToBu' versus 'hsvYwToBu'. Saturation and Value ------------------------------------------------- In these examples, the hue was constant at 0 (red). The first four colormaps vary value [0,1] while holding saturation to 1. The second set varies saturation [0,1] and hold value constant at 1. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 60-72 .. image:: images/cmap_satval_grad.png :class: sphx-glr-single-img Cyclic and Named Colors ------------------------------------------------- Since the hue is cyclic, any same hue can be represented by a number plus an integer value. For example, blue hue is 0.67, 1.67, 2.66, 3.67, etc. This results in the ability to produce repeated color maps by representing the low and high arguments with values greater than 2. Also, the arguments can be strings of named Matplotlib colors. See for example `named colors `_. To shift the named color hues above 1, the color string is prefixed with a '+' character. This provides the method of reversing the direction of the hue while still using named color values. Examples using cyclic and named color for HSV color maps are: .. literalinclude:: source/gu_colormaps.py :language: python :lines: 95-108 This produces the following colormaps. .. image:: images/cmap_cyclic.png :class: sphx-glr-single-img In the above, the last two colormaps use the RGB gradient method as a comparison to the map produced by the HSV gradient method which precedes them. This exemplifies the effect of a reduction in saturation and value while linearly moving through RGB space. Hue HSV Modifications ========================================================================================= While varying hue in HSV color space, there is an apparent 'sharpness' or rapid change in apparent color at yellow, cyan, and magenta. This is seen in 'hsv' colormap shown below. .. image:: images/hsv_colormap.png :class: sphx-glr-single-img This perceptual change is also reflected in the rapid change in CIELAB color space represented by lightness, L*. (A more detailed discussion of this is in `Choosing Colors in Matplotlib `_ ). To smooth-out this sharpness in the HSV colormap, the following method is available:: cmu.hue_cmap(lowHue=0, hiHue=None, smooth=1.6, name=None) Which returns a colormap of constant value and saturation of 1. The *smooth* parameter is the degree of smoothing at yellow, cyan and magenta, with a value of 1 for no smoothing. For values greater than one, hues around yellow, cyan and magenta are expanded, reducing the extent of red, green and blue hues. For values less than one, the opposite occurs. The *smooth* value range is [0.1,10]. The default value is 1.6. The parameters *lowHue* and *highHue* are similary defined for the hue as given in the previous sections. When color names are used for this parameter, only the hue value of the color is used. Example colormaps using this method are given below. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 111-122 .. image:: images/cmap_hueHSV.png :class: sphx-glr-single-img The effect of lightness, L*, on smoothing is shown in the grayscale maps below, for the first four colormaps. .. image:: images/cmap_grayscale.png :class: sphx-glr-single-img A qualitative description of smoothing by examining the L* numerical values is shown in the plot below. The effect of smoothing is illustrated in this plot showing the peaks, valleys and plateaus of the lightness curves at CMY (0.167, 0.5, 0.833) and RGB (0.0, 0.33, 0.67). .. image:: images/cmap_Lplot.png :class: sphx-glr-single-img The script for producing the above plot is given below. .. literalinclude:: source/gu_cmap_plot.py :language: python The effect of smoothing out an HSV colormap when applied to a surface is shown in the simple surface plot below. .. image:: images/hue_torus.png :class: sphx-glr-single-img Smooth HSV ========================================================================================= The 'smooth' parameter is also available when using the *hsv_cmap_gradient* method. This effect is most apparent when creating colormaps between the RGB colors. The regular and smooth colormaps are shown as a comparison. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 125-134 .. image:: images/cmap_smooth.png :class: sphx-glr-single-img In these examples, the red and green values were selected with reduced values to create maps with similar starting and ending L* values, as seen in the following plot. .. image:: images/cmap_Lplot2.png :class: sphx-glr-single-img Lab Linear Gradient ========================================================================================= A colormap with a linearly monotonic increasing L* values may be preferable for increased perception when used for illustrating surface geometries. This type of map is also beneficial when surface geometry visualization is needed when printed on black and white printers. Numerous sequential colormaps to choose from are by Matplotlib in the section `Sequential `_. The cmap_xtra file provides the *Lab_cmap_gradient* method to construct colormaps which are linear in Lab space. Consequently, these maps will be linear in L*. Linear Lab colormaps are created using a function from a file and creating the cmap as:: import s3dlib.cmap_xtra as cmx cmap = cmx.Lab_cmap_gradient(lowColor='k', highColor='w', name=None, mirrored=False) All calling parameters are identical to those used in the *rgb_cmap_gradient* method. Examples of colormaps using *Lab_cmap_gradient* are given below. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 137-144 The resulting colormaps are below. The linear Matplotlib colormaps of 'magma' and 'viridis' are shown as a comparison to those generated using this function. .. image:: images/cmap_lab.png :class: sphx-glr-single-img Grayscale maps for these colormaps illustrate the perceived lightness values. .. image:: images/cmap_grayscale_lab.png :class: sphx-glr-single-img The plot below quantitatively demonstrates the linear nature of the colormaps and the relative differences between the maps. .. image:: images/cmap_Lplot3.png :class: sphx-glr-single-img Lab Linear, Hue Constant Gradient ========================================================================================= Colormap having a linear lightness gradient from a low, *lowL*, to a high, *hiL*, with a constant value of hue can be produced using the method:: import s3dlib.cmap_xtra as cmx cmap = cmx.Hue_Lab_gradient(color,lowL=0.0, hiL=1.0, name=None) where *color* is a Matplotlib named color, a three-element list or a single numerical value which will be used to set the H value of the gradient. The range for the lowL and hiL arguments is [0,1]. For example, using this method as: .. literalinclude:: source/gu_colormaps.py :language: python :lines: 182-190 results in colormaps as: .. image:: images/cmap_hue_lab.png :class: sphx-glr-single-img with corresponding L* plots: .. image:: images/cmap_hue_lab_Lplot.png :class: sphx-glr-single-img Colormaps with a linear gradient in L* may be a substitute for the shading using the surface method *map_cmap_from_normal*. The following is a comparison among the above colormaps when surface mapping to a sphere. .. image:: images/hue_lab.png :class: sphx-glr-single-img In the above figure, the Matplotlib named colormap 'gray' and shading a white sphere is also shown as a reference. Lab Linear of a Colormap ========================================================================================= Colormaps having a linear lightness gradient from a low, *lowL*, to a high, *hiL*, with a colormap hue of maximum Saturation can be produced using the method:: import s3dlib.cmap_xtra as cmx cmap = cmx.Hue_Lab_gradient(colormap, lowL=None, hiL=None, name=None) where *colormap* is a colormap, which will be to set the H values of the gradient. If assigned, the range for the lowL and hiL arguments is [0,1]. If not defined, the domain is the range taken from the colormap. For example, using this method as: .. literalinclude:: source/gu_colormaps.py :language: python :lines: 193-199 results in colormaps as: .. image:: images/cmap_cmap_lab.png :class: sphx-glr-single-img with corresponding L* plots: .. image:: images/cmap_cmap_lab_Lplot.png :class: sphx-glr-single-img This method is most suitable using **sequential colormaps** since the resulting colormap is linear from start to finish. The effect of this method is to, in general, increase the color saturation of the colormap when the second-derivative of L* is negative. This is seen in the Matplotlib colormaps of *viridis*, *plasma*, *Reds*, *YlGn*, and *copper*. The opposite effect of reducing saturation occurs for colormaps with positive derivativess, evident for *spring* and *autumn*. Stitch Cmaps ========================================================================================= Colormaps may be created from concatenated colormaps using .. code-block:: python cmu.stitch_cmap( *maps, bndry=None, name=None) which returns an instance of a registered matplotlib.colors.Colormap with name. When *name* is None, the name is assigned an eight-character string of random upper-case letters. The *bndry* argument is a float or a list of float values designating the boundaries between the input maps. Values range from greater than 0 to less than 1. If *bndry* is None (default), the input maps are evenly spaced. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 148-178 This method can be used to construct simple linear tricolor or multi-color colormaps, for example *fWt_2*. Specific colormap values can be emphasized using this method as seen in the *inferno_3* and *RdCy_4* examples, (0.2, 0.6) and (0.3,0.8) respectively. Using Lab colormaps, `Diverging and Cyclic `_ colormaps can be constructed. .. image:: images/cmap_stitch.png :class: sphx-glr-single-img Stitch Colors ========================================================================================= Simple multi-color maps are created using the function:: cmu.stitch_color(*colorVals, bndry=None, name=None ) which returns an instance of a registered matplotlib.colors.Colormap with name. When *name* is None, the name is assigned an eight-character string of random upper-case letters. The *bndry* argument is a float or a list of float values designating the boundaries between the input colors. Values range from greater than 0 to less than 1. If *bndry* is None (default), the input colors are evenly spaced. The following is equivalent for two-color colormaps:: cmu.binary_cmap(negColor='b',posColor='r',name=None,bndry=None) but in this case, the division between the colors are set by the single float *bndry* argument, which ranges from 0.03 to 0.97, with a default value of 0.5. **Binary colormaps are particularly useful for having different colors for front and back surfaces.** For example, see :ref:`bin_surf` and :ref:`fig_8_knot`. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 11-17 .. image:: images/cmap_binary.png :class: sphx-glr-single-img .. _section_colormaps: Section Cmaps ========================================================================================= Colormap can be created from a section of a registered colormap, cmap, using:: cmu.section_cmap(cmap, lowIndx, hiIndx, name=None) which returns an instance of a registered matplotlib.colors.Colormap with name. When name is None, the name is assigned an eight-character string of random upper-case letters. The *lowIndx* argument is a float value designating the start of the input colormap in the range 0 to 0.97. Similarly, *hiIndx* designates the end. For example, using the included Matplotlib 'gnuplot2' and 'terrain' colormaps:: cmu.section_cmap('gnuplot2',0.23,0.8,name='gpl2') cmu.section_cmap('terrain',0.23,0.75,name='trn2') The following colormaps are generated: .. image:: images/cmap_section.png :class: sphx-glr-single-img .. _mirrored_colormaps: Mirrored and Reversed ========================================================================================= Any registered colormap can have the order reversed using .. code-block:: python cmu.reversed_cmap(cmap,name=None) which returns a new instance of a registered colormap. The returned colormap name is the *cmap* name with the suffix '_r'. Any registered colormap can be used to create a mirrored color map using .. code-block:: python cmu.mirrored_cmap(cmap,name=None,rev=False) The returned colormap name is the *cmap* name with the suffix '_m'. If the *rev* argument is True, the returned colormap is also reversed and the name has the suffix '_mr'. .. literalinclude:: source/gu_colormaps.py :language: python :lines: 74-93 .. image:: images/cmap_mirror_rev.png :class: sphx-glr-single-img .. _cmap_funmaps: Creating Cmaps Using Functions ========================================================================================= The previous functions provide methods for creating colormaps which are linear in RGB, HSV or Lab color spaces. For a more general approach, colormaps may be created using a function returning the color component values using .. code-block:: python cmu.op_cmap(op,rgb=True,name=None) where *op* is a function that takes one argument, a N Numpy array of float numbers in the domain of 0 to 1. The function returns a 3XN or 4XN array of color values. By default, RGB or RGBA color values are returned by the operation function. If the *rgb* argument is set False, the operation returns HSV color values. This returns a registered colormap with the name given by the *name* argument. When *name* is None, the colormap is named using the function name. For a lambda function, the name is assigned an eight-character string of random upper-case letters. .. literalinclude:: source/gu_colormaps2.py :language: python :lines: 38-70 .. image:: images/cmap_op.png :class: sphx-glr-single-img Note that in the above script and plot, the function names are assigned to the colormap names since the *name* argument was not defined when *op_cmap* was called. Transparency ========================================================================================= Any registered colormap can have the transparency set using .. code-block:: python cmu.alpha_cmap(cmap,alpha,constant=False,name=None) where *cmap* argument is a cmap or a registered colormap name. The argument *alpha* ranges from 0 to 1 for fully transparent to opaque, respectively. This function will create and return a cmap with a name with suffix '_a'. The argument *constant* default is *False*, in which case, all alpha values of the returned colormap are the input values multiplied by alpha. When *constant* is set to True, all colors in the map will have the same alpha value. When only the alpha channel is to be modified from a registered colormap, use the method:: cmu.op_alpha_cmap( cmap,operation,fit=True,name=None ) where cmap is a registered colormap and *operation* is a function returning a numerical value in the domain 0 to 1. When fit is True, the function range is adjusted from 0 to 1. Otherwise, the return value of the operation must be in the range of 0 to 1. There are two advantages of using this method, compared to the *op_cmap* method. First, the same function may be applied for different colormaps. Second, using the default for *fit*, the function range is conveniently set from 0 to 1. .. literalinclude:: source/gu_colormaps2.py :language: python :lines: 13-29 .. image:: images/cmap_alpha.png :class: sphx-glr-single-img In the above example, the *gapJet* colormap was created to demonstrate the effect of True or False values for the *constant* argument using an initial colormap with transparency values. DualCmap ========================================================================================= Colormaps map one independent variable, u, to a color, i.e., RGBA = f(u). A DualCmap class object provides a means of mapping two independent variables, u and v, to a color, RGBA = g(u,v). Instead of using an object.map_cmap_from_op method, a two-dimensional colormap may be used with the object.map_color_from_op method which is passed a function as:: def op_function(xyz) : u,v = f(xyz) return dcmap( u,v ) where f(xyz) is a function of xyz and dcmap is an instance of the DualCmap class. DualCmap objects are constructed from two colormaps, xcmap and ycmap, as:: dcmap = cmu.DualCmap( xcmap, ycmap, kind='sum', norm=True ) where *kind* indicates the method of combining colormap colors. Values are either predefined types of combinations ('sum','ave','ave2','srt') or a float in the range 0.2 to 4.0. The argument *norm* indicates if the method arguments are to be normalized. Otherwise, the calling arguments to the instance, u and v, should be in the range 0 to 1. Using the following colormaps: .. image:: images/cmap_dualcmap4.png :class: sphx-glr-single-img the resulting DualCmaps can be constructed: .. image:: images/dualcmap4.png :class: sphx-glr-single-img The following examples demonstrate using a DualCmap object: * :ref:`complex_dualcmap` where kind is 'srt' or 1.5 * :ref:`swirl_dualcmap` where kind is the default 'sum' * :ref:`plaid_dualcmap` where kind is 'ave' * :ref:`four_dualcmap` where kind is 'ave' Miscellaneous ========================================================================================= Color Space Cmap Comparisons ------------------------------------------------- The colormap functions provide the creation of colormaps which are linear in RGB, HSV or Lab colorspace. A comparison between the differences is shown below using the same initial and final colors for the colormaps. The Matplotlib 'Blues' colormap is used as the reference colormap. .. image:: images/cmap_rgbhsvlab.png :class: sphx-glr-single-img The lightness, L*, for these colormaps is compared below. .. image:: images/cmap_rgbhsvlab_Lplot.png :class: sphx-glr-single-img Since the Lab, HSV_Lab and Blues_L colormaps are linear in Lab space, these line-plots overlap. A visualization of several linear colormaps in both Lab and RGB colorspace are included in the :ref:`cmap_colorspace` example. The above colormaps were generated using the following script fragment:: from colorspacious import cspace_converter blues = colormaps['Blues'] strRGB = cm.colors.to_rgb(blues(0.0)) endRGB = cm.colors.to_rgb(blues(1.0)) strHSV = cm.colors.rgb_to_hsv(strRGB) endHSV = cm.colors.rgb_to_hsv(endRGB) strLab = cspace_converter("sRGB1", "CAM02-UCS" )(strRGB) endLab = cspace_converter("sRGB1", "CAM02-UCS" )(endRGB) sLab = strLab[0]/100 eLab = endLab[0]/100 cm_rgb = cmu.rgb_cmap_gradient(strRGB,endRGB,name='RGB') cm_hsv = cmu.hsv_cmap_gradient(strHSV,endHSV,name='HSV') cm_lab = cmx.Lab_cmap_gradient(strRGB,endRGB,name='Lab') cm_Hlb = cmx.Hue_Lab_gradient(endRGB,sLab,eLab,name='Hue_Lab') cm_Blu = cmx.Cmap_Lab_gradient('Blues') cmaps = [cm_rgb,cm_hsv,cm_lab,cm_Hlb,cm_Blu,'Blues'] Colormap Figures ------------------------------------------------- Any colormaps can be readily analyzed and compared using the *cmap_xtra* function:: show_cmaps(plt,cmaps,onlyColormaps=True,show=True) where *plt* is *matplotlib.pyplot* and *cmaps* is a list of colormaps. The colormap list can also include strings, the names of registered colormaps. Three figures are generated: colormap images, grayscale colormap images and L* plot. If only the colormap figure is to be generated, the boolean *onlyColormaps* is set True (the default). By default, the *matplotlib.pyplot* method *show()* will be called unless the *show* argument is set to False. The following demonstrates three figures for a set of example colormaps. Visual color scale. .. image:: images/cmap_script_1.png :class: sphx-glr-single-img Visual L* gray scale. .. image:: images/cmap_script_2.png :class: sphx-glr-single-img x versus L* line plot. .. image:: images/cmap_script_3.png :class: sphx-glr-single-img Note that the x,L* plot does not reflect the alpha channel of the colormap. .. literalinclude:: source/gu_Lstar_PLOT.py :language: python DualCmap Figures ------------------------------------------------- Any dualcmaps can be readily analyzed and compared in a similar manner as with colormaps using:: show_dualcmaps(plt,dcmaps,usize=1,show=True) where *plt* is *matplotlib.pyplot* and *dcmaps* is a list of dualcmaps. The argument *usize* sets the size of the individual maps. The following demonstrates a figure for a set of example dualcmaps. The first two dualcmaps are used in the :ref:`complex_dualcmap` example. .. image:: images/dualcmap_script.png :class: sphx-glr-single-img The above colormaps were generated using the following script fragment: .. literalinclude:: source/gu_dualcmap_script.py :language: python The *show_dualcmaps* function is show below. .. literalinclude:: source/gu_dcmap_sh.py :language: python .. _css_colors: Named Colors ------------------------------------------------- There are numerous available colors in the `List of named colors `_ in Matplotlib, sorted by hsv color value. When constructing custom colormaps, comparing colors based on the Lab color values is often convenient. The following table lists the named CSS colors sorted by Lab color. .. image:: images/colors_by_lab.png :class: sphx-glr-single-img Summary Table ========================================================================================= .. image:: images/cmap_summary.png :class: sphx-glr-single-img The above colormaps were generated using the following script fragment:: def RYGCBM(t): #... function for op_cmap v = (0.5*( 1+np.cos(12*np.pi*t) ))**6 return t, np.ones(len(t)), v #.. HSV cmu.rgb_cmap_gradient('red','blue', name='1.') cmu.hue_cmap ('+red','blue', 1.6, name='2.') cmu.hsv_cmap_gradient('+tomato','lightblue', name='3.') cmu.stitch_cmap ('plasma','viridis','inferno',bndry=[0.2,0.6], name='4.') cmu.stitch_color ('red','yellow','green','blue',bndry=[0.3,0.4,0.8], name='5.') cmu.binary_cmap ('red','blue', name='6.') cmu.reversed_cmap ('viridis', name='7.') cmu.mirrored_cmap ('viridis', name='8.') cmu.alpha_cmap ('viridis',0.75, name='9.') cmu.op_alpha_cmap ('viridis',lambda t:np.cos(2*np.pi*t), name='10.' ) cmu.op_cmap (RYGCBM,False, name='11.') cmu.section_cmap ('viridis',0.2,0.8, name='12.') cmx.Lab_cmap_gradient('red','blue', name='13.') cmx.Hue_Lab_gradient ('red', name='14.') cmx.Cmap_Lab_gradient('gist_rainbow_r', hiL=0.7, name='15.') cmaps = [ str(i)+'.' for i in range(1,16)]