Starting from:

$30

CECS 229 Programming Assignment #5

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

More products