Image Compression with SVD
Import the necessary libraries.
We use this image of a raccoon for our example.
We check the shape of the array representing the image.
Therefore the image is made up of \(750\times 1000\) pixels. Let's have a look at the image.
We'll work with the gray scale image for svd compression. To work with colour we would just to the same thing to each colour channel matrix, then recombine to create the final colour image.
Compute the SVD of the image:
Computing an approximation of the image using the first column of \(U\) and first row of \(V\) reproduces the most prominent feature of the image, the light area on top/left and the dark area on the bottom/right. The darkness of the fur around the eyes causes the extra darkness in the middle/right of the reconstruction. Each column of pixels in this image is a different weighting of the same values, \(\vec{u}_1\):
reconst_img = np.matrix(U[:, :1]) * np.diag(sigma[:1]) * np.matrix(V[:1, :])
fig = plt.figure(figsize=(3,3))
plt.imshow(reconst_img, cmap='gray');
Even with just the first five vectors, the shape of the face begins to appear.
for i in range(2, 6):
reconst_img = np.matrix(U[:, :i]) * np.diag(sigma[:i]) * np.matrix(V[:i, :])
fig = plt.figure(figsize=(3,3))
plt.imshow(reconst_img, cmap='gray')
title = "n = %s" % i
plt.title(title)
plt.show()
Let's use a few more of the singular values to approximate the image.
for i in range(5, 51, 10):
reconst_img = np.matrix(U[:, :i]) * np.diag(sigma[:i]) * np.matrix(V[:i, :])
fig = plt.figure(figsize=(3,3))
plt.imshow(reconst_img, cmap='gray')
title = "n = %s" % i
plt.title(title)
plt.show()
Using \(n=45\) of the singular values/vectors we have a really good reconstruction of the image.
In fact, looking at \(n=30\) this is a pretty good approximation.
n = 30
reconst_img = np.matrix(U[:, :n]) * np.diag(sigma[:n]) * np.matrix(V[:n, :])
fig = plt.figure(figsize=(3,3))
plt.imshow(reconst_img, cmap='gray')
title = "n = %s" % n
plt.title(title)
plt.show()
The original image is \(750\times 1000\) pixels. Therefore, it takes \(7,500,000\) numbers to store it. The svd approximation using n singular values/vectors takes \(n(750+1000)+n = 1751n\) numbers to store it. So when \(n = 30\), it requires \(\frac{401\cdot 40}{400,000}= 7\%\) as much space as the original data.