Beyond the Basics: Creating Custom Colormaps in OpenCV
Beyond the Basics: Creating Custom Colormaps in OpenCV
Have you ever found yourself staring at OpenCV’s standard colormaps thinking, “These just don’t quite capture what I need”? You’re not alone! While OpenCV provides several built-in colormaps that work well for many applications, there are times when you need something more personalized.
In this guide, I’ll walk you through creating your own custom colormaps using OpenCV’s Lookup Table (LUT) functionality. Whether you’re working on scientific visualization, remote sensing data, or just want your images to pop with your brand colors, this technique will give you the flexibility you need.
Understanding Lookup Tables in OpenCV
Think of a lookup table as a cheat sheet for pixel transformations. For every possible input value (0-255 for an 8-bit grayscale image), the LUT specifies exactly what the output should be. For colormaps, we’re essentially saying: “When you see pixel value 128, transform it to this specific RGB color.”
Creating Our Custom Colormap Function
Let’s build a function that will create a lookup table based on a dictionary of color mappings:
import cv2
import numpy as np
import collections
def cvlut(colordict):
cmap = collections.OrderedDict()
cmap = colordict
crange = 0
clist = list(cmap.items())
r = []
g = []
b = []
while(crange < 256):
for i in range(len(clist)):
if i != (len(clist) - 1):
# Fill in the range between current color point and next color point
for n in range(clist[i][0], clist[i + 1][0]):
r.append(clist[i][1][0])
g.append(clist[i][1][1])
b.append(clist[i][1][2])
crange += 1
else:
# Handle the final range (to 255)
for n in range(clist[i][0], 256):
r.append(clist[i][1][0])
g.append(clist[i][1][1])
b.append(clist[i][1][2])
crange += 1
# Create the actual lookup table
lut = np.zeros((256, 1, 3), dtype=np.uint8)
lut[:, 0, 0] = r # Red channel
lut[:, 0, 1] = g # Green channel
lut[:, 0, 2] = b # Blue channel
return lut
What’s happening here? Our function takes a dictionary that maps grayscale values to RGB colors. For each range in the grayscale spectrum, we assign specific RGB values. The function builds three lists (one for each color channel) and then combines them into a properly formatted lookup table that OpenCV can use.
Putting Our Custom Colormap to Work
Now let’s see how we can apply our newly created function:
# Define color transitions at specific grayscale values
colormap_dict = {
0: [0, 0, 0], # Black at grayscale 0
111: [220, 8, 8], # Red at grayscale 111
128: [237, 252, 2], # Yellow at grayscale 128
171: [8, 220, 85], # Green at grayscale 171
213: [0, 163, 32] # Darker green at grayscale 213
}
# Create our custom LUT
custom_lut = cvlut(colormap_dict)
# Apply it to our grayscale image
colormap_img = cv2.LUT(gray2bgr_img, custom_lut)
The magic here is that we’re creating what’s called a “pseudocolor” or “false color” representation. We’re not changing any actual data in the image—just how it’s visualized. This technique is especially useful for highlighting features in scientific or medical imaging, satellite imagery, thermal scans, and more.
Pro Tip: Using External Colormap Files
While defining colors in your code works fine for simple cases, for more complex colormaps, I recommend creating a separate colormap file. Here’s what such a file might look like:
# custom_colormap.txt format: grayscale_value R G B
0 255 255 255
1 250 250 250
2 246 246 246
...
120 136 136 136
...
240 255 0 0
241 255 0 15
...
255 255 0 239
This approach makes it much easier to design detailed gradients or to reuse colormaps across different projects.
Complete Working Example
Here’s a full example you can try yourself:
import cv2
import numpy as np
import collections
def cvlut(colordict):
cmap = collections.OrderedDict()
cmap = colordict
crange = 0
clist = list(cmap.items())
r = []
g = []
b = []
while(crange < 256):
for i in range(len(clist)):
if i != (len(clist) - 1):
for n in range(clist[i][0], clist[i + 1][0]):
r.append(clist[i][1][0])
g.append(clist[i][1][1])
b.append(clist[i][1][2])
crange += 1
else:
for n in range(clist[i][0], 256):
r.append(clist[i][1][0])
g.append(clist[i][1][1])
b.append(clist[i][1][2])
crange += 1
lut = np.zeros((256, 1, 3), dtype=np.uint8)
lut[:, 0, 0] = r
lut[:, 0, 1] = g
lut[:, 0, 2] = b
return lut
if __name__=="__main__":
# Load a grayscale image
image = cv2.imread("path_to_your_image", cv2.IMREAD_GRAYSCALE)
# Convert grayscale to BGR to ensure proper dimensions for color mapping
gray2bgr_img = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
# Define our colormap with key grayscale points
colormap_dict = {
0: [0, 0, 0], # Black
111: [220, 8, 8], # Red
128: [237, 252, 2], # Yellow
171: [8, 220, 85], # Green
213: [0, 163, 32] # Dark green
}
# Generate and apply our custom colormap
custom_lut = cvlut(colormap_dict)
colormap_img = cv2.LUT(gray2bgr_img, custom_lut)
# Save the result
cv2.imwrite('custom-colormap-result.jpg', colormap_img)
# Display the result (optional)
cv2.imshow("Original", image)
cv2.imshow("Custom Colormap", colormap_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Practical Applications
This technique is incredibly useful for:
- Remote sensing: Highlight vegetation, water, or urban areas in satellite imagery
- Medical imaging: Emphasize specific structures in MRI or CT scans
- Scientific visualization: Make patterns in your data more visible
- Thermal imaging: Create intuitive hot-to-cold gradients
- Brand consistency: Apply your organization’s color scheme to data visualizations
Next Steps to Explore
Once you’ve mastered the basics of custom colormaps, consider these advanced techniques:
- Creating smooth transitions between color points using interpolation
- Building interactive tools to adjust colormap parameters in real-time
- Combining multiple colormaps for multi-channel data
Custom colormaps may seem like a small detail, but they can dramatically improve how effectively your visualizations communicate information. Give it a try on your next project!