CECS 229 Programming Assignment #5

Submission Instructions:

To receive credit for this assignment you must submit the to CodePost a file named pa5.py by the due date:

Objectives:

Define a matrix data structure with relevant matrix operations.

Understand the role of matrices in simple image processing applications.

Problem 1.

Implement a class Matrix that creates matrix objects with attributes

colsp -column space of the Matrix object, as a list of columns (also lists)

rowsp -row space of the Matrix object, as a list of rows (also lists)

The constructor takes a list of rows as an argument, and constructs the column space from this rowspace. If a list is not provided, the parameter defaults to an empty list.

You must implement the following methods in the Matrix class:

Setters

set_col(self, j, u) – changes the j-th column to be the list u. If u is not the same length as the existing columns, then the method raises a ValueError with the message Incompatible column length.

set_row(self,i, v) – changes the i-th row to be the list v. If v is not the same length as the existing rows, then method raises a ValueError with the message Incompatible row length.

set_entry(self,i, j, x) – changes the existing aij

entry in the matrix to x.

Getters

get_col(self, j) – returns the j-th column as a list.

get_row(self, i) – returns the i-th row as a list v.

get_entry(self, i, j) – returns the existing aij

entry in the matrix.

col_space(self) – returns the list of vectors that make up the column space of the matrix object

row_space(self) – returns the list of vectors that make up the row space of the matrix object

get_diag(self, k) – returns the k

-th diagonal of a matrix where k=0

returns the main diagonal, k>0

returns the diagonal beginning at a1(k+1)

, and k<0

returns the diagonal beginning at a(−k+1)1

. e.g. get_diag(1) for an n×n

matrix returns [ a12,a23,a34,…,a(n−1)n

]

__str__(self) – returns a formatted string representing the matrix entries as

a11a21⋮am1a12a22⋮am2……⋱…a1ma2m⋮amn

Overloaded operators

In addition to the methods above, the Matrix class must also overload the +, -, and * operators to support:

Matrix + Matrix addition

Matrix – Matrix subtraction

Matrix * scalar multiplication

Matrix * Matrix multiplication

Matrix * Vec multiplication

scalar * Matrix multiplication

# Importing the Vector class(created in the previous assignment) from the vector.py file

# from Vector import Vec

# Writing a Matrix class to create matrix objects with rows and columns with overloaded operators

class Matrix:

def __init__(self, rowsp):

self.rowsp = rowsp

self.colsp = self._construct_cols(rowsp)

# Constructing columns based on the number of row space

def _construct_cols(self, rowsp):

# Intializing column space as an empty list

colsp = []

# Setting Column number the same as the number of rows in the row space

cols_num = len(rowsp[0])

# Creating a list of empty lists for each column space

for _ in range(cols_num):

colsp.append([])

# Iterating through each row in the row space

for row in rowsp:

# Iterating through each entry in the row

for i, entry in enumerate(row):

# Appending the entry to the column space list

colsp[i].append(entry)

return colsp

def set_col(self, j, u):

”’

Changes the j-th column to be the list `u`. If `u` is not the same length

as the existing columns, then the method raises a `ValueError` with the

message `Incompatible column length.`

”’

# Conditional statement to check u = length of the existing columns

if len(u) != len(self.colsp[0]):

# Raising ValueError if condition is not met

raise ValueError(“Incompatible column length.”)

else: # Running the code below if the condition is met

# Updating the first column space with the new column

self.colsp[j – 1] = u # -> j-1 because the index starts from 0

# Iterating through the row space

for i in range(len(self.rowsp)):

# Updating the row space with the new column

self.rowsp[i][j – 1] = u[i]

def set_row(self, i, v):

”’

Changes the i-th row to be the list `v`. If `v` is not the same length

as the existing rows, then method raises a `ValueError` with the

message `Incompatible row length.`

”’

# Conditional statement to check v = length of the existing rows

if len(v) != len(self.rowsp[0]): # Starting from 0 because the index starts from 0

# Raising ValueError if condition is not met

raise ValueError(“Incompatible row length.”)

else: # Running the code below if the condition is met

# Updating the row space with the new row

self.rowsp[i – 1] = v

# Reconstructing the column space of the matrix

self.colsp = self._construct_cols(self.rowsp)

def set_entry(self, i, j, x):

”’

Changes the existing a_ij entry in the matrix to ‘x’.

”’

# Replacing the j-th entry in the row with x

self.rowsp[i – 1][j – 1] = x # -> i-1, j-1 because the index starts from 0

# Replacing the i-th entry in the column with x

self.colsp[j – 1][i – 1] = x # -> i-1, j-1 because the index starts from 0

# Returns the j-th column as a list

def get_col(self, j):

return self.colsp[j – 1] # -> j-1 because the index starts from 0

# Returns the i-th row as a list v

def get_row(self, i):

return self.rowsp[i – 1] # -> i-1 because the index starts from 0

# Returns the a_ij entry in the matrix

def get_entry(self, i, j):

return self.rowsp[i – 1][j – 1]

# Returns the list of vectors that make up the column space of the matrix object

def row_space(self):

return self.rowsp

# Returns the list of vectors that make up the row space of the matrix object

def col_space(self):

return self.colsp

def get_diag(self, k):

”’

Returns the k-th diagonal of the matrix as a list, where:

-> k = 0 returns the main diagonal

-> k > 0 returns the diagonal beginning at a_1(k+1)

-> k < 0 returns the diagonal beginning at a_-(k+1)1

”’

if k > 0:

# Storing diagonal entries beginning at a_1(k+1)

diag_3 = []

# Iterating through the row spaces from a_1(k+1)

for i in range(len(self.rowsp) – k):

# Appending the values based on the given kth value

diag_3.append(self.rowsp[i][i + k]) # -> 1(k+1) => (1)(k+1) => [i][i+k]

# Returning the diagonal values from a_1(k+1)

return diag_3

elif k < 0:

# Storing diagonal entries beginning at a_-(k+1)1

diag_2 = []

# Iterating through the row spaces from a_-(k+1)1

for i in range(len(self.rowsp) + k):

# Appending the values based on the given kth value

diag_2.append(self.rowsp[i – k][i]) # -> -(k+1)1 => (1-k)(1) => [i-k][i]

# Returning the diagonal values from a_-(k+1)1

return diag_2

else:

# Storing the main diagonal entries

main_diag = []

# Iterating through the row spaces

for i in range(len(self.rowsp)):

# Appending the values of the main diagonal

main_diag.append(self.rowsp[i][i])

# Returning the values of the main diagonal

return main_diag

def __add__(self, other):

if len(self.rowsp) != len(other.rowsp) or len(self.colsp) != len(other.colsp):

raise ValueError(“Incompatible matrix dimensions for addition.”)

else:

# Creating a list of empty lists for the sum of the matrices

matrix_sum = []

for i in range(len(self.rowsp)):

row_sum = []

for j in range(len(self.colsp)):

# Appending the sum of the corresponding entries to row sum

row_sum.append(self.rowsp[i][j] + other.rowsp[i][j])

# Appending the row sum to the matrix sum

matrix_sum.append(row_sum)

# Returning the matrix sum as a Matrix object

return Matrix(matrix_sum)

def __sub__(self, other):

if len(self.rowsp) != len(other.rowsp) or len(self.colsp) != len(other.colsp):

raise ValueError(“Incompatible matrix dimensions for subtraction.”)

else:

# Creating a list of empty lists for the difference of the matrices

matrix_diff = []

for i in range(len(self.rowsp)):

# Storing each reduced row in a list

row_diff = []

for j in range(len(self.colsp)):

# Appending the difference of the corresponding entries to row difference

row_diff.append(self.rowsp[i][j] – other.rowsp[i][j])

# Appending the row difference to the matrix difference

matrix_diff.append(row_diff)

# Returning the reduced matrix as a Matrix object

return Matrix(matrix_diff)

def __mul__(self, other):

if type(other) == float or type(other) == int:

# Storing the product of matrix-scalar multiplication

scalar_mult_matrix = []

# Iterating through each row in the row space

for final_matrix_row in self.rowsp:

# Storing the each scaled row in a list

scaled_row = []

# Going through each element in the row

for element in final_matrix_row:

# Mulitplying each element with the scalar value

scaled_element = element * other

# Appending the scaled product into the scaled row

scaled_row.append(scaled_element)

# Appending the scaled rows to the scaled matrix

scalar_mult_matrix.append(scaled_row)

# Returning a scaled matrix as a Matrix object

return Matrix(scalar_mult_matrix)

elif type(other) == Matrix: # If the other object is a matrix

# Checking if dimensions of the matrices match or not

if len(self.rowsp[0]) != len(other.rowsp):

raise ValueError(“Dimension mismatch for multiplication.”)

else: # Running the code below if the dimensions are fine

# Array to store the product of the matrices

product_of_matrices = []

# Iterating through the rows of the first matrix

for row in range(len(self.rowsp)):

# Storing the product of each row of the first matrix with the columns of the second matrix

final_matrix_row = []

# Iterating through the columns of the other matrix

for column in range(len(other.colsp)):

# Storing the sum of the product of each row

matrix_sum = 0

# Iterating through the first index of the other matrix’s column

for l in range(len(other.colsp[0])):

# Storing the multiplication of first matrix

self_matrix_mul = self.rowsp[row][l]

# Storing the multiplication of the second matrix

other_matrix_mul = other.rowsp[l][column]

# Adding the products of the two matrices’ rows

matrix_sum += self_matrix_mul * other_matrix_mul

# Appending the matrix sum to final matrix row

final_matrix_row.append(matrix_sum)

# Appending the final matrix rows to the final matrix product

product_of_matrices.append(final_matrix_row)

# Returning the product of matrices as a Matrix object

return Matrix(product_of_matrices)

elif type(other) == Vec.Vec: # If the other object is a vector

“””This didn’t work”””

matrix_vector_product = []

for final_matrix_row in self.rowsp:

mat_vec_product = 0

for row in range(len(final_matrix_row)):

mat_vec_product += final_matrix_row[row] * Vec.Vec(other)

matrix_vector_product.append(mat_vec_product)

return Vec.Vec(matrix_vector_product)

else:

print(“ERROR: Unsupported Type.”)

return

def __rmul__(self, other):

if type(other) == float or type(other) == int:

return self.__mul__(other)

else:

print(“ERROR: Unsupported Type.”)

return

# Returns a formatted string representing the matrix entries as

def __str__(self):

“””prints the rows and columns in matrix form “””

mat_str = “”

for row in self.rowsp:

mat_str += str(row) + “\n”

return mat_str

def __eq__(self, other):

“””overloads the == operator to return True if

two Matrix objects have the same row space and column space”””

return self.row_space() == other.row_space() and self.col_space() == other.col_space()

def __req__(self, other):

“””overloads the == operator to return True if

two Matrix objects have the same row space and column space”””

return self.row_space() == other.row_space() and self.col_space() == other.col_space()

”’

B = Matrix([ [1, 2, 3, 4], [0, 1, 2, 3], [-1, 0, 1, 2], [-2, -1, 2, 3]])

A = Matrix([ [2, 0], [0, 2], [0, 0], [0, 0]])

print(“Matrix A:”)

print(A)

print()

print(“Matrix B:”)

print(B)

”’

Matrix A:

[2, 0]

[0, 2]

[0, 0]

[0, 0]

Matrix B:

[1, 2, 3, 4]

[0, 1, 2, 3]

[-1, 0, 1, 2]

[-2, -1, 2, 3]

Tester Cell for get_diag()

“””All Works

B = Matrix([ [1, 2, 3, 4], [0, 1, 2, 3], [-1, 0, 1, 2], [-2, -1, 2, 3]])

print(“Matrix:”)

print(B)

print(“Main diagonal:”,B.get_diag(0))

print(“Expected: [1, 1, 1, 3]”)

print()

print(“Diagonal at k = -1:”, B.get_diag(-1))

print(“Expected: [0, 0, 2]”)

print()

print(“Diagonal at k = -2:”, B.get_diag(-2))

print(“Expected: [-1, -1]”)

print()

print(“Diagonal at k = -3:”, B.get_diag(-3))

print(“Expected: [-2]”)

print()

print(“Diagonal at k = 1:”, B.get_diag(1))

print(“Expected: [2, 2, 2]”)

print()

print(“Diagonal at k = 2:”, B.get_diag(2))

print(“Expected: [3, 3]”)

print()

print(“Diagonal at k = 3:”, B.get_diag(3))

print(“Expected: [4]”)

“””

Matrix:

[1, 2, 3, 4]

[0, 1, 2, 3]

[-1, 0, 1, 2]

[-2, -1, 2, 3]

Main diagonal: [1, 1, 1, 3]

Expected: [1, 1, 1, 3]

Diagonal at k = -1: [0, 0, 2]

Expected: [0, 0, 2]

Diagonal at k = -2: [-1, -1]

Expected: [-1, -1]

Diagonal at k = -3: [-2]

Expected: [-2]

Diagonal at k = 1: [2, 2, 2]

Expected: [2, 2, 2]

Diagonal at k = 2: [3, 3]

Expected: [3, 3]

Diagonal at k = 3: [4]

Expected: [4]

Tester Cell for

row_space()

col_space()

get_row()

get_col()

set_row()

set_col()

”’All Works

A = Matrix([[1, 2, 3], [4, 5, 6]])

print(“Original Row Space:”, A.row_space())

print(“Original Column Space:”, A.col_space())

print(“Original Matrix:”)

print(A)

print()

A.set_row(1, [10, 20, 30])

print(“Modification #1”)

print(“Row Space after modification:”, A.row_space())

print(“Column Space after modification:”, A.col_space())

print(“Modified Matrix:”)

print(A)

print()

A.set_col(2, [20, 50])

print(“Modification #2”)

print(“Row Space after modification:”, A.row_space())

print(“Column Space after modification:”, A.col_space())

print(“Modified Matrix:”)

print(A)

print()

A.set_row(2, [40, 50, 6])

print(“Modification #3”)

print(“Row Space after modification:”, A.row_space())

print(“Column Space after modification:”, A.col_space())

print(“Modified Matrix:”)

print(A)

print()

A.set_entry(2, 3, 60)

print(“Modification #4”)

print(“Row Space after modification:”, A.row_space())

print(“Column Space after modification:”, A.col_space())

print(“Modified Matrix:”)

print(A)

print()

print(“The 2nd row is:”, A.get_row(2))

print(“The 3rd column is:”, A.get_col(3))

print()

print(“Modification #5″)

A.set_row(2, [40, 50])

A.set_col(2, [30, 4, 1])

print(A)

”’

Original Row Space: [[1, 2, 3], [4, 5, 6]]

Original Column Space: [[1, 4], [2, 5], [3, 6]]

Original Matrix:

[1, 2, 3]

[4, 5, 6]

Modification #1

Row Space after modification: [[10, 20, 30], [4, 5, 6]]

Column Space after modification: [[1, 10], [20, 5], [3, 30]]

Modified Matrix:

[10, 20, 30]

[4, 5, 6]

Modification #2

Row Space after modification: [[10, 20, 30], [4, 50, 6]]

Column Space after modification: [[1, 10], [20, 50], [3, 30]]

Modified Matrix:

[10, 20, 30]

[4, 50, 6]

Modification #3

Row Space after modification: [[10, 20, 30], [40, 50, 6]]

Column Space after modification: [[1, 40], [50, 50], [3, 6]]

Modified Matrix:

[10, 20, 30]

[40, 50, 6]

Modification #4

Row Space after modification: [[10, 20, 30], [40, 50, 60]]

Column Space after modification: [[1, 40], [50, 50], [3, 60]]

Modified Matrix:

[10, 20, 30]

[40, 50, 60]

The 2nd row is: [40, 50, 60]

The 3rd column is: [3, 60]

Modification #5

—————————————————————————

ValueError Traceback (most recent call last)

Cell In[88], line 49

45 print()

48 print(“Modification #5”)

—> 49 A.set_row(2, [40, 50])

50 A.set_col(2, [30, 4, 1])

51 print(A)

Cell In[87], line 56, in Matrix.set_row(self, i, v)

53 # Conditional statement to check v = length of the existing rows

54 if len(v) != len(self.rowsp[0]): # Starting from 0 because the index starts from 0

55 # Raising ValueError if condition is not met

—> 56 raise ValueError(“Incompatible row length.”)

57 else: # Running the code below if the condition is met

58 # Updating the row space with the new row

59 self.rowsp[i – 1] = v

ValueError: Incompatible row length.

Expected Output

Original Row Space: [[1, 2, 3], [4, 5, 6]]

Original Column Space: [[1, 4], [2, 5], [3, 6]]

Original Matrix:

[1, 2, 3]

[4, 5, 6]

Modification #1

Row Space after modification: [[10, 20, 30], [4, 5, 6]]

Column Space after modification: [[10, 4], [20, 5], [30, 6]]

Modified Matrix: Set row 1 to [10, 20, 30]

[10, 20, 30]

[4, 5, 6]

Modification #2

Row Space after modification: [[10, 20, 30], [4, 50, 6]]

Column Space after modification: [[10, 4], [20, 50], [30, 6]]

Modified Matrix: Set col 2 to [20, 50]

[10, 20, 30]

[4, 50, 6]

Modification #3

Row Space after modification: [[10, 20, 30], [40, 50, 6]]

Column Space after modification: [[10, 40], [20, 50], [30, 6]]

Modified Matrix: Set row 2 to [40, 50, 6]

[10, 20, 30]

[40, 50, 6]

Modification #4

Row Space after modification: [[10, 20, 30], [40, 50, 60]]

Column Space after modification: [[10, 40], [20, 50], [30, 60]]

Modified Matrix: Set row 2 col 3 to 60

[10, 20, 30]

[40, 50, 60]

The 2nd row is: [40, 50, 60]

The 3rd column is: [30, 60]

Modification #5

—————————————————————————

ValueError Traceback (most recent call last)

~\AppData\Local\Temp/ipykernel_9756/1966277524.py in <module>

48 #——— MODIFICATION 5 —————#

49 print(“Modification #5”)

—> 50 A.setRow(2, [40, 50])

51 A.setCol(2, [30, 4, 1])

52 print(“Modified Matrix: Set row 2 to [40, 50] and col 2 to [30, 4, 1]”)

~\AppData\Local\Temp/ipykernel_9756/2205165582.py in setRow(self, i, v)

159 “””Sets the i-th row to be the list v”””

160 if len(v) != len(self.Rowsp[0]):

–> 161 raise ValueError(“ERROR: Incompatible row length.”)

162 else:

163 self.Rowsp[i-1] = v # Updating the row

ValueError: ERROR: Incompatible row length.`

Tester cell for +, -, *

“””———————————–TESTER CELL————————————————-

“TESTING OPERATOR + ”

A = Matrix([[1, 2],[3, 4],[5, 6]])

B = Matrix([[1, 2],[1, 2]])

C = Matrix([[10, 20],[30, 40],[50, 60]])

# P = A + B # dimension mismatch –> Check successful

Q = A + C

R = A – C

print(“Matrix A”)

print(A)

print()

print(“Matrix C”)

print(C)

print()

”’Works”’

print(“Matrix Q = A + C”)

print(Q)

print()

”’Works”’

print(“Matrix R = A – C”)

print(R)

print()

“TESTING OPERATOR * ”

# TESTING SCALAR-MATRIX MULTIPLICATION

T = -0.5 * B

print(“Matrix B”)

print(B)

print()

”’Works”’

print(“Matrix T = -0.5 * B”)

print(T)

print()

# TESTING MATRIX-MATRIX MULTIPLICATION

U = A * B

print(“Matrix U = A * B”)

print(U)

print()

# TESTING MATRIX-VECTOR MULTIPLICATION

x = Vec([0, 1]) # Vec object

b = A * x # b is a Vec data type

print(“Vector b = A * x”)

print(b)

“””

Matrix A

[1, 2]

[3, 4]

[5, 6]

Matrix C

[10, 20]

[30, 40]

[50, 60]

Matrix Q = A + C

[11, 22]

[33, 44]

[55, 66]

Matrix R = A – C

[-9, -18]

[-27, -36]

[-45, -54]

Matrix B

[1, 2]

[1, 2]

Matrix T = -0.5 * B

[-0.5, -1.0]

[-0.5, -1.0]

Matrix U = A * B

[3, 6]

[7, 14]

[11, 22]

—————————————————————————

TypeError Traceback (most recent call last)

Cell In[92], line 52

50 # TESTING MATRIX-VECTOR MULTIPLICATION

51 x = Vec([0, 1]) # Vec object

—> 52 b = A * x # b is a Vec data type

53 print(“Vector b = A * x”)

54 print(b)

Cell In[87], line 217, in Matrix.__mul__(self, other)

215 mat_vec_product = 0

216 for i in range(len(row)):

–> 217 mat_vec_product += row[i] * Vec(other)

218 matrix_vector_product.append(mat_vec_product)

219 return Vec(matrix_vector_product)

File c:\Users\User\OneDrive\Desktop\CECS Files\CECS 229 Files\Programming Assignment #5\Vector.py:129, in Vec.__rmul__(self, other)

127 vector_product = []

128 # Running a loop through all the elements in the vector

–> 129 for element in range(len(self.elements)):

130 # Multiplying the each elements in the vector to the scalar value

131 product_result = self.elements[element] * other

132 # Appending the product of the elements to the list

TypeError: object of type ‘Vec’ has no len()

⎡⎣⎢135246⎤⎦⎥[01]=⎡⎣⎢1⋅0+2⋅13⋅0+4⋅15⋅0+6⋅1⎤⎦⎥=⎡⎣⎢246⎤⎦⎥

Expected Output:

Matrix A

[1, 2]

[3, 4]

[5, 6]

Matrix C

[10, 20]

[30, 40]

[50, 60]

Matrix Q = A + C

[11, 22]

[33, 44]

[55, 66]

Matrix R = A – C

[-9, -18]

[-27, -36]

[-45, -54]

Matrix B

[1, 2]

[1, 2]

Matrix T = -0.5 * B

[-0.5, -1.0]

[-0.5, -1.0]

Matrix A:

[1, 2]

[3, 4]

[5, 6]

Row-space of A:

[[1, 2], [3, 4], [5, 6]]

Column-space of A:

[[1, 3, 5], [2, 4, 6]]

Matrix B:

[1, 2]

[1, 2]

Row-space of B:

[[1, 2], [1, 2]]

Column-space of B:

[[1, 1], [2, 2]]

Matrix U = A * B

[3, 6]

[7, 14]

[11, 22]

Vector b = A * x

[0, -2, -4]

Extra-Credit

Worth: 5% extra-credit applied to midterm

To Receive Credit You Must:

Submit your work on this Jupyter NB to the appropriate Dropbox folder by Sunday, 4/9 @11:59 PM

Submit your completed video to the appropriate Dropbox folder by Sunday, 4/9 @11:59 PM

Demo your work to me during OH in order to receive the extra-credit. The last day you may demo is Thursday, 4/20.

No partial credit. This is an all-or-nothing EC opportunity

Background:

One of my favorite bands is “Alt-J”. Take a look at the music video for their song, “Matilda” at https://www.youtube.com/watch?v=Q06wFUi5OM8. The faces you see morphing into one another are the faces of the four members who were in the band at the time. In this exercise you will explore how a simplified version of this “morphing effect” can be achieved. In our simplified morphing effect, we will fade one face into another.

First, keep in mind that a video is just a collection of several still images displayed with a speed fast enough to make the change from one image to another imperceptible to the human eye.

To make the discussion simpler, suppose the images are grayscale pictures. We can represent a grayscale picture with m×n

pixels as a matrix Pm×n

where each entry pij∈{0,1,…,255}

is the intensity value of the pixel at location (i,j)

, [The intensity values range from 0 (black) to 255 (white)]. We are able to prove that the set of matrices S={Pm×n|pij∈Z256}

is a vector space, under addition and scalar multiplication defined as below:

Let P,Q∈S

, and α∈R,0≤α≤1

Addition: P+Q=[aij] where aij={pij+qij255 if the sum is 255 or less otherwise

Scalar Multiplication: αP=[aij] where aij={αpij255 if the product is 255 or less otherwise

Hence, given two “image-matrices” P1,P2∈S

, we can form convex combinations of these two elements with the confidence that the resulting matrices will be in S

, and thus, still represent images; i.e., if α1,α2∈R

such that α1+α2=1

, then

α1P1+α2P2∈S

and represents a new image.

Think: what would the image corresponding to matrix P

look like if P=0.5P1+0.5P2

? Since the images P1

and P2

make an equal contribution to the intensity of each pixel in P

, we can expect the image to look like an equal mix of the two images. e.g. if the two images contain faces in more-or-less the same position, the resulting image should display a face that more-or-less looks like both faces.

What if P=0.85P1+0.15P2

? Then, since most of the intensity in each pixel of P

is being contributed by P1

, we can expect the resulting image P

to display something that looks more like the first image, P1

, vs the second image, P2

.

Task 1:

Download the png and image modules. The image module contains the methods

file2image() – Reads an image into a list of lists of pixel values (triples with values representing the three intensities in the RGB color channels). e.g. [[(1, 2, 3), (1, 2, 3), (1, 2, 3)],[(1, 2, 3), (1, 2, 3), (1, 2, 3)],[(1, 2, 3), (1, 2, 3), (1, 2, 3)]] would be representing an image with 3×3

pixels.

image2file() – Writes an image in list of lists format to a file.

Use the functions listed above to implement:

img2matrix(filename) – creates and returns a Matrix object with the image data returned by file2image() from the module image. The parameter filename is a string data type specifying the location of the image you wish to use. If the image is not grayscale, you must convert it to grayscale prior to creating the Matrix object. You can do so using the functions isgray() and color2gray(), also found in the image module.

matrix2img(image_matrix, path) – creates a png file out of a Matrix object. You may want to use the function image2file() from the image module.

“””

import image

import png

def image2matrix(filename):

takes a png file and returns a Matrix object of the pixels

INPUT: filename – the path and filename of the png file

OUTPUT: a Matrix object with dimensions m x n, assuming the png file has width = n and height = m,

# Reading the image file into a triplet list of pixel values

image_data = image.file2image(filename)

# Conditional statement to check if the image is grayscale

if not image.isgray(image_data):

image_data = image.color2gray(image_data)

return Matrix(image_data)

def matrix2image(img_matrix, path):

returns a png file created using the Matrix object, img_matrix

INPUT:

* img_matrix – a Matrix object where img_matrix[i][j] is the intensity of the (i,j) pixel

* path – the location and name under which to save the created png file

OUTPUT:

* a png file

image_data = []

for i in range(img_matrix.nrows()):

row = []

for j in range(img_matrix.ncols()):

pixel_value = int(img_matrix[i][j])

row.append(pixel_value)

image_data.append(row)

image.image2file(image_data, path)

“””

“””——————TESTER FOR FUNCTIONS png2graymatrix() AND graymatrix2png()————————-“””

# M = image2matrix(“img11.png”) # matrix for img11.png

# F = image2matrix(“img02.png”) # matrix for img02.png

# C = (0.40 * M) + (0.60 * F) # convex combo: first image contributes 40% of its intensity, second image contributes 60%

# matrix2image(C, “mixedfaces.png”) # converting the matrix to png named mixedfaces

Task 2

Download and extract the zip folder faces.zip. In it, you will find the images of 20 faces.

Use the functions you implemented in Task 1 to implement a function called mix(img1, img2) that generates a set of 101 images. These images must be the result of taking convex combinations of two given images. In particular, you should begin by combining the two images so that the 1st/101 picture looks completely like img1. Then, modify the scalars of the combination so that the mixed image is the sum of a percentage of the intensities for each image. For example, the 2nd/101 picture would be a mixture of 99% of the first image’s intensity mixed with 1% of the second image’s intensity. The 51th/100 picture will look like both images equally mixed together (50/50). The 76th picture will looks like 25% of the first image’s intensity mixed with 75% of the second image’s intensity, and the 101st/101 picture looks like img2 only.

Call your mix() function on two images of your choice found in the faces.zip folder. The resulting images should give the illusion that one face is morphing into the other.

“””def mix(img1, img2):

generates a set of 101 images that results from the convex mixing the given images

INPUT:

– img1: string of path + name of first image

– img2: string of path + name of second image

OUTPUT: None (images are saved to the path given to matrix2image())

image1_matrix = image2matrix(img1)

image2_matrix = image2matrix(img2)

for i in range(101):

percentage_1 = (100 – i) / 100

percentage_2 = i / 100

mixed_image_matrix = (Matrix(image1_matrix) * percentage_1) + (Matrix(image2_matrix) * percentage_2)

file_path = f”mixed_image/{i}.png”

matrix2image(mixed_image_matrix, file_path)

“””

Task 3

Use the function make_video() below to create a video out of the 101 images you generated in Task 2. You will need to have installed the package opencv in order for the function to work. I recommend that you complete this entire task in a separate IDE such as PyCharm, where it is easier to install packages.

# import cv2

# import os

“””def make_video(images, outvid=None, fps=5, size=None, is_color=True, format=”XVID”):

Create a video from a list of images.

@param outvid output video

@param images list of images to use in the video

@param fps frame per second

@param size size of each frame

@param is_color color

@param format see http://www.fourcc.org/codecs.php

@return see http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_gui/py_video_display/py_video_display.html

By default, the video will have the size of the first image.

It will resize every image to this size before adding them to the video.

fourcc = cv2.VideoWriter_fourcc(*format)

vid = None

for image in images:

if not os.path.exists(image):

raise FileNotFoundError(image)

img = cv2.imread(image)

if vid is None:

if size is None:

size = img.shape[1], img.shape[0]

vid = cv2.VideoWriter(outvid, fourcc, float(fps), size, is_color)

if size[0] != img.shape[1] and size[1] != img.shape[0]:

img = cv2.resize(img, size)

vid.write(img)

vid.release()

return vid

“””

Sample Usage:

img_path = “C:\\Users\\kapiv\\Documents\\CECS 229\\CA #5\\faces\\”

vid_path = “C:\\Users\\kapiv\\Documents\\CECS 229\\CA #5\\male_faces.avi”

images = [] # Initializing empty list of image paths

for i in range(15): # adding images male00.png – male14.png to the list

if i < 10:

file = f”{img_path}male0{i}.png”

else:

file = f”{img_path}male{i}.png”

print(“Adding:”, file)

images.append(file)

make_video(images, vid_path, format = “mp4v”) # creating video