If you see this, something is wrong
First published on Saturday, Jan 11, 2025 and last modified on Sunday, May 10, 2026
Mathedu
shear, fast rotation
In that assessment, you will decompose the matrix of a rotation of angle in \( [0,\pi)\) into the product of three matrices of "shears", that act on just one axis at the time.
And at the end, you will be invited to paste and run the given Python script to vizualise two ways to rotate the famous ’lena’ image (in grayscale).
For the scope of that text, the rotations and shears are identified with their matrices in the canonical basis of the plane.
A horizontal shear is a matrix of the form \( S^H_{\alpha} =\begin{bmatrix}1&\alpha\\ 0&1 \end{bmatrix}\) , with its parameter \( \alpha\) being a given real number.
Consider the column vector \( X=\begin{bmatrix} x\\ y \end{bmatrix}\) , and prove that \( Y=S^H_{\alpha} X\) has the same ordinate as \( X\) .
Consider the horizontal shears \( S^H_{\alpha} =\begin{bmatrix}1&\alpha\\ 0&1 \end{bmatrix}\) of parameter \( \alpha\) , and \( S^H_{\alpha'} =\begin{bmatrix}1&\alpha'\\ 0&1 \end{bmatrix}\) of parameter \( \alpha'\) , and prove that \( S^H_{\alpha}S^H_{\alpha'}=S^H_{\alpha+\alpha'}\) .
A vertical shear is a matrix of the form \( S^V_{\beta} =\begin{bmatrix}1&0\\ \beta&1 \end{bmatrix}\) , with its parameter \( \beta\) being a given real number.
Consider the column vector \( X=\begin{bmatrix} x\\ y \end{bmatrix}\) , and prove that \( Y=S^V_{\beta} X\) has the same abscissa as \( X\) .
Consider the vertical shears \( S^V_{\beta} =\begin{bmatrix}1&0\\ \beta&1 \end{bmatrix}\) of parameter \( \beta\) , and \( S^V_{\beta} =\begin{bmatrix}1&0\\ \beta&1 \end{bmatrix}\) of parameter \( \beta'\) , and prove that \( S^V_{\beta}S^V_{\beta'}=S^V_{\beta+\beta'}\) .
Consider the horisontal shear \( S^H_{\alpha} =\begin{bmatrix}1&\alpha\\ 0&1 \end{bmatrix}\) of prarmeter \( \alpha\) , and the vertical shear \( S^V_{\beta} =\begin{bmatrix}1&0\\ \beta&1 \end{bmatrix}\) of parameter \( \beta\) .
Consider the product \( A=S^H_{\alpha}S^V_{\beta}S^H_{\alpha}\) .
Consider the following formulae.
Prove the following formulae (in that order).
Use the fact that \( R_{\theta}= \begin{bmatrix}\cos(\theta)&-\sin(\theta)\\ \sin(\theta)&\cos(\theta) \end{bmatrix}\) .
For an image, the rotation of \( \pi/2\) involves both row and column indexes together, making the parallelisation impossible.
As the horizontal (resp. vertical) shears act only on the row (resp. column) indexes, making it possible to parallelise the process.
So that it is interesting to decompose the rotation into shears, for the sake of fastness of the computation.
In the script below, the parallelisation is not performed of course. It is just a little Python script for the pleasure of the eyes.
In Python, a \( 512\times 512\) image is a \( 512\times 512\times 3\) tensor with \( 3\) layers.
For an image in colors, each layer is an RGB component of a color, red, green and blue.
For a grayscale image, as we have, only the layer \( 0\) conains information, and it is the level of gray of each pixel.
To import it, we need the function ’imread’ in the library ’imageo’.
Then we extract the layout ’0’ to obtain a \( 512\times 512\) matrix.
That matrix can then be visualized we the function ’imshow’ of the library ’matplotlib.pyplot’.
We shall rotate by \( \pi/2\) the image, both directely and with the help of 3 sucessive shears.
The matrix of the rotation of angle \( \pi/2\) is \( R=\begin{bmatrix} 0&-1\\ 1&0 \end{bmatrix}\) .
To rotate the image ’lena’, each vector ’array([i,j])’, with i and j calculated from the center of the image, must be multiplied to the right by the matrix \( R\) . Indeed, the indexes (i,j) are analog to the vectors of the canonical basis in the plane of the image.
As \( \tan(\pi/4)=1\) , the matrix of the horizontal shear is \( HS=\begin{bmatrix} 1&-1\\ 0&1 \end{bmatrix}\) .
To shear horizontally the image ’lena’, we must first set margins of \( 255\) pixels at each side of the image, to obtain a \( 1024\times 1024\) image. Then each vector ’array([i,j])’ with customed ranges must be multiplied to the right by the matrix \( HS\) . We obtain an image called ’lena1_sheared’.
And as \( sin(\pi/2)=1\) , the matrix of the vertical shear is \( VS=\begin{bmatrix} 1&0\\ 1&1 \end{bmatrix}\) .
To shear vertically the image ’lena1_sheared’, we must first set margins of \( 512\) pixels at each side of the image, to obtaine a \( 2048\times 2048\) image. Then each vector ’array([i,j])’ with customed ranges must be multiplied to the right by the matrix \( VS\) . After a crop to the size \( 1024\times 1024\) , we obtain an image called ’lena2_sheared’.
Then we shear horizontally the image ’lena2_sheared’ to obtain the image ’lena3_rotated’. To do that, we must first set margins of \( 512\) pixels at each side of the image, to obtain a \( 2048\times 2048\) image. Then each vector ’array([i,j])’ with customed ranges must be multiplied to the right by the matrix \( HS\) . After a crop to the size \( 512\times 512\) , we obtain an image called ’lena3_rotated’.
You are invited to copy that script directly in a .py file, that you will store in the same directory as the image ’lena_gray.gif’ below.
Then, run and enjoy!
import imageio as iio
from matplotlib.pyplot import *
from numpy import *
# Read 'lena_gray.gif'
lena0=iio.imread('lena_gray.gif')
# Extract the first layer of the image, in gray
lena=lena0[:,:,0]
# Visualize in figure 1 the image as a matrix 512x512 pixels
imshow(lena,cmap='gray')
# Direct rotation of pi/2
R=array([[0,-1],[1,0]])
lena_rotated=zeros([512,512])
for i in arange(-255,256):
for j in arange(-255,256):
V=array([i,j])@R
lena_rotated[256+i,256+j]=lena[256+V[0],256+V[1]]
# Visualize in figure 2 the rotated image as a 512x512 matrix
figure()
imshow(lena_rotated,cmap='gray')
# margins of 256 pixels at each side
lena1=zeros([1024,1024])
lena1[256:768,256:768]=lena
# First horizontal shear
HS=array([[1,-1],[0,1]])
lena1_sheared=zeros([1024,1024])
for i in arange(-255,255):
for j in arange(-255+i,255+i):
U=array([i,j])@HS
lena1_sheared[512+i,512+j]=lena1[512+U[0],512+U[1]]
# Visualize in figure 3 the sheared image as a 1024x1024 marix
figure()
imshow(lena1_sheared,cmap='gray')
# margins of 512 pixels at each side
lena2=zeros([2048,2048])
lena2[512:1536,512:1536]=lena1_sheared
# Vertical shear
VS=array([[1,0],[1,1]])
lena2_sheared=zeros([2048,2048])
for j in arange(-511,511):
for i in arange(-255-j,255-j):
W=array([i,j])@VS
lena2_sheared[1024+i,1024+j]=lena2[1024+W[0],1024+W[1]]
# crop to 1024 over 1024 pixels
lena2_sheared=lena2_sheared[512:1536,512:1536]
# Visualize in figure 4 the double sheared image as a 1024x1024 marix
figure()
imshow(lena2_sheared,cmap='gray')
# margins of 512 pixels at each side
lena3=zeros([2048,2048])
lena3[512:1536,512:1536]=lena2_sheared
# Second horiaontal shear
lena3=zeros([2048,2048])
lena3[512:1536,512:1536]=lena2_sheared
lena3_rotated=zeros([2048,2048])
for i in arange(-511,511):
for j in arange(-511+i,511+i):
U=array([i,j])@HS
lena3_rotated[1024+i,1024+j]=lena3[1024+U[0],1024+U[1]]
# Crop to 512 over 512 pixels
lena3_rotated=lena3_rotated[768:1280,768:1280]
# Visualize in figure 5 the rotated image obtained by 3 shears as a 512x512
matrix
figure()
imshow(lena3_rotated,cmap='gray')The decomposition of a rotation of \( \frac{\pi}{2}\) into three shears allow fast rotation of an image.
We discovered how to do it theoretically and how to implement it in Python, with the famous ’Lena’ image.