Monday, March 23rd, 2026¶
Before the break, we started to discuss representing RGB images as 3D numpy arrays.
import numpy as np
import matplotlib.pyplot as plt
RGB(s) image arrays¶
Recall: Most computer images are stored as arrays of RGB(A) values. We can read an image file into an RGB(A) array using plt.imread. The syntax is: plt.imread(<path to some image file>). For example, download the image mario.png from the course webpage and place it into the same folder as this Jupyter notebook.
mario = plt.imread('mario.png')
print(mario.shape)
plt.imshow(mario)
(224, 256, 4)
<matplotlib.image.AxesImage at 0x23c9ab62490>
We can look at the shape of the array to see if it contains RGB or RGBA values. In this case, the mario.png image file includes a transparency channel.
print(mario.shape)
(224, 256, 4)
Exercise: Use NumPy slicing to remove the transparency channel from the mario array.**
mario = mario[:, :, :3]
plt.imshow(mario)
<matplotlib.image.AxesImage at 0x23c92747390>
Exercise: Use NumPy slicing and plt.imshow to zoom in on Mario (in the lower-left corner).
plt.imshow(mario[175:210, 25:75])
<matplotlib.image.AxesImage at 0x23c91f84690>
Exercise: Create an array blueless_mario where the blue channel information from mario has been removed (i.e. set to 0) and plot using plt.imshow.
plt.imshow(mario[:,:,2],cmap='Blues',vmin=0,vmax=1)
<matplotlib.image.AxesImage at 0x23c9926fc50>
blueless_mario = mario.copy()
blueless_mario[:, :, 2] = 0
plt.imshow(blueless_mario)
<matplotlib.image.AxesImage at 0x23c91fe2490>
Exercise: Create an array mixed_mario where:
- the red channel of
mixed_mariomatches the green channel ofmario, - the green channel of
mixed_mariomatches the blue channel ofmario, - the blue channel of
mixed_mariomatches the red channel ofmario.
mixed_mario = mario.copy()
mixed_mario[:,:,0] = mario[:,:,1]
mixed_mario[:,:,1] = mario[:,:,2]
mixed_mario[:,:,2] = mario[:,:,0]
plt.imshow(mixed_mario)
<matplotlib.image.AxesImage at 0x23c992ef110>
mario[0,0]
array([0.36078432, 0.5803922 , 0.9882353 ], dtype=float32)
Note: For the next project, we will be working with integer-valued RGB triples. The integer-values will range from 0 to 255. An integer 0 means no color while an integer 255 means full color (equivalent to a float of 1).
Exercise: Convert the mario array to an integer-type array mario_RGB_int containing RGB triples with values between 0 and 255.
mario_int = (255 * mario).astype(int)
mario_int[0,0]
array([ 92, 148, 252])
plt.imshow(mario_int)
<matplotlib.image.AxesImage at 0x23c99515810>
Exercise: Convert the mario_RGB_int array into a float-type array containing RGB triples with values between 0 and 1.
mario_float = mario_int / 255
plt.imshow(mario_float)
<matplotlib.image.AxesImage at 0x23c9abed590>
Project 3: Tartans¶
Let's get started working with tartans by generating vertical and horizontal stripes for the following pattern (see project page for details):
Pattern :
B14 K6 B6 K6 B6 K32 OG32
where the colors B, K, and OG are given by the RGB triples:
B : [52, 80, 100]
K : [16, 16, 16]
OG : [92, 100, 40]
To get started, we'll need to initialize an array with the correct shape. What is the total width of this pattern?
total_width = 14 + 6 + 6 + 6 + 6 + 32 + 32
print(total_width)
102
vertical_stripes = np.zeros((102, 102, 3), dtype=int)
Right now, our array is a pure black picture. Let's add the first vertical stripe, which has color B = [52, 80, 100] and has width 14.
vertical_stripes[:, :14] = (52, 80, 100)
vertical_stripes[:, 14:20] = (16, 16, 16)
plt.imshow(vertical_stripes)
<matplotlib.image.AxesImage at 0x23c9b127ed0>
widths = [14, 6, 6, 6, 6, 32, 32]
colors = ...
first_names = ['Jon', 'Callen', 'Morgan']
last_names = ['Lottes', 'Dalton', 'Gunther']
for first_name, last_name in zip(first_names, last_names):
print(first_name, last_name)
Jon Lottes Callen Dalton Morgan Gunther
Exercise: Add the remaining stripes from the sample pattern to the vertical_stripes array.
Pattern :
B14 K6 B6 K6 B6 K32 OG32
where the colors B, K, and OG are given by the RGB triples:
B : [52, 80, 100]
K : [16, 16, 16]
OG : [92, 100, 40]
B = (52, 80, 100)
K = (16, 16, 16)
OG = (92, 100, 40)
vertical_stripes[:, :14] = B
vertical_stripes[:, 14:20] = K
vertical_stripes[:, 20:26] = B
vertical_stripes[:, 26:32] = K
vertical_stripes[:, 32:38] = B
vertical_stripes[:, 38:70] = K
vertical_stripes[:, 70:102] = OG
plt.imshow(vertical_stripes)
<matplotlib.image.AxesImage at 0x23c9b2f5d10>
Some thoughts:
- We need a much better way to generate this vertical stripes array. There was far too much manual typing/calculation to add each stripe. We'll come back and address this shortly.
- Now that we have an array of vertical stripes, we can easily generate an array of horizontal stripes. This can be done transposing our array (see below).
- Once we have vertical and horizontal stripes, we need to super-impose them somehow to generate the tartan pattern.
Array transposes¶
For a 2-dimensional matrix, the transpose flips rows and columns. That is, the first row becomes the first column, the second row becomes the second column, etc. In Python, we can use the .T method on a 2D array to get its transpose:
A = np.arange(25).reshape(5,5)
print(A)
[[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19] [20 21 22 23 24]]
print(A.T)
[[ 0 5 10 15 20] [ 1 6 11 16 21] [ 2 7 12 17 22] [ 3 8 13 18 23] [ 4 9 14 19 24]]
We would like to take the transpose of our vertical_stripes array so that the columns of vertical_stripes become the rows of horizontal_stripes. In other words, the vertical stripes will become horizontal stripes.
Problem: The vertical_stripes array is a 3-dimensional array (rows, columns, color channels). What does vertical_stripes.T give us in this case?
vertical_stripes.T.shape
(3, 102, 102)
It turns out that the .T attribute reverses the order of the axes. That is, the first axis (rows) becomes the last axis, the second axis (columns) becomes the second-last axis, etc. In the case of the vertical_stripes array, the color channel axis became the row axis, the column axis remained as the column axis, and the row axis became the color channel axis. For our needs, this is not useful. Instead, we just want to swap the row and column axes.
We can use the np.transpose function to do more targeted transposing:
#help(np.transpose)
When calling np.transpose, we can optionally supply a keyword argument axes which gives a permutation of the axes of the array. In particular, using axes = [1, 0, 2] will give a transposed matrix where:
- the old axis
1(i.e. the columns) becomes the new axis0(i.e. the rows), - the old axis
0becomes the new axis1, and - the old axis
2(i.e. the color channel) remains as axis2.
Let's use this to define the horizontal_stripes array.
horizontal_stripes = np.transpose(vertical_stripes, axes=[1,0,2])
plt.imshow(horizontal_stripes)
<matplotlib.image.AxesImage at 0x23c9b1cfc50>
Creating a tartan from vertical and horizontal arrays¶
We now have vertical and horizontal stripes. How can we combine them to get a tartan pattern?
One simple idea is to take the average of the horizontal and vertical stripe arrays.
averaged_tartan = (horizontal_stripes + vertical_stripes) // 2
plt.imshow(averaged_tartan)
<matplotlib.image.AxesImage at 0x23c9c503890>
This gives flat colors rather than an interleaved combination. Can we instead generate a checkerboard pattern to interleave these stripes? To do so, we want to go row by row, column by column, and alternatingly select a color from the vertical_stripes and horizontal_stripes arrays.
Exercise: Create a checkerboard_tartan array that combines the vertical_stripes and horizontal_stripes arrays in checkerboard pattern.
checkerboard_tartan = np.zeros((102, 102, 3))
for row in range(102):
for col in range(102):