Solutions

1 - Setup

In [2]:
pythonPic = plt.imread('./images/Teaching_Python.png') #64x64x3 array
plt.imshow(pythonPic)
plt.show()
dims = np.shape(pythonPic)

2 - Playing with Colours

In [3]:
# Remove colours
for i in range(3):
    pythonPicCopy = np.copy(pythonPic) #Create a copy to avoid binding issues with changing array values
    pythonPicCopy[:,:,i] = 0.0 #Set this colour to be zero (not present) at every pixel
    #Fancy titles
    titStr = colours[i] + ' set to 0'
    plt.title(titStr)
    plt.imshow(pythonPicCopy)
    plt.show()
# Now do it by quarters...
half_height = dims[0]//2
half_width = dims[1]//2
pythonPicCopy = np.copy(pythonPic)
# top-right, no red
pythonPicCopy[:half_height, half_width:, 0] = 0.0
# bottom-left, no green
pythonPicCopy[half_height:, :half_width, 1] = 0.0
# bottom-right, no blue
pythonPicCopy[half_height:, half_width:, 2] = 0.0
plt.title('Quartered Image')
plt.imshow(pythonPicCopy)
plt.show()

3 - (Optional) Colours are Fun

In [4]:
# Remove colours
for i in range(3):
    pythonPicCopy = np.copy(pythonPic) #Create a copy to avoid binding issues with changing array values
    pythonPicCopy[:,:,0:i] = 0.0 
    pythonPicCopy[:,:,i+1:3] = 0.0 #Set all other colours to be zero
    #Fancy titles
    titStr = colours[i] + ' only'
    plt.title(titStr)
    plt.imshow(pythonPicCopy)
    plt.show()
# Now do it by quarters...
pythonPicCopy = np.copy(pythonPic)
dims = pythonPicCopy.shape #This gives us the array dimensions of pythonPicCopy
# top-right, all red
pythonPicCopy[:half_height, half_width:, 1:3] = 0.0
# bottom-left, all green
pythonPicCopy[half_height:, :half_width, 0:3:2] = 0.0
# bottom-right, all blue
pythonPicCopy[half_height:, half_width:, 0:2] = 0.0
plt.title('Quartered Image')
plt.imshow(pythonPicCopy)
plt.show()

4 - The SVD and a Low-Rank Approximation

In [5]:
def LowRank(A, k):
    m, n = A.shape
    U, S, VT = spla.svd(A)
    Sk = np.zeros([m, n])
    Sk[np.diag_indices(k)] = S[0:k]
    Ak = U@Sk@VT
    return Ak
plt.title('Original Image')
plt.imshow(pythonPic)
plt.show()
# Let's reconstruct the image with a low rank approximation
# Low rank (Try changing)
for k in [2,5,7,10,20]:
    reconImage = np.zeros(dims, dtype='float') #We still have dims from before, which we can use
    for i in range(3):
        reconImage[:,:,i] = LowRank(pythonPic[:,:,i], k)
    plt.title('Low-Rank Approximation')
    plt.imshow(reconImage)
    plt.show()
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

5 - Image Noise Generation

In [7]:
def AddNoise(img, xi):
    #Add noise level xi to the image img. Clip values that fall outside acceptable range?
    dims = img.shape #get image dimensions
    noisyImg = img + nprd.normal(0, xi, dims) #add noise to every pixel; mean 0, st.dev xi.
    return noisyImg

6 - Noise Reduction

In [8]:
xi = 0.1
fuzzyPic = AddNoise(monoPic, xi)
PlotGreyscale(fuzzyPic, 'fuzzyPic')
kVals = np.arange(5,30,5)
errNorms = []
#Use LowRank from before
for k in kVals:
    resolvedPic = LowRank(fuzzyPic, k)
    PlotGreyscale(resolvedPic, 'resolvedPic, k:'+str(k))
    errNorms.append(spla.norm(resolvedPic - monoPic, 2))
plt.plot(kVals, errNorms, 'x-', linewidth=2, markersize=20)
plt.title('Error Plot')
plt.xlabel('Number of singular values')
plt.ylabel('Error in $L^2$ norm')
plt.show()