Geometric Transformations using OpenCV (python)
Geometric transformations are a common image processing technique that have numerous applications, such as rescaling images for different screen sizes, aligning features on different images, augmenting data for machine learning, animating sprites programmatically, etc..

Remember that images are represented as matrices, oftentimes resources on the internet, software documentation, and other sources will refer to the dimensions of the image using different terms. It's important to keep the following in mind:
- x = width = number of columns
- y = height = number of rows
Translation
Translation is the process of moving an image from its original location by shifting it in x and y directions. To carry out this operation, you need to specify how much movement in the x and y direction is required (lets call this tx, ty).
A transformation matrix will then need to be created. Given tx and ty, a transformation matrix M can be defined as:

Lets define a python function for this transformation:
def ImageTranslation(image, xOffset, yOffset):
# Translate the image by x and y offset
# image - input image as a matrix
# xOffset - offset in x direction
# yOffset - offset in y direction
# define the transformation matrix m
M = np.float32([[1,0,xOffset],[0,1,yOffset]])
# preform the image Translation and return the results
return cv2.warpAffine(image,M,(image.shape[1], image.shape[0]))
# Translate the image by 200 pixels in the x directrion and 100 in the y direction
translated_im = ImageTranslation(original_im,200,100)
# Show the results
DisplayImageComparison(original_im, translated_im,"Translated image")
Scaling
Scaling is the process of resizing the image, that is, changing the width and height of the image. This process preserves the orientation of the image. The new size of the image can be defined in 2 ways, either by specifying a scaling factor or by specifying the width and height of the desired image.
When scaling images, the concept of interpolation is essential. this is because when resizing an image, the algorithm will need to determine how to fill in missing values (upscaling) or which values to discard (downscaling) in order to produce a more natural-looking result. If you want to find out more about the various options available, check out this story here on image interpolation in OpenCV.
Lets define a python function that scales the images based on a scaling factor:
def ScaleImageByRatio(image, xScale, yScale, interpolationMethod = cv2.INTER_LINEAR):
# Scale the image using a scaling factor
# image - input image as a matrix
# xScale - scale factor for the x axis (width)
# yScale - scale factor for the y axis (height)
# interpolationMethod - interpolation method used
return cv2.resize(image,None,fx=xScale, fy=yScale, interpolation = interpolationMethod)
# Scale the image 1.5 times on the x axis, and .5 times on the y axis
scaled_im = ScaleImageByRatio(original_im,1.5,.5)
# Show the results
DisplayImageComparison(original_im, scaled_im,"Scaled image")
Lets define a python function that scales the images based the desired dimensions:
def ScaleImageToSize(image, newWidth, newHeight, interpolationMethod = cv2.INTER_LINEAR):
# Scale the image to a given size
# image - input image as a matrix
# newWidth - new Width for the imamge (x axis)
# newHeight - new height for the image (y axis)
# interpolationMethod - interpolation method used
return cv2.resize(image,(newWidth, newHeight), interpolation = interpolationMethod)
# Scale the to 200 (width) x 400 (height)
scaled_im = ScaleImageToSize(original_im,200,400)
# Show the results
DisplayImageComparison(original_im, scaled_im,"Scaled image")
Rotation
Rotate the image given an angle. The image can be rotated on the midpoint by defining the following transformation matrix (where Θ is the angle of rotation):

OpenCV also allows us to easily preform a similarity transform, we will take advantage of this in the implimentation of the function. In a similiraty transform a midpoint for the rotation can be defined (i.e. the rotation isn’t always from the centre of the image), as well as a way to scale the image in the rotation process. This can be done by setting up the following transformation matrix:

where:

Lets define a python function that preforms a similarity transform (rotate, translates [default is midpoint], scales [default is no scaling]) on the iamge:
def RotateImage(image, angleOfRotation, midPoint = None, scale = 1 ):
# Preform a similarity transform on the image
# image - input image as a matrix
# angleOfRotation - angel of rotation for the output
# midpoint for the rotation as a (x,y) tuple, defult is centre of image
# scaling factor for the output
rows,cols = image.shape[:2]
if not midPoint:
midPoint = (cols/2, rows/2)
M = cv2.getRotationMatrix2D(midPoint,angleOfRotation,scale)
return cv2.warpAffine(image,M,(cols,rows))
# Rotate the image 20 degrees at the midpoint
rotated_im = ImageTranslation(RotateImage(original_im,20),10,20)
# Show the results
DisplayImageComparison(original_im, rotated_im,"Rotated image (midpoint)")
# Rotate the image 30 degrees at the pixel 10 (x), 20 (y) and scale the image to .8
rotated_im = RotateImage(original_im,30,(10,20),.5)
# Show the results
DisplayImageComparison(original_im, rotated_im,"Rotated and scaled image (10 (x), 20 (y))")
Affine Transformation
Affine transformations preserve the parallelism of lines in an image. To apply a transformation, we need three points from the input image and their corresponding locations in the output image.
In this transform parallelism is preserved, however image rotation and scale is altered to get to the desired output
def AffineTransform(image,orignPoints, destPoints):
# Preform a affine transform on the image
# image - input image as a matrix
# orignPoints - list of list containing original 3 points for the transformation i.e. [[x1,y1],[x2,y2],[x3,y3]]
# destPoints - list of list containing destination of the 3 points original points i.e. [[x1,y1],[x2,y2],[x3,y3]]
rows,cols = image.shape[:2]
origin = np.float32(orignPoints)
dest = np.float32(destPoints)
M = cv2.getAffineTransform(origin,dest)
return cv2.warpAffine(image,M,(cols,rows))
# Perform an affine transform where the 0th pixel from the top and left are shifted by 5
affine_im = AffineTransform(original_im,[[0,0],[0,10],[10,0]],[[0,0],[5,10],[10,5]])
# Show the results
DisplayImageComparison(original_im, affine_im,"Affine transform")
Perspective Transformation
To perform perspective transformations, you need a 3x3 transformation matrix. Perspective transformations preserve straight lines — that is, they remain unchanged once the transformation has been made. To find the transformation matrix for this image, you need to know the coordinates of four points on the input image and their corresponding points on the output image. Among these 4 points, 3 of them should not be collinear.
def PerspectiveTransform(image,orignPoints, destPoints):
# Preform a perspective transform on the image
# image - input image as a matrix
# orignPoints - list of list containing original 4 points for the transformation i.e. [[x1,y1],[x2,y2],[x3,y3]]
# destPoints - list of list containing destination of the 4 points original points i.e. [[x1,y1],[x2,y2],[x3,y3]]
rows,cols = image.shape[:2]
origin = np.float32(orignPoints)
dest = np.float32(destPoints)
M = cv2.getPerspectiveTransform(origin,dest)
return cv2.warpPerspective(image,M,(cols,rows))
# Perform a perspective transform where the right side of the image is sticky (points 500,0 and 500,500)
# The left side has been pinched in the y axis by 200 px.
perspective_im = PerspectiveTransform(original_im,[[500,0],[500,500],[0,0],[0,500]],[[500,0],[500,500],[0,100],[0,400]])
# Show the results
DisplayImageComparison(original_im, perspective_im,"Perspective transform")
If you would like to get a copy of the code used in this article, it can be found here on Github.






