Is there any good color map to convert gray-scale image to colorful ones using python's PIL?

32,052

Solution 1

I figured out with the duplicate answer mentioned by @ImportanceOfBeingErnest (How to convert Numpy array to PIL image applying matplotlib colormap)

import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

import timeit

from PIL import Image

def pil_test():
    cm_hot = mpl.cm.get_cmap('hot')
    img_src = Image.open('test.jpg').convert('L')
    img_src.thumbnail((512,512))
    im = np.array(img_src)
    im = cm_hot(im)
    im = np.uint8(im * 255)
    im = Image.fromarray(im)
    im.save('test_hot.jpg')

def rgb2gray(rgb):
    return np.dot(rgb[:,:,:3], [0.299, 0.587, 0.114])

def plt_test():
    img_src = mpimg.imread('test.jpg')
    im = rgb2gray(img_src)
    f = plt.figure(figsize=(4, 4), dpi=128)
    plt.axis('off')
    plt.imshow(im, cmap='hot')
    plt.savefig('test2_hot.jpg', dpi=f.dpi)
    plt.close()

t = timeit.timeit(pil_test, number=30)
print('PIL: %s' % t)
t = timeit.timeit(plt_test, number=30)
print('PLT: %s' % t)

The performance result is:

PIL: 1.7473899199976586
PLT: 10.632971412000188

They both give me similar result with hot color map.

Test Image with hot CMap

Solution 2

You can use the color maps from matplotlib and apply them without any matplotlib figures etc. This will make things much faster:

import matplotlib.pyplot as plt

# Get the color map by name:
cm = plt.get_cmap('gist_rainbow')

# Apply the colormap like a function to any array:
colored_image = cm(image)

# Obtain a 4-channel image (R,G,B,A) in float [0, 1]
# But we want to convert to RGB in uint8 and save it:
Image.fromarray((colored_image[:, :, :3] * 255).astype(np.uint8)).save('test.png')

Note:

  • If your input image is float, the values should be in the interval [0.0, 1.0].
  • If your input image is integer, the integers should be in the range [0, N) where N is the number of colors in the map. But you can resample the map to any number of values according to you needs:

    # If you need 8 color steps for an integer image with values from 0 to 7:
    cm = plt.get_cmap('gist_rainbow', lut=8)
    
Share:
32,052
Adam
Author by

Adam

Updated on July 09, 2022

Comments

  • Adam
    Adam almost 2 years

    Matplotlib has a lot of good color maps, but is bad in performance. I'm writing some code to make gray-scale image colorful where interpolate with color map is a good idea. I wonder whether there are open source color maps available or demo code to use Pillow to convert gray-scale images into colorful ones via colormap?


    Clarify:

    1. Matplotlib is good for demo use, but bad performace for thounsands of images.
    2. Matplotlib colormaps
    3. You can map grayscale images to colormaps to get colorful ones.

    Demo:

    The first image is grayscale, second is mapped in 'jet' cmap, third being 'hot'.

    Matplotlib demo

    The problem is that I do not know much about colors, and I'd like to achieve such effects in PIL for better performance.

    • Bart
      Bart about 7 years
      Please clarify your question.. "but is bad in performance", why is it bad in performance? "where interpolate with color map is a good idea", what do you mean? " to convert gray-scale images into colorful ones", in what way? What colors should be mapped to which gray tones? Do you have an example of the input image, and what the result should be?
  • NichtJens
    NichtJens almost 6 years
    While it does not mitigate the timing difference completely, you certainly should switch the matplotlib backend to a non-interactive one. You want PNGs, so use mpl.use("agg"). Tested with your code, it looks about twice as fast.