+1 (315) 557-6473 

Python Program to Implement Image Compression Assignment Solution.


Instructions

Objective
To complete a Python assignment, you are required to write a program to implement image compression in Python. This task involves creating a program that effectively reduces the size of an image while preserving its essential visual information. Image compression is a crucial technique in various applications, including digital storage and transmission. By efficiently encoding the image data, you can significantly reduce the storage space required and improve the speed of data transfer. In your Python assignment, you will explore different image compression algorithms and techniques, such as the use of mathematical transformations and quantization processes. This project will not only enhance your programming skills but also deepen your understanding of image processing concepts.

Requirements and Specifications

program to implement image compression in python

Source Code

!pip install otter-grader

# Initialize Otter

import otter

grader = otter.Notebook("lab6.ipynb")

# Lab 6: Image Compression and Matrix Factorization

Matrix factorization is a way to find a set of basis vectors that describe a given dataset. Depending on the factorization used, the set of bases are different.

In this notebook, we use singular value decomposition (SVD) and nonnegative matrix factorization (NMF)

## Faces dataset

We use "[labeled faces in the wild](http://vis-www.cs.umass.edu/lfw/)" dataset.

%matplotlib inline

import matplotlib.pyplot as plt

import numpy as np

from sklearn.datasets import fetch_lfw_people

lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)

img_count, img_height, img_width = lfw_people.images.shape

print('number of faces in dataset:', img_count)

print('image width in pixels :', img_width)

print('image height in pixels :', img_height)

Each face is vectorized into a row in data matrix `X`

## Question 1a: Data transformation

Inspecting `lfw_people.images.shape` shows images are stored as a 3-dimensional array of size (1288, 50, 37). Use `numpy.ndarray.reshape()` to tranform matrix `lfw_people.images` to a 2-dimensional array of size `image_count` by `image_width` * `image_height` with name `X`. Take first image, `X[0]`, and `numpy.ndarray.reshape()` it back to a 2-dimensional array of size `image_width` * `image_height` with name `X0_img`.

X = lfw_people.images.reshape((img_count, img_width*img_height))

X0_img = X[0].reshape((img_height, img_width))

grader.check("q1a")

If everything went correctly, you should see a gray scale image below

# each row of X is a vectorized image

plt.imshow(X0_img, cmap=plt.cm.gray);

## Question 1b: Visualization

To make plotting easier, create a plotting function.

def draw_img(img_vector, h=img_height, w=img_width):

"""

1. takes img_vector,

2. reshapes into right dimensions,

3. draws the resulting image

"""

plt.imshow( img_vector.reshape((img_height, img_width)), cmap=plt.cm.gray)

plt.xticks(())

plt.yticks(())

_Cell Intentionally Blank_

# check draw_img function

draw_img(X[49])

## Question 1c: Standardization

Since SVD looks for singular values (related to eigenvalues) and eigenvectors of `X`, center each column (pixel) of `X` so that mean of each column is zero. Otherwise the result can be strange. This is a detail that is not critical for understanding the conceptual aspect of SVD. The variance of each pixel can be left alone. Use [`sklearn.preprocessing.StandardScaler`](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html) to standardize `X` (hint: use `.fit_transform`).

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler(with_std=False)

Xstd = scaler.fit_transform(X)

grader.check("q1c")

The mean centered images look unnatural, but all the pertinent data is retained

# standardization transforms image data

draw_img(Xstd[49])

### Inverse Transformation

We can recover the original data by putting the means back

# inverse tranformation recovers original image units

Xorig = scaler.inverse_transform(Xstd)

draw_img(Xorig[49])

## Question 2a: Singular Value Decomposition (SVD)

Numpy package has SVD decomposition function [`numpy.linalg.svd`](https://numpy.org/doc/stable/reference/generated/numpy.linalg.svd.html). Decompose `Xstd` into `U`, `S`, `VT`, i.e., $X_{std} = U S V^T$.

from numpy.linalg import svd

U, S, VT = np.linalg.svd(Xstd)

grader.check("q2a")

## Question 2b: Matrix Factors from SVD

Since `S` is a diagonal matrix, multiplying `U` by `S` is like scaling each column of `U` by the corresponding diagonal entry. Check that we can recover the original data from the matrix factors by inverse transforming the resulting `Xhat`

# Compute real S

Strue = np.zeros((U.shape[1], VT.shape[0]))

Strue[:S.size, :S.size] = np.diag(S)

US = U.dot(Strue)

# reconstruct standardized images from matrix factors

Xhat = US.dot(VT)

# inverse transform Xhat to remove standardization

Xhat_orig = scaler.inverse_transform(Xhat).astype('float32')

draw_img(Xhat_orig[49])

grader.check("q2b")

### Dimensionality reduction

We can describe each face using smaller portions of matrix factors. Because of how `US` and `VT` are ordered, the first portions of `US` and `VT` retain the most information. So, to keep the most relevant parts, keep the first columns of `US` and first rows of `VT`. Below illustrate why this is called a dimensionality reduction method.

In the following, we keep 500 columns of `US` and 500 rows of `VT` out of 1288.

# reconstruct Xhat with less information: i.e. dimensionality is reduced

Xhat_500 = US[:, 0:500] @ VT[0:500, :]

# inverse transforms Xhat to remove standardization

Xhat_500_orig = scaler.inverse_transform(Xhat_500)

# draw recovered image

draw_img(Xhat_500_orig[49])

Using even smaller number degrades the reconstruction; however, still the reconstructed image captures the "gist" of the original data.

# reconstruct Xhat with less information: i.e. dimensionality is reduced

Xhat_100 = US[:, 0:100] @ VT[0:100, :]

# inverse transforms Xhat to remove standardization

Xhat_100_orig = scaler.inverse_transform(Xhat_100)

# draw recovered image

draw_img(Xhat_100_orig[49])

To make the dimensionality reduction and inverse transform easier, write a function:

def dim_reduce(US_, VT_, dim=100):

Xhat_ = US_[:, 0:dim] @ VT_[0:dim, :]

return scaler.inverse_transform(Xhat_)

We can see how the increasing the rows and columns of matrix factors used increases fidelity of reconstructions

dim_vec = [50, 100, 200, 400, 800]

plt.figure(figsize=(1.8 * len(dim_vec), 2.4))

for i, d in enumerate(dim_vec):

plt.subplot(1, len(dim_vec), i + 1)

draw_img(dim_reduce(US, VT, d)[49])

### Matrix Factors and "Eigenfaces"

What are in these matrix factors `US` and `VT`?

`VT` contains the set of "basis" vectors. In this setting rows of `VT` are called eigenfaces

# each row of VT is an "eigenface"

draw_img(VT[0])

Plotting more eigenfaces show how the information in each eigenfaces are highlighting different components of photos

num_faces = 10

# each row of VT is an "eigenface"

plt.figure(figsize=(1.8 * 5, 2.4 * 2))

for i in range(0, 10):

one_face = VT[i]

plt.subplot(2, 5, i + 1)

draw_img(one_face)

### Each face is a linear combination of eigenfaces

Reconstructing a face can be thought of as combining these eigenfaces according to some row of `US` (coefficients).

# each face is a linear combination of eigenfaces VT

face_num = 3 # which face to reconstruct?

dim = 300 # higher dim is more accurate fit

draw_img(scaler.inverse_transform(US[face_num, 0:dim].reshape((1,-1)) @ VT[:dim,:]))

## Question 3: Nonnegative Matrix Factorization

NMF is a matrix factorization method that require nonnegative data matrix. Images are represented as light intensities between 0 and 255: i.e. nonnegative numbers.

NMF decomposes `X` into `W` and `H` such that $X \approx WH$. NMF is slower than SVD. So, we only choose a very small number of basis here: 200. Obtain `W` and `H`. Use [`sklearn.decomposition.NMF`](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.NMF.html).

from sklearn.decomposition import NMF

model = NMF(n_components=200, init='nndsvd', random_state=0)

W = model.fit_transform(X)

H = model.components_

grader.check("q3")

### Matrix factor H

NMF matrix factor `H` contain the set of basis faces.

draw_img(H[0])

Following shows that the set of basis vectors are very different than what SVD chose

num_faces = 20

plt.figure(figsize=(1.8 * 5, 2.4 * 4))

for i in range(0, num_faces):

one_face = VT[i]

plt.subplot(4, 5, i + 1)

draw_img(H[i])

However, each face is still a linear combination of matrix `H`

draw_img(W[30]@H) # using 200 NMF basis vectors

draw_img(dim_reduce(US, VT, 200)[30]) # using 200 SVD basis vectors

draw_img(X[30]) # original image

---

To double-check your work, the cell below will rerun all of the autograder tests.

grader.check_all()

## Submission

Make sure you have run all cells in your notebook in order before running the cell below, so that all images/graphs appear in the output. The cell below will generate a zip file for you to submit. **Please save before exporting!**

# Save your notebook first, then run this cell to export your submission.

grader.export()