$30
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