Wednesday, March 25th, 2026¶
Last class, we started working on creating tartan pattern images.
import numpy as np
import matplotlib.pyplot as plt
Project 3: Tartans (cont.)¶
Last class, we worked with the following example tartan recipe.
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]
We used the given recipe to generate an array containing the vertical stripes.
B = (52, 80, 100)
K = (16, 16, 16)
OG = (92, 100, 40)
total_width = 14 + 6 + 6 + 6 + 6 + 32 + 32
vertical_stripes = np.zeros((102, 102, 3), dtype=int)
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
We used the np.transpose function to "flip" the rows/columns of the vertical stripes array and create an array of corresponding horizontal stripes.
horizontal_stripes = np.transpose(vertical_stripes, axes=[1,0,2])
Let's check that the two arrays look correct.
plt.figure(figsize=(8,4))
plt.subplot(1,2,1)
plt.imshow(vertical_stripes)
plt.title('Vertical stripes')
plt.subplot(1,2,2)
plt.imshow(horizontal_stripes)
plt.title('Horizontal stripes')
plt.tight_layout()
Creating a tartan from vertical and horizontal arrays¶
For the project, we need to generate the authentic tartan pattern. As a warmup, can we generate a checkerboard pattern to interleave these horizontal/vertical 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), dtype=vertical_stripes.dtype)
checkerboard_tartan = np.zeros(vertical_stripes.shape, dtype=vertical_stripes.dtype)
checkerboard_tartan = np.zeros_like(vertical_stripes)
checkerboard_tartan = np.zeros((total_width, total_width, 3), dtype=int)
nrows, ncols = checkerboard_tartan.shape[:2]
for row in range(nrows):
for col in range(nrows):
if row % 2 == 0 and col % 2 == 0: # If row and column are even
checkerboard_tartan[row,col] = vertical_stripes[row,col]
elif row % 2 == 1 and col % 2 == 1: # If row and column are odd
checkerboard_tartan[row,col] = vertical_stripes[row,col]
else: # If row odd and column even or
# row even and column odd
checkerboard_tartan[row,col] = horizontal_stripes[row,col]
plt.figure(figsize=(8,8))
plt.imshow(checkerboard_tartan)
<matplotlib.image.AxesImage at 0x26e364e4f50>
Where to go from here:
- We need a better way of generating the
vertical_stripesarray. - We need to generate the more authentic tartan pattern described in the project page.
- We need to pad our tartan pattern to be
500by500rows/columns.
Generating the vertical_stripes array algorithmically¶
As mentioned last class, we would like to come up with a better way to generate the vertical_stripes array. That is, we don't want to have to manually define each stripe line-by-line, and we don't want to have to calculate appropriate slices by hand.
As a first step, let's focus on converting the given tartan pattern into something type of data that we can iterate through.
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]
Can we code this information as some sort of Python list(s)?
B = [52, 80, 100]
K = [16, 16, 16]
OG = [92, 100, 40]
widths = [14, 6, 6, 6, 6, 32, 32]
colors = [B, K, B, K, B, K, OG]
Can we find the total width of the pattern without manually adding the widths by hand?
total_width = sum(widths)
print(total_width)
102
With the total width calculated, we can intialize the vertical_stripes array that we will then fill with colored stripes.
Exercise: Use a for loop to iterate through each width/RGB pair and add the corresponding stripe to the vertical_stripes array.
vertical_stripes = np.zeros((total_width, total_width, 3), dtype=int)
start = 0
for width, color in zip(widths, colors):
end = start + width
vertical_stripes[:, start:end] = color
start = end
plt.imshow(vertical_stripes)
<matplotlib.image.AxesImage at 0x26e384c0e10>
This is a huge improvement on our previous code. Can we do better? It would be nice if we could use Python to automatically process the "recipe" to generate the lists of widths and colors. We will talk next week about how to process strings in a way that will help with this task.
We also need to tackle the following challenges for the project:
- We need to generate the more authentic tartan pattern described in the project page.
- We need to repeat our tartan pattern to fill a
500by500image.
Boolean NumPy arrays¶
A Boolean array is just a NumPy array that contains True and False values. That is, the dtype of the array is Bool. We can easily contruct Boolean arrays using Boolean expressions with arrays.
my_array = np.arange(10)
print(my_array)
[0 1 2 3 4 5 6 7 8 9]
my_bool_array = my_array % 3 == 1
print(my_bool_array)
[False True False False True False False True False False]
We can use logical operators on Boolean arrays:
~will negate a Boolean array (that is,TruebecomesFalseand vice-versa),&works likeandfor two Boolean arrays, and|works likeorfor two Boolean arrays.
print(~my_bool_array)
[ True False True True False True True False True True]
my_bool_array2 = my_array % 2 == 0
print(my_bool_array)
print(my_bool_array2)
[False True False False True False False True False False] [ True False True False True False True False True False]
print(my_bool_array & my_bool_array2)
[False False False False True False False False False False]
print(my_bool_array | my_bool_array2)
[ True True True False True False True True True False]
Boolean masks¶
One of the biggest strengths of using Boolean arrays is that they can be used as slicing tools for other arrays. For example, if a is an array (of any type) and mask is a Boolean array of the same shape, then a[mask] is a slice of the a array containing only the values where the corresponding value of mask is True.
We call this type of slicing masking, and the Boolean array the mask.
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]]
Suppose we want to identify the elements of the a array that have remainder 1 after division by 3.
Note: When masking, the resulting array slice is flattened into a 1D array (regardless of the shape of the original array). However, since the changes to the slice propogate back to the original array, we can make changes to this slice while preserving the layout of the data in the original array.
For example, suppose we want to subtract 100 from each value of the a matrix which has remainder 1 after division by 3.
mask = (a % 3 == 1)
print(a)
print(mask)
[[ 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]] [[False True False False True] [False False True False False] [ True False False True False] [False True False False True] [False False True False False]]
print(a[mask])
[ 1 4 7 10 13 16 19 22]
a[mask] -= 100
print(a)
[[ 0 -99 2 3 -96] [ 5 6 -93 8 9] [-90 11 12 -87 14] [ 15 -84 17 18 -81] [ 20 21 -78 23 24]]
Back to tartans: Masking (optional)¶
Exercise: Write a function checkerboard_mask(n) that takes in an integer n and constructs an n by n Boolean array that alternates True/False everytime you move through a row or column. For example, checkerboard_mask(6) should return an array that looks like:
$$
\begin{bmatrix}
\text{True} & \text{False} & \text{True} & \text{False} & \text{True} & \text{False}
\\
\text{False} & \text{True} & \text{False} & \text{True} & \text{False} & \text{True}
\\
\text{True} & \text{False} & \text{True} & \text{False} & \text{True} & \text{False}
\\
\text{False} & \text{True} & \text{False} & \text{True} & \text{False} & \text{True}
\\
\text{True} & \text{False} & \text{True} & \text{False} & \text{True} & \text{False}
\\
\text{False} & \text{True} & \text{False} & \text{True} & \text{False} & \text{True}
\end{bmatrix}$$
def get_checkerboard_mask(n):
checkerboard_mask = np.zeros((n,n), dtype=bool)
for row in range(n):
for col in range(n):
if (row + col) % 2 == 0:
checkerboard_mask[row, col] = True
return checkerboard_mask
n = 6
mask = np.zeros((n,n),dtype=bool)
rows = np.arange(n)
cols = np.arange(n)
ROWS, COLS = np.meshgrid(rows,cols)
mask[(ROWS + COLS) % 2 == 0] = True
print(mask)
[[ True False True False True False] [False True False True False True] [ True False True False True False] [False True False True False True] [ True False True False True False] [False True False True False True]]
COLS
array([[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[2, 2, 2, 2, 2],
[3, 3, 3, 3, 3],
[4, 4, 4, 4, 4]])
block = np.array([[True, False],
[False, True]])
print(block)
[[ True False] [False True]]
print(get_checkerboard_mask(5))
[[ True False True False True] [False True False True False] [ True False True False True] [False True False True False] [ True False True False True]]
Exercise: Use a Boolean array generated by the checkerboard_mask function as a mask to create the checkerboard tartan pattern from the vertical_stripes and horizontal_stripes arrays.
checkerboard_mask = get_checkerboard_mask(total_width)
checkerboard_tartan = np.zeros_like(vertical_stripes)
checkerboard_tartan[checkerboard_mask] = vertical_stripes[checkerboard_mask]
checkerboard_tartan[~checkerboard_mask] = horizontal_stripes[~checkerboard_mask]
plt.imshow(checkerboard_tartan)
<matplotlib.image.AxesImage at 0x26e3867c050>
Exercise: Write a function authentic_mask(n) that takes in an integer n and constructs an n by n Boolean array consisting of the sequences:
[True, True, False, False, True, True, False, ...]in the first row,[False, True, True, False, False, True, True, ...]in the second row,[False, False, True, True, False, False, True, ...]in the third row,[True, False, False, True, True, False, False, ...]in the fourth row,- ...
For example, authentic_mask(6) should return an array that looks like:
$$
\begin{bmatrix}
\text{True} & \text{True} & \text{False} & \text{False} & \text{True} & \text{True}
\\
\text{False} & \text{True} & \text{True} & \text{False} & \text{False} & \text{True}
\\
\text{False} & \text{False} & \text{True} & \text{True} & \text{False} & \text{False}
\\
\text{True} & \text{False} & \text{False} & \text{True} & \text{True} & \text{False}
\\
\text{True} & \text{True} & \text{False} & \text{False} & \text{True} & \text{True}
\\
\text{False} & \text{True} & \text{True} & \text{False} & \text{False} & \text{True}
\end{bmatrix}$$
Exercise: Use a Boolean array generated by the authentic_mask function as a mask to create the authentic tartan pattern from the vertical_stripes and horizontal_stripes arrays.