0%

There is no Royal Road to Geometry

- Euclid

Recently, I was doing some ROS development and TF2 is a ROS package for tracking coordinate frames over time and affine transformation is essentail for understanding how it works.

What is affine transformation

In geometry, an affine transformation can be represented by acting a linear transformation and a translation on a vector.

Examples of affine transformation:
Transformation name Affine matrix Example
identity [100010001]{\displaystyle {\begin{bmatrix}1&0&0\\0&1&0\\0&0&1\end{bmatrix}}} translation
rotation [cos(θ)sin(θ)0sin(θ)cos(θ)0001]{\displaystyle {\begin{bmatrix}\cos(\theta )&-\sin(\theta )&0\\\sin(\theta )&\cos(\theta )&0\\0&0&1\end{bmatrix}}} rotation
scaling [cx=2000cy=10001]{\displaystyle {\begin{bmatrix}c_{x}=2&0&0\\0&c_{y}=1&0\\0&0&1\end{bmatrix}}} rotation
shearing [1cx=0.50cy=010001]{\displaystyle {\begin{bmatrix}1&c_{x}=0.5&0\\c_{y}=0&1&0\\0&0&1\end{bmatrix}}} shearing
reflection [100010001]{\displaystyle {\begin{bmatrix}-1&0&0\\0&1&0\\0&0&1\end{bmatrix}}} reflection
translation [10vx>001vy=0001]{\displaystyle {\begin{bmatrix}1&0&v_{x}>0\\0&1&v_{y}=0\\0&0&1\end{bmatrix}}}

“Affine Transformation” Wikipedia, Wikimedia Foundation, 16 November 2021, https://en.wikipedia.org/wiki/Affine_transformation.

Representation

It can be represented with the following formula:

y~=Ax+b. {\displaystyle \mathbf {\tilde y} =A\mathbf {x} +\mathbf {b} .}

Which is a equivalent to the following matrix:

[y1]=[Ab0...01][x1] {\begin{bmatrix}y \\1 \end{bmatrix} = \left[\begin{array} {ccc|c} & A & & b \\ 0 & ... & 0 & 1 \end{array}\right] \begin{bmatrix}x \\1 \end{bmatrix}}

Example

Code snippet:

affine_transformation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# System module imports
from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse

# Load image
parser = argparse.ArgumentParser(description='Code for Affine Transformations tutorial.')
parser.add_argument('--input', help='Path to input image.', default='src/MarioNSMBUDeluxe.png')
args = parser.parse_args()

src = cv.imread(cv.samples.findFile(args.input))
if src is None:
print('Could not open or find the image:', args.input)
exit(0)


srcTri = np.array( [[0, 0], [src.shape[1] - 1, 0], [0, src.shape[0] - 1]] ).astype(np.float32)
dstTri = np.array( [[0, src.shape[1]*0.33], [src.shape[1]*0.85, src.shape[0]*0.25], [src.shape[1]*0.15, src.shape[0]*0.7]] ).astype(np.float32)

warp_mat = cv.getAffineTransform(srcTri, dstTri)
warp_dst = cv.warpAffine(src, warp_mat, (src.shape[1], src.shape[0]))
# Rotating the image after Warp
center = (warp_dst.shape[1]//2, warp_dst.shape[0]//2)
angle = -50
scale = 0.6
rot_mat = cv.getRotationMatrix2D( center, angle, scale )
warp_rotate_dst = cv.warpAffine(warp_dst, rot_mat, (warp_dst.shape[1], warp_dst.shape[0]))
cv.imshow('Source image', src)
cv.imshow('Warp', warp_dst)
cv.imshow('Warp + Rotate', warp_rotate_dst)
cv.waitKey()

Calculation Example
notes