Colors can be very handy when it comes to showing data. For instance, the temperature (which is currently way to low) during a day within a region of a country can be highlighted on a map using different colors. In finite element analysis, the computed stress in a structure can be shown using different colors and so on. This brings us to the topic of this blog:
Should I care about which colormap I use when visualizing data??
Perhaps this was a bit to sloppy formulation... Anyway we are going to look at some data using different colormaps and see what happens.
Let's start of with something interesting so why not a picture... of my dog... So, let's fire up a jupyter notebook, import some packages and then import and visualize an image of my dog
import matplotlib.pyplot as plt import matplotlib.image as mpimg from scipy import ndimage img_raw = mpimg.imread('Pics_Blog_colormaps/Moltas_2.JPG') img = ndimage.rotate(img_raw, -90) plt.figure(figsize=(20,10)) plt.imshow(img) plt.axis('off') plt.show()
And the result would look like
To convert the color image to grayscale I have used one of the algorithmes found on this informative webpage.
gray = np.dot(img[...,:3], [0.2989, 0.5870, 0.1140]) gray = gray / np.max(gray) plt.figure(figsize=(20,10)) plt.imshow(gray,cmap='gray') plt.axis('off') plt.colorbar() plt.show()
Nice, seems that the conversion to grayscale worked quite OK. Each point in the picture now has a value between 0 and 1 it is shown using the "gray" colormap. Now, what will happen if we show this picture in a rainbow type colormap (in python matplotlib it is called jet)? To do so we just exchange cmap = 'gray' to cmap = 'jet'
Hmm... what happened to the dog... The right side of the dog is looking kind of strange. Moreover, if you look at the sky, the right side of the picture is very light in the original picture (as well as the gray scale) and the left side is kind of darker. This seems to be the opposite when viewing the image in this rainbow colormap... interesting. We will get to why this happens in a section below but for now we can say that the data (i.e. the intensity of each point) is perceived differently when using the grayscale colormap compared to the rainbow colormap.
Since a few years back, the default colormap in matplotlib was changes from jet to a Perceptually Uniform Sequential Colormap called viridis (green in latin). In addition to the viridis colormap, there are some other colormaps with similar characteristics. Let's see how the dog looks in some of these Perceptually Uniform Sequential Colormaps
Nice! Lots of different colors and it still looks like a dog! Moreover, the issue with the perceived darkness of the sky found with the jet colormap is not present here. If you would like to read more about different colormaps available, check out this matplotlib web page
Let's get (sort of) quantitative
The example with my dog above is perhaps a bit to complex (but fun) to really understand what is going on with the different colormaps, so it is time for a simpler example. Let's have a look at pyramid where the height of the pyramid is represented by a color. (I have borrowed the example from here)
from mpl_toolkits.mplot3d import Axes3D x0 = np.loadtxt('Pics_Blog_colormaps/data/GIZA_X.txt') y0 = np.loadtxt('Pics_Blog_colormaps/data/GIZA_Y.txt') z0 = np.loadtxt('Pics_Blog_colormaps/data/GIZA_Z.txt') x = np.reshape(x0,(482,482)) y = np.reshape(y0,(482,482)) z = np.reshape(z0,(482,482)) fig = plt.figure(figsize=(16,5)) ax1 = fig.add_subplot(1, 2, 1, projection='3d') p1 = ax1.plot_surface(x, y, z, rstride=1, cstride=1, cmap='jet', linewidth=0, antialiased=False, shade=True) cb1 = fig.colorbar(p1, shrink=0.7) ax1.view_init(30,45) ax2 = fig.add_subplot(1, 2, 2) p2 = ax2.scatter(x0,y0,c=z0,cmap='jet') cb2 = fig.colorbar(p2, shrink=0.7) ax2.axis('equal') plt.show()
The left hand figure is a 3d surface plot of the pyramid and in the figure to the right the same pyramid is shown from above (2d plot). I don't know what you think, but when I look at the two figures above it seems to me that there is something going on around the turquoise color as well as in the area around the yellow color (marked with arrows). When looking at the colors I get the feeling that the elevation of the pyramid is not constant, which it is, so perhaps the colors do not do a good job representing the data. What would the figures look like if plotted in the gray colormap?
Ok, not that colorful (obviously) but the impression is that the height of the pyramid is increasing at a constant rate. Let's continue with showing the same figures in the same Perceptually Uniform Sequential Colormaps as above
I would like to say that they all do a nice job of visualizing an increase of height with a constant rate. Now, we get to the quantitative part. Let's plot the perceived lightness, L*, of the colormaps, I have used the formulas from this stack overflow post to calculate the L* value.
def sRGBtoLin(colorChannel): # Send this function a decimal sRGB gamma encoded color value # between 0.0 and 1.0, and it returns a linearized value. out = np.zeros(len(colorChannel)) for ii in range(0,len(out)): if colorChannel[ii] <= 0.04045 : out[ii] = colorChannel[ii] / 12.92 else: out[ii] = np.power(((colorChannel[ii] + 0.055)/1.055),2.4) return out from matplotlib import cm cmap_name = 'jet' # get the colormap my_cmap = cm.get_cmap(cmap_name) # extract the colormap RGBA values at the 16 points rgba = my_cmap(np.arange(0,1,0.001)) # slice rgba to discard Alpha rgb01= rgba[:,:3] #print (rgb01) rgb255 = np.multiply(rgb01,255) rgb = np.rint(rgb255) # rounds up to nearest integer Y = (0.2126 * sRGBtoLin(rgb01[:,0]) + 0.7152 * sRGBtoLin(rgb01[:,1]) + 0.0722 * sRGBtoLin(rgb01[:,2])) L_star = np.zeros(len(Y)) for ii in range(0,len(Y)): if Y[ii] <= (216/24389): L_star[ii] = Y[ii] * (24389/27) else: L_star[ii] = np.power(Y[ii],(1/3)) * 116 - 16 X = np.arange(0,len(L_star)) plt.figure(figsize=(10,8)) plt.ylim(-5,105) plt.xlim(-50,1050) plt.scatter(X,L_star,c=X,s=120,cmap=cmap_name) plt.ylabel('L*') plt.show()
What i have done is just to calculate the L* values for 1000 evenly spread points over the colormap. For the rainbow (Jet) colormap the results looks like this
Hmmm, there are some change of slope in the curve, two quite severe ones located around the turquoise color and yellow color. These change of slope corresponds rather good to the two areas shown in the pyramid plot for the rainbow colormap above. It is also shown that the L* value is increasing up to the sort of yellow color and then start to decrease. This is also consistent with the reversted lightness of the sky found in the pictures of the dog above. Now, lets see what the same plot looks like for a gray scale colormap.
Ok, this looks linear and nice. Not that colorful though.. Finally let's see how the Perceptually Uniform Sequential Colormaps used above look like
Sweet! All of the colormaps show a nice (almost) linear increase of the perceived lightness.
One more thing to add. If a figure that uses the rainbow colormap is printed in grayscale it would look something like this
As you can see it is impossible to distinguish between very high and low values which is also a consequence of the low perceived lightness for both low and high values. So if you want to make sure that your figures make sense even if printed in grayscale better not use a rainbow colormap. This is probably not that much of a problem as it was say 20 years ago (this has happened to me several times in the past).
Finally, I would also like to mention a different category of colormaps, the diverging colormap. Sometimes we would like to show, for instance, if the current value is higher or lower than some reference value. This could be if the temperature at a certain location is above or below the normal temperature. Let's use such a colormap for the pyramid example where we would like to show which part of the pyramid that is higher and lower than 200. In this example the colormap RdBu_r is used
Here we can see that values that are close to 200 will get an almost white color and the further away from the 200 mark the point is, the darker the color. Just a final note that this colormap would not do a great job if printed in grayscale since it would be difficult to see if the deviation from 200 is positive of negative
Colormaps in LS-prepost
As I do fair amount of finite element analysis during my daily work I thought it would be fun to show how you can incorporate any colormap from matplotlib into my favorite software of post processing of finite element results, LS-prepost. First of all we will create a text file that describes the colormap. This can be done with a few lines of python code.
f = open('plasma.txt','w') f.write('* Fringe color palette\n') for ii in range(0,20): aa = cm.plasma(int(256/20*ii)) f.write(' %i %4.3f %4.3f %4.3f\n' %(ii+1,aa[0],aa[1],aa[2]) ) f.close()
And the result would look something like this
Now let's see what it would look like for one of the many great examples that are available from the LS-Dyna example web page, the particular example used here is from a metal forming process and can be found here. After loading the result files open the fring setting options, press the palette button followed by the load button and select one of the files create by the python script above.
Now to the visualization! This is what a plot of the effective plastic strain looks like with the default rainbow colormap:
and with a grayscale colormap
and finally a Perceptually Uniform Sequential Colormaps (Plasma)
Well, not to bad! Perhaps one word of caution regarding the grayscale colormap. When visualizing a 3D structure like this, usually there is some sort of shading going on. This can then give the impression that a values within a shaded area are lower than they actually are.
Note that the intention here is just to show how you can include one of the many colormaps from matplotlib into LS-prepost and not to give an analysis of the finite element results.
Time to conclude
The intention of this blog post is not to say: the rainbow colormap is dead... so don't use it ,or: if you use the rainbow colormap people are going to die... so don't use it. It is more to show that there are other options. I think that, depending on what you are trying to show, the diverging colormaps can be quite interesting to use.
Alright, another blog post has come to its end. We are going on vacation now for a couple of weeks but are back again in the autumn with new (hopefully) interesting blog posts. If you have questions or comments, just send us an email, info@gemello.se, and we will get back to you.