Recall: your final tartan should be a 500
by 500
(by 3
) array. You will need to think about how to repeat your pattern to reach the desired size.
One strategy: duplicate your assigned pattern sufficiently many times to exceed the desired tartan size.
[1,2,3,4] * 5
Once we have a pattern that exceeds the desired tartan size, we can take a slice of the generated tartan that has the correct size:
generated_tartan[:500, :500]
Another strategy is to user modular division to iterate through your pattern until you've filled a 500
by 500
(by 3
array):
my_list = ['a','b','c','d','e','f','g']
for i in range(10):
letter = my_list[i % len(my_list)]
print(letter)
You will need to work out how to generate the authentic tartan pattern.
For the checkerboard pattern, we could use four if
/elif
/else
statements to decide whether to pull from the horizontal or vertical stripes:
if i % 2 == 0 and j % 2 == 0:
tartan[i,j] = horizontal_stripes[i,j]
elif i % 2 == 0 and j % 2 == 1:
tartan[i,j] = vertical_stripes[i,j]
elif i % 2 == 1 and j % 2 == 0:
tartan[i,j] = vertical_stripes[i,j]
else:
tartan[i,j] = horizontal_stripes[i,j]
We could do the same for the authentic pattern, but we'd have sixteen if
/elif
/else
statements.
if i % 4 == 0 and j % 4 == 0:
tartan[i,j] = horizontal_stripes[i,j]
elif i % 4 == 0 and j % 4 == 1:
tartan[i,j] = horizontal_stripes[i,j]
elif i % 4 == 0 and j % 4 == 2:
tartan[i,j] = vertical_stripes[i,j]
...
...
$1 / \left(\frac{1}{4}\right)$
$\frac{1}{\frac{1}{4}}$
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=3, ncols=4)
t = np.linspace(0,2*np.pi,1000)
for i in range(3):
for j in range(4):
ax = axes[i,j]
ax.plot(t,np.sin((i+1)*(j+1)*t))
ax.grid()
ax.set_xlim((0,2*np.pi))
ax.set_ylim((-1,1))
for ax in axes[:,0]:
ax.set_ylabel('$x(t)$')
for ax in axes[-1,:]:
ax.set_xlabel('$t$')
for ax in axes[:,1:].flatten():
ax.set_yticklabels([])
for ax in axes[:-1,:].flatten():
ax.set_xticklabels([])
fig.tight_layout()
%matplotlib inline
s = 'This is a string.'
s.split('is')
list_of_strings = ['Hello', 'my name is','Jon']
'1234'.join(list_of_strings)
We can also use the .replace
method to substitute a string for a substring:
print(s)
s.replace('is', '!!Hello!!')
N = 1000
ptriples = get_ptripes(N)
slopes = [5/12, 3/4]
colors = ['b','r']
for slope,color in zip(slopes,colors):
plt.plot([0,N], [0, N*slope], color=color)
plt.plot([0,N], [0, N/slope], color=color)
I've downloaded the hamburg.png
image from the project page into my Jupyter notebook directory, so let's open it:
import matplotlib.pyplot as plt
import numpy as np
hamburg = plt.imread('hamburg.png')
plt.imshow(hamburg)
What is the structure of this array that we've loaded?
print(hamburg.shape)
This array has 600
rows (meaning our image is 600
pixels tall), 900
columns (meaning our image is 900
pixels wide), and has 3
color channels (namely, red/green/blue).
For this project, we would like to work with grayscale values rather than RGB values. In other words, we want to collapse the three color channels down to just one.
(hamburg[:,:,0] == hamburg[:,:,1]).min()
(hamburg[:,:,0] == hamburg[:,:,2]).min()
(hamburg[:,:,1] == hamburg[:,:,2]).min()
Since our image contains purely gray pixels (i.e. the red, green, blue channels all match), we can simply take one of the three channels to get a grayscale value:
hamburg_gray = hamburg[:,:,0] # Take just the red-channel information
print(hamburg_gray.shape)
plt.imshow(hamburg_gray)
We need to specify our colormap, in particular, cmap='gray'
.
plt.imshow(hamburg_gray, cmap='gray')
To be safe, let's specify vmin=0
and vmax=1
to make sure that plt.imshow
is correctly drawing our grayscale values:
plt.imshow(hamburg_gray, cmap='gray', vmin=0, vmax=1)
For the above image, we started with a grayscale image represented as an RGB array. What if we start from a full-color image?
# The "Pilotwings.jpg" file has integer RGB values
# We will rescale by 255 to get floating point values
pilotwings = plt.imread('Pilotwings.jpg') / 255
plt.imshow(pilotwings)
print(pilotwings.shape)
How can we generate a grayscale version of this image?
pilotwings_red = pilotwings[:,:,0]
pilotwings_green = pilotwings[:,:,1]
pilotwings_blue = pilotwings[:,:,2]
fig = plt.figure(figsize=(6,6))
plt.subplot(2,2,1)
plt.imshow(pilotwings)
plt.title('Original')
plt.axis('off')
plt.subplot(2,2,2)
plt.imshow(pilotwings_red, cmap='gray', vmin=0, vmax=1)
plt.title('Red channel')
plt.axis('off')
plt.subplot(2,2,3)
plt.imshow(pilotwings_green, cmap='gray', vmin=0, vmax=1)
plt.title('Green channel')
plt.axis('off')
plt.subplot(2,2,4)
plt.imshow(pilotwings_blue, cmap='gray', vmin=0, vmax=1)
plt.title('Blue channel')
plt.axis('off')
plt.tight_layout()
We could average the three color channels together to get a more representative grayscale array:
pilotwings_gray = (pilotwings_red + pilotwings_green + pilotwings_blue) / 3
plt.imshow(pilotwings_gray, cmap='gray', vmin=0, vmax=1)
Note: we can compute this mean more immediately using the .mean
method:
pilotwings.shape
pilotwings.mean()
help(pilotwings.mean)
We can supply an optional argument axis=2
to compute means only along the color channel axis:
pilotwings_gray = pilotwings.mean(axis=2)
plt.imshow(pilotwings_gray, cmap='gray', vmin=0, vmax=1)
First, let's think about how to add salt/pepper noise to an image:
plt.imshow(hamburg, cmap='gray', vmin=0, vmax=1)
First, let's make a copy of our array that we can add noise to. This way, we don't mess up our original image and can make comparisons to the noisy and filtered images.
noisy_hamburg = hamburg_gray.copy()
print(noisy_hamburg.shape)
# Adding a "salt" pixel in the 595th row, 310th column
noisy_hamburg[595, 310] = 1
plt.imshow(noisy_hamburg[500:600, 300:400], cmap='gray', vmin=0, vmax=1)
We would like to be able to randomly introduce salt and pepper noise. We can use the np.random.random
function to help us:
help(np.random.random)
np.random.random((2,3))
Strategy: let's generate an array of the same shape as our image filled with randomly drawn values between 0
and 1
. If the randomly drawn value is greater than, say .9
, we'll add salt noise in that position.
num_rows, num_cols = hamburg_gray.shape
random_array = np.random.random((num_rows, num_cols))
for i in range(num_rows):
for j in range(num_cols):
# We're considering the pixel in the ith row, jth column
if random_array[i,j] > .9: # If the random value is greater than .9
noisy_hamburg[i,j] = 1 # add salt noise
plt.imshow(noisy_hamburg, cmap='gray', vmin=0, vmax=1)
What if we want to add pepper noise as well?
noisy_hamburg = hamburg_gray.copy()
num_rows, num_cols = hamburg_gray.shape
random_salt_array = np.random.random((num_rows, num_cols))
random_pepper_array = np.random.random((num_rows, num_cols))
for i in range(num_rows):
for j in range(num_cols):
# We're considering the pixel in the ith row, jth column
if random_salt_array[i,j] > .9: # If the random value is greater than .9
noisy_hamburg[i,j] = 1 # add salt noise
if random_pepper_array[i,j] > .9:# If the random value is greater than .9
noisy_hamburg[i,j] = 0 # add pepper noise
plt.imshow(noisy_hamburg, cmap='gray', vmin=0, vmax=1)
Note: the implementation above gives a preference to pepper noise, since it's possible for salt pixels to be overwritten as pepper (but not the reverse). Instead, let's use a single random array, but select select different subintervals between 0
and 1
to identify salt/pepper noise:
noisy_hamburg = hamburg_gray.copy()
num_rows, num_cols = hamburg_gray.shape
random_array = np.random.random((num_rows, num_cols))
for i in range(num_rows):
for j in range(num_cols):
# We're considering the pixel in the ith row, jth column
if random_array[i,j] < .1: # If the random value is less than .1
noisy_hamburg[i,j] = 0 # add pepper noise
if random_array[i,j] > .9: # If the random value is greater than .9
noisy_hamburg[i,j] = 1 # add salt noise
plt.imshow(noisy_hamburg, cmap='gray', vmin=0, vmax=1)
Exercise. Use the above sample code to write a function sp_noise(img, noise)
that adds salt and pepper noise to an image. Its first argument img should be a 2-dimensional numpy array representing the image and the second argument noise should be the fraction of pixels that are to be replaced by noise (for example, with noise = 0.05 about 5% of pixels should be noise, consisting in roughly equal parts of white and black pixels). The function should return a 2-dimensional numpy array representing the original image with noise added.
Note. In the example code above, we iterated through every row and column and decided for each entry whether or not to add salt and pepper noise. This is perfect case to use Boolean arrays:
noisy_hamburg = hamburg_gray.copy()
num_rows, num_cols = hamburg_gray.shape
random_array = np.random.random((num_rows, num_cols))
# Let's generate an Boolean array with True in the locations that we want
# to add salt noise and False everywhere else
salt_mask = (random_array > .9)
# Then, we can use `salt_mask` as a slice on the `noisy_hamburg` array:
noisy_hamburg[salt_mask] = 1
# Do the same thing for pepper noise:
pepper_mask = (random_array < .1)
noisy_hamburg[pepper_mask] = 0
plt.imshow(noisy_hamburg, cmap='gray', vmin=0, vmax=1)
Now that we have a noisy image, let's try to apply the mean filter.
3
by 3
grid centered centered at each pixel to compute the mean.To start, let's make a copy of our noisy array that we will then filter.
filtered_hamburg = noisy_hamburg.copy()
If we are considering the pixel in the i
th row and j
th column (i.e. noisy_hamburg[i,j]
, we want a 3
by 3
slice centered on the [i,j]
th pixel. That is, we want a slice that includes the i-1
, i
, and i+1
rows and the j-1
, j
, and j+1
columns.
i = 20
j = 30
#noisy_hamburg[19:21 + 1, 29:31 + 1]
grid = noisy_hamburg[i-1:i+2, j-1:j+2]
mean = grid.mean()
filtered_hamburg[i,j] = mean
filtered_hamburg = noisy_hamburg.copy()
# For now, we need to skip the first row and last row
for i in range(1,num_rows-1):
# We also need to skip the first column and last column
for j in range(1, num_cols-1):
grid = noisy_hamburg[i-1:i+2, j-1:j+2]
mean = grid.mean()
filtered_hamburg[i,j] = mean
plt.imshow(filtered_hamburg,cmap='gray',vmin=0,vmax=1)
What about the median filter?
filtered_hamburg = noisy_hamburg.copy()
# For now, we need to skip the first row and last row
for i in range(1,num_rows-1):
# We also need to skip the first column and last column
for j in range(1, num_cols-1):
grid = noisy_hamburg[i-1:i+2, j-1:j+2]
median = np.median(grid)
filtered_hamburg[i,j] = median
plt.imshow(filtered_hamburg,cmap='gray',vmin=0,vmax=1)