Color Map Effects on Face Images

As soon as you get some data, you may want to create a plot and see what it looks like. You may even want to share your findings with your colleagues in a way that is easiest for them to understand.

For these reasons, you will eventually need to choose the colors for you plot in order to summarize some information. A pre defined set of colors is called a color map, or cmap for short.

Color maps are useful because it saves you from picking colors by coloring your plot automatically. In some areas there are standard color maps, but usually, when unsure about what color to use, you just go with jet or grayscale.

To be honest, I find jet colors to be very appealling because it cleary shows low valleys in blue and the high peaks in red, with the values in between as some shades of yellowish green. The contrast feels great and the colors are shiny.

Just until recently I came across this article, which shows why jet color map is a bad idea. He explains in a very detailed way why jet color map may be highlighting some regions that are not supposed to. At first, I thought it was just some fancy concepts for designers and useless for engineers, so I kept using jet.

Until I started to visualize face images - which, in fact, humans are extremely good at noticing subtleties.

Now, it all make sense, and I decided to share why.

You can still use jet color map if you want, but I created this tutorial to uncover what problems you might face when using it.

Importing the libraries


In [1]:
%matplotlib inline
import numpy as np #as always
import matplotlib.pyplot as plt #to visualize things
import urllib #to download our dataset
from PIL import Image #to manipulate images
from StringIO import StringIO # these libraries are used to unzip 
from zipfile import ZipFile   # and store images in memory
from pylab import rcParams #just so I can resize my resulting images
import warnings #ignore warnings
warnings.filterwarnings('ignore')
rcParams['figure.figsize'] = 15, 15

Downloading the dataset


In [2]:
url1 = 'http://fei.edu.br/~cet/frontalimages_spatiallynormalized_part1.zip'
url2 = 'http://fei.edu.br/~cet/frontalimages_spatiallynormalized_part2.zip'
zip_files  = [urllib.urlretrieve(url1)[0],urllib.urlretrieve(url2)[0]]
archive = [ZipFile(zip_files[0],'r'),ZipFile(zip_files[1],'r')]

face_db = []
for name in archive[0].namelist()+archive[1].namelist():
   try:
      face = Image.open(StringIO(archive[0].read(name)))
   except:
      face = Image.open(StringIO(archive[1].read(name)))
   face_db.append(np.array(face))

face_db = np.array(face_db)

Greyscale color map

The most widely used color map for images is greyscale. This color map has uniform luminance, which results in perceptually uniform tones across the image.


In [3]:
np.random.seed(16)
f, axarr = plt.subplots(1,5)
for i in range(5):
    axarr[i].imshow(face_db[np.random.randint(face_db.shape[0])],cmap='gray')
    axarr[i].axis('off')


Jet color map

The jet color map is the most widely used non-uniform color map. It has its niche and can actually look nice for some visualizations, but since the luminance is non-uniform, it should be used with caution.


In [4]:
np.random.seed(16)
f, axarr = plt.subplots(1,5)
for i in range(5):
    axarr[i].imshow(face_db[np.random.randint(face_db.shape[0])],cmap='jet')
    axarr[i].axis('off')


Discussion

After these two examples, hopefully it's now clear to why some people say some non-uniform color maps (jet) are bad. In this example, we can clearly see that some important facial features, such as wrinkles and moles are almost gone, whereas some other facial regions are unnecessarily highlighted.

There are dozens of other options for color maps. The code below shows some of the color maps available on matplotlib. Some of them are uniform and some aren't. Nevertheless, all of them has an application that makes each of them suitable.

Can you identify which are uniform and which aren't?


In [5]:
np.random.seed(16)
face = np.random.randint(199)

cmaps = [cmap for cmap in plt.colormaps() if not cmap.endswith("_r")]

k=0
f, axarr = plt.subplots(7,12)
for j in range(7):
    for i in range(12):
        axarr[j,i].set_title(cmaps[k], fontsize=10)
        axarr[j,i].imshow(face_db[face],cmap=cmaps[k])
        axarr[j,i].axis('off')
        k+=1