213 - A18 - Matrix Transformations

29 days ago by Professor213

In this worksheet, the user will experiment with Linear Transformations and Computer Graphics

John Travis

Mississippi College

Matrix Transformations

If T:$R^n \rightarrow R^m$ is a Linear Transformations, then $T(x)=Ax$, for some unique matrix $A$.  Therefore, one can completely describe a linear transformation using a matrix.  For computer graphics, all screen images consist of a discrete collection of points.  A linear transformation on these points will cause the image to be distorted in some manner.  Some interesting transformations discussed in Linear Algebra include reflections, expansion and contraction, shears, projections and combinations of these.  In the experiment below, we will start with the blue "home plate" and see what happens when an input transformation acts upon that image.  You can enter whatever you want for the matrix $A$ but you might want to utilize one (or a multiplicative product) of the standard transformations from the text.  (Also, if you care to notice the coding, we only map corner points instead of all points since linear transformations map lines to lines.)

# This one shows specific matrix transformations in 2D...skew, rotation, etc. N=2 pretty_print(html('Notice what happens to the ideas of contraction, expansion, left and right when the scalar is less than 1')) @interact def _(transform=('Select:',list(['Identity', 'Reflect x-axis', 'Reflect y-axis', 'Rotate Origin', 'Skew Right','Skew Left', 'Skew Up', 'Skew Down','Horizontal Contraction','Horizontal Expansion','Vertical Contraction','Vertical Expansion','Rotation'])),scalar=slider(1/10,47/10,1/10,2), move_box =checkbox(default=False) ): A = identity_matrix(N) if transform=='Reflect x-axis': A = matrix([[1,0],[0,-1]]) show(A) if transform=='Reflect y-axis': A = matrix([[-1,0],[0,1]]) show(A) if transform=='Rotate Origin': A = matrix([[-1,0],[0,-1]]) show(A) if transform=='Skew Right': A = matrix([[1,scalar],[0,1]]) show(A) if transform=='Skew Left': A = matrix([[1,-scalar],[0,1]]) show(A) if transform=='Skew Up': A = matrix([[1,0],[scalar,1]]) show(A) if transform=='Skew Down': A = matrix([[1,0],[-scalar,1]]) show(A) if transform=='Horizontal Contraction': A = matrix([[1/scalar,0],[0,1]]) show(A) if transform=='Horizontal Expansion': A = matrix([[scalar,0],[0,1]]) show(A) if transform=='Vertical Contraction': A = matrix([[1,0],[0,1/scalar]]) show(A) if transform=='Vertical Expansion': A = matrix([[1,0],[0,scalar]]) show(A) if transform=='Rotation': angle = scalar # radians c = cos(angle) s = sin(angle) A = matrix([[c,-s],[s,c]]) show(A) StartBox = line2d([(0,0),(0,1)],thickness=6)+line2d([(0,1),(1,2)],thickness=6)+line2d([(1,2),(2,1)],thickness=6)+line2d([(2,1),(2,0)],thickness=6)+line2d([(2,0),(0,0)],thickness=6) BoxCorners = matrix([[0, 0, 1, 2, 2],[0, 1, 2, 1, 0]]) if move_box: StartBox = line2d([(1,1),(1,2)],thickness=6)+line2d([(1,2),(2,3)],thickness=6)+line2d([(2,3),(3,2)],thickness=6)+line2d([(3,2),(3,1)],thickness=6)+line2d([(3,1),(1,1)],thickness=6) BoxCorners = matrix([[1, 1, 2, 3, 3],[1, 2, 3, 2, 1]]) NewCorners = (A*BoxCorners).transpose() # switch result matrix to rows for easier extraction of A*B columns. EndBox = Graphics() for col in range(5): c1 = NewCorners[Mod(col,5)] c2 = NewCorners[Mod(col+1,5)] EndBox += line2d([c1,c2],thickness=5, color="red") # EndBox += line2d([c1,c2],thickness=5, rgbcolor=(0.5,1,0.5)) show(StartBox+EndBox,aspect_ratio=True) 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       
Traceback (click to the left of this block for traceback)
...
SyntaxError: invalid syntax
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_sage_input_7.py", line 10, in <module>
    exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("KSAgIAojICAgICAgICBFbmRCb3ggKz0gbGluZTJkKFtjMSxjMl0sdGhpY2tuZXNzPTUsIHJnYmNvbG9yPSgwLjUsMSwwLjUpKSAgIAogICAgICAgICAgIAogICAgc2hvdyhTdGFydEJveCtFbmRCb3gsYXNwZWN0X3JhdGlvPVRydWUp"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
  File "", line 1, in <module>
    
  File "/tmp/tmpUDT0A7/___code___.py", line 2
    )   
    ^
SyntaxError: invalid syntax

Now, experiment with some transformation matrices that you choose.  You can combine transformation but multiplying the simple transformations already discussed above.

Assignment:

  • Create a matrix transformations which
    • reflects the image across the y-axis
    • skews to the right
    • expands vertically
    • does all three at the same time
  • Put your results in Matrix format in new cells below the interact.  Use a comment (start the line with a '#') to briefly describe each matrix.
# Here is an example of how you can combine transformations using sage. A1 = matrix([[1, 2],[0, 1]]) A2 = matrix([[-1, 0],[0, 1]]) A3 = matrix([[1, 0],[0, 2]]) A = A3*A2*A1 show(A) 
       

                                
                            

                                

Or perhaps flip over x-axis, then rotate 60°, then skew left, and then finally expand horizontally.

A1 = matrix([[1, 0],[0, -1]]) A2 = matrix([[cos(pi/3), -sin(pi/3)],[sin(pi/3), cos(pi/3)]]) A3 = matrix([[1, -3],[0, 1]]) A4 = matrix([[2, 0],[0, 1]]) A = A4*A3*A2*A1 show(A) 
       

                                
                            

                                
# This one shows matrix transformations in 2D...skew, rotation, etc. N=2 @interact def _(A = identity_matrix(QQ, N),auto_update=False ): StartBox = line2d([(0,0),(0,1)],thickness=6)+line2d([(0,1),(1,2)],thickness=6)+line2d([(1,2),(2,1)],thickness=6)+line2d([(2,1),(2,0)],thickness=6)+line2d([(2,0),(0,0)],thickness=6) BoxCorners = matrix([[0, 0, 1, 2, 2],[0, 1, 2, 1, 0]]) NewCorners = (A*BoxCorners).transpose() # switch result matrix to rows for easier extraction of A*B columns. EndBox = Graphics() npts = 5 print(A) for col in range(5): c1 = NewCorners[Mod(col,5)] c2 = NewCorners[Mod(col+1,5)] EndBox += line2d([c1,c2],thickness=5, color="red") for col in range(npts): c1 = NewCorners[Mod(col,npts)] c2 = NewCorners[Mod(col+1,npts)] EndBox += line2d([c1,c2],thickness=5, rgbcolor=(0.5,1,0.5)) show(StartBox+EndBox,aspect_ratio=True) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Let's do the above but with a bunch of points!

N=2 pretty_print(html('Notice what happens to the ideas of contraction, expansion, left and right when the scalar is less than 1')) @interact def _(transform=('Select:',list(['Identity', 'Reflect x-axis', 'Reflect y-axis', 'Rotate Origin', 'Skew Right','Skew Left', 'Skew Up', 'Skew Down','Horizontal Contraction','Horizontal Expansion','Vertical Contraction','Vertical Expansion','Rotation'])),scalar=slider(1/10,47/10,1/10,2), move_box =checkbox(default=False) ): A = identity_matrix(N) if transform=='Reflect x-axis': A = matrix([[1,0],[0,-1]]) show(A) if transform=='Reflect y-axis': A = matrix([[-1,0],[0,1]]) show(A) if transform=='Rotate Origin': A = matrix([[-1,0],[0,-1]]) show(A) if transform=='Skew Right': A = matrix([[1,scalar],[0,1]]) show(A) if transform=='Skew Left': A = matrix([[1,-scalar],[0,1]]) show(A) if transform=='Skew Up': A = matrix([[1,0],[scalar,1]]) show(A) if transform=='Skew Down': A = matrix([[1,0],[-scalar,1]]) show(A) if transform=='Horizontal Contraction': A = matrix([[1/scalar,0],[0,1]]) show(A) if transform=='Horizontal Expansion': A = matrix([[scalar,0],[0,1]]) show(A) if transform=='Vertical Contraction': A = matrix([[1,0],[0,1/scalar]]) show(A) if transform=='Vertical Expansion': A = matrix([[1,0],[0,scalar]]) show(A) if transform=='Rotation': angle = scalar # radians c = cos(angle) s = sin(angle) A = matrix([[c,-s],[s,c]]) show(A) # fix this to graph correctly BoxCorners = matrix([[2.6, 2.8, 3, 3.2, 3.4, 3, 3, 2.9, 2.8, 2.7], [ 1, 1, 1, 1, 1, 0.8, 0.6, 0.5, 0.4, 0.5]]) StartPts = points(BoxCorners,color='red') NewCorners = (A*BoxCorners).transpose() # switch result matrix to rows for easier extraction of A*B columns. EndBox = points(NewCorners) show(StartPts+EndBox,aspect_ratio=True) 
       
Notice what happens to the ideas of contraction, expansion, left and right when the scalar is less than 1
Select: 
scalar 
move_box 

Click to the left again to hide and once more to show the dynamic interactive window

For the images above, only the corners were transformed since lines are mapped to lines under linear transformations.  This saves time rather than transforming each pixel dot for all the lines in the square.  For more general images, each pixel must be submitted to the transformation.  For the experiment below, we will start with a general image and apply the same inputs as above.

(The hope is that the student would be able to upload an image on their own to use here.)

Below is under construction.  Suggestions are welcome!

Can you figure out how to take this image in Sage and apply the various transformations...

import pylab, numpy # This one is still under construction...need to find a nice b/w picture for the Start Image. It should work fine # so long as the pixels are stored as data points in columns. # This one shows matrix transformations in 2D...skew, rotation, etc. # Read in a png image as a numpy array. A = pylab.imread(DATA + 'Head.jpg')[::-1,:] A_image = numpy.mean(A, 2) N=2 @interact def _(A = identity_matrix(QQ, N),auto_update=False ): StartImage = pts = number of pixels in the Image StartPoints = convert StartImage to a collection of pixels with x-coords in first row and y-coord in 2nd NewPoints= (A*StartPoints).transpose() # switch result matrix to rows for easier extraction of A*B columns. EndImage= Graphics() for col in range(4): c1 = NewPoints[Mod(col,pts)] c2 = NewPoints[Mod(col+1,pts)] EndBox += line2d([c1,c2],thickness=5, rgbcolor=(0.5,1,0.5)) show(StartImage+EndImage,aspect_ratio=True) 
       

Click to the left again to hide and once more to show the dynamic interactive window

# future expansion for linear transformations into 3D 
       
# This is a copy of the first cell but prepared to use in a webwork problem N=2 choices = ['Identity', 'Reflect x-axis', 'Reflect y-axis', 'Rotate Origin', 'Skew to the right if above and to the left if below','Skew to the left if above and to the right if below', 'Skew out vertically', 'Skew in vertically','Horizontal Contraction','Horizontal Expansion','Vertical Contraction','Vertical Expansion','Rotation'] @interact def _(transform1=('Select:',list(choices)),transform2=('Select:',list(choices)),transform3=('Select:',list(choices)),scalar=slider(0,32/10,1/10,0,label='Scalar Rotation') ): def getA(transform): A = identity_matrix(N) if transform=='Reflect x-axis': A = matrix([[1,0],[0,-1]]) if transform=='Reflect y-axis': A = matrix([[-1,0],[0,1]]) if transform=='Rotate Origin': A = matrix([[-1,0],[0,-1]]) if transform=='Skew to the right if above and to the left if below': A = matrix([[1,2],[0,1]]) if transform=='Skew to the left if above and to the right if below': A = matrix([[1,-2],[0,1]]) if transform=='Skew out vertically': A = matrix([[1,0],[2,1]]) if transform=='Skew in vertically': A = matrix([[1,0],[-2,1]]) if transform=='Horizontal Contraction': A = matrix([[1/2,0],[0,1]]) if transform=='Horizontal Expansion': A = matrix([[2,0],[0,1]]) if transform=='Vertical Contraction': A = matrix([[1,0],[0,1/2]]) if transform=='Vertical Expansion': A = matrix([[1,0],[0,2]]) if transform=='Rotation': c = cos(scalar) s = sin(scalar) A = matrix([[c,-s],[s,c]]) return A A1 = identity_matrix(N) A2 = identity_matrix(N) A3 = identity_matrix(N) A1 = getA(transform1) A2 = getA(transform2) A3 = getA(transform3) # Make the rotation always happen for now c = cos(scalar) s = sin(scalar) A4 = matrix([[c,-s],[s,c]]) BigA = A4*A3*A2*A1 StartBox = line2d([(0,0),(0,1)],thickness=10)+line2d([(0,1),(1,2)],thickness=10)+line2d([(1,2),(2,1)],thickness=10)+line2d([(2,1),(2,0)],thickness=10)+line2d([(2,0),(0,0)],thickness=10) BoxCorners = matrix([[0, 0, 1, 2, 2],[0, 1, 2, 1, 0]]) NewCorners = (BigA*BoxCorners).transpose() # switch result matrix to rows for easier extraction of A*B columns. EndBox = Graphics() for col in range(5): c1 = NewCorners[Mod(col,5)] c2 = NewCorners[Mod(col+1,5)] EndBox += line2d([c1,c2],thickness=5, rgbcolor=(0.5,1,0.5)) show(StartBox+EndBox,aspect_ratio=True) 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       
 
       
[(1, 0), (-2, 1)]
[(1, 0), (-2, 1)]