๐จ๐๐ Crafting Color Imagesยถ
This page demonstrates how to load RGB color images into a three-dimensional array in Python, how to think about the numbers in that array, and how to construct a color image from three different two-dimensional images. Please work through these examples, discuss with your classmates or instructors, and try the activities on your own.
import matplotlib.pyplot as plt
import numpy as np
How we load a color image into an array?ยถ
Many of us are probably familiar with lots of different color image formats, including .jpg, .png, .gif, .tif, and other. Whereas a single FITS image extension will usually have one brightness value per pixel, these color images store three brightness values per pixel, one each for red, green, blue.
We can read these image files into arrays providing the plt.imread function with the file path to an image. Please copy the file /home/zkbt/astr3510/rainbow.jpg into the same directory as your notebook, and then run this code. This function uses the Python Imaging Library (PIL.Image.open), so if you need more control over loading images you should work with that directly.
rainbow = plt.imread('rainbow.jpg')
The variable rgb is now a three-dimensional array, with the first dimension corresponding to pixel row, the second to pixel column, and the third to wavelength band. This image has 1434 rows, 1764 columns, and 3 color bands (red, green, blue).
rainbow.shape
(1434, 1764, 3)
If our trusty friend plt.imshow receives a three-dimensional array with 3 elements in the third dimension, it will try to interpret the array as a color image and display it as such.
plt.figure(dpi=300)
plt.imshow(rainbow);
This magic trick of treating a 3D array as a color image will work as long as the values in the image are integer numbers between 0 and 255 ($= 2^8 - 1$ for 8-bit color), or floating point numbers between 0.0 and 1.0. Let's look at the RGB values for the first row and first column of our image:
rainbow[0,0,:]
array([184, 183, 118], dtype=uint8)
The data type of the numbers is "unsigned 8-bit integer", meaning whole numbers between 0 and 255. The RGB values for the first row and first pixel are R=184, G=183, B=118, where 0 means no light of that color and 255 means the brightest possible light for that color. If the three values were all the same, the color would be some shade of gray; since there's a little less blue, the color should be shifted a little toward orange-ish. To my eye, the upper left region looks like a warm gray!
Discuss!ยถ
Talk to someone:
- Pick at least one pixel in the color image, guess what its RGB pixel brighness values will be, then print out the values to compare. If you can't see some colors, ask your partner(s) to point out what's what.
What do the individual color channel arrays look like?ยถ
Let's dig into the details of this image a little more. Since we might want to similar actions for a few different images, let's write a function that we can resuse. (The code of this function is available in /home/zkbt/astr3510/rgb.py, if you just want to copy and paste it from there.)
def show_rgb_separately(some_rgb_image, cmap='gray'):
'''
This function provides a handy way to look at the
individual RGB channels of a color image.
Parameters
----------
some_rgb_image : the color image array to display
An array with dimensions (rows, columns, 3), with the
last dimension corresponding to the three RGB colors.
cmap : str
Which matplotlib colormap should we use for displaying brightness?
(default = 'gray', with black at bottom and white at top)
'''
# give names to the colors to use as titles
colors = ['red', 'green', 'blue']
# check if the units are integer (0-255) or not (0.0-1.0)
if some_rgb_image.dtype == np.uint8:
vmax = 255
else:
vmax = 1.0
# set up big figure to fill with plots
fi = plt.figure(figsize=(12,12), dpi=300)
# set up a grid of panels into which we can plot
grid = plt.GridSpec(2, 3, height_ratios=[1, 3])
# loop through the three color channels
for i in range(3):
# point toward the panel in row 0, column i
plt.subplot(grid[0,i])
# show the image for this color channel in this panel
plt.imshow(some_rgb_image[:,:,i], cmap=cmap, vmin=0, vmax=vmax)
# add a title to the panel
plt.title(colors[i])
# point toward row 1, all columns
plt.subplot(grid[1,:])
# show the color image
plt.imshow(some_rgb_image)
Now let's try out our function on our color image.
show_rgb_separately(rainbow)