Apply MatplotLib or custom colormap to OpenCV image
For Python >= 2.7, cmapy packages this functionality in a convenient way. Install it with:
Python 2.7:
pip install cmapy
Python 3.x:
pip3 install cmapy
Or, for Anaconda (from conda-forge):
conda install -c conda-forge cmapy
And use it like this:
import cv2
import matplotlib.pyplot as plt
import cmapy
# Read image.
img = cv2.imread('imgs/woman.png')
# Colorize.
img_colorized = cv2.applyColorMap(img, cmapy.cmap('viridis'))
# Display
plt.imshow(img_colorized)
plt.show()
Different colormaps give something like this:
See all the available colormaps in action here.
Disclaimer: I wrote cmapy (because I needed this functionality for another project), and internally, it does pretty much the same as the other answers.
Answering my own question because I did not find an easy solution on StackOverflow:
def apply_custom_colormap(image_gray, cmap=plt.get_cmap('seismic')):
assert image_gray.dtype == np.uint8, 'must be np.uint8 image'
if image_gray.ndim == 3: image_gray = image_gray.squeeze(-1)
# Initialize the matplotlib color map
sm = plt.cm.ScalarMappable(cmap=cmap)
# Obtain linear color range
color_range = sm.to_rgba(np.linspace(0, 1, 256))[:,0:3] # color range RGBA => RGB
color_range = (color_range*255.0).astype(np.uint8) # [0,1] => [0,255]
color_range = np.squeeze(np.dstack([color_range[:,2], color_range[:,1], color_range[:,0]]), 0) # RGB => BGR
# Apply colormap for each channel individually
channels = [cv2.LUT(image_gray, color_range[:,i]) for i in range(3)]
return np.dstack(channels)
image_gray = cv2.imread('./lena.jpg', cv2.IMREAD_GRAYSCALE)
image_bgr = apply_custom_colormap(image_gray, cmap=plt.get_cmap('bwr'))
cv2.imshow('image with colormap', image_bgr)
cv2.waitKey(0)
Produces the image:
In recent versions of OpenCV (starting with 3.3), there's an overload of applyColorMap
, which allows you to provide a custom colormap (either 1 or 3 channel). I've modified verified.human's code to simply generate a colormap suitable to use with this function.
I've taken few more opportunities to simplify the code:
ScalarMappable.to_rgba
can return bytes (in range 0-255) directly when you setbytes
argument toTrue
.- We can use array indexing with negative step size to remove the alpha channels as well as switch from RGB to BGR in one step
Code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
def get_mpl_colormap(cmap_name):
cmap = plt.get_cmap(cmap_name)
# Initialize the matplotlib color map
sm = plt.cm.ScalarMappable(cmap=cmap)
# Obtain linear color range
color_range = sm.to_rgba(np.linspace(0, 1, 256), bytes=True)[:,2::-1]
return color_range.reshape(256, 1, 3)
image_gray = cv2.imread('cage.png', cv2.IMREAD_GRAYSCALE)
image_bgr = cv2.applyColorMap(image_gray, get_mpl_colormap('bwr'))
cv2.imshow('image with colormap', image_bgr)
cv2.waitKey()