Affine Transforms

Six numbers rotate, scale, shear and shift an image. Edit them directly and watch what each one controls.

Overview

One matrix for four operations

Rotating, scaling, shearing and translating look like four separate things. They're one thing with different numbers in it — an affine transform, written as a 2×3 mat:

| a  b  e |
| c  d  f |

A point at (x, y) moves to (ax + by + e, cx + dy + f). The four numbers a, b, c and d handle all that involves direction — rotation, scale, shear, flip — and e and f slide the result sideways and down.

The defining property is in the name: affine transforms preserve straightness and parallelism. A straight line stays straight, and two parallel lines stay parallel. any of it you can do to a photo by moving, turning, stretching or skewing it's affine. any of it involving perspective — railway tracks settling — isn't, because parallel lines stop being parallel.

Affine Transforms

This module needs JavaScript: the images are computed in the page rather than downloaded.

Worth knowing

An affine transform maps straight lines to straight lines and keeps parallel lines parallel.
The four numbers a, b, c, d do the rotating, scaling and shearing; e and f do the translating.
The determinant ad − bc is how much area scales by. At zero the transform collapses the image to a line.
Implementations iterate over the output and look backwards, because iterating forwards leaves holes.

Affine Transforms

Six numbers that cover each rotation, scale, shear and shift an image can take.

What each number does

Set all to the identity. A and d at 1, the rest at 0 — and the image is unchanged. Then move one at a time.

a scales flatly. At 2 the image doubles in width. At 0.5 it halves; at −1 it mirrors left-to-right.

d scales vertically, the same way.

c shears in x. Each row slides sideways in proportion to its distance from the centre, turning a rectangle into a parallelogram. This is the italic change.

b shears in y, tilting columns instead of rows.

e and f translate, in pixels, and are the only two that don't interact with the others.

Rotation isn't its own control because it doesn't need one. A rotation by θ is:

a =  cos t    b = -sin t
c =  sin t    d =  cos t

Set a and d to 0.87 and c to 0.5 with b at −0.5 and the image turns by thirty degrees. Rotation is a given combination of scale and shear, which is the sort of fact the mat makes obvious and four separate functions hide.

The determinant

The readout under the visualisation shows ad − bc, the determinant. It has a direct meaning: it's the factor by which area is times'd.

A pure rotation has determinant 1, because rotating doesn't change area. Doubling both scales gives 4. A minus determinant means the image has been flipped — orientation reversed.

At determinant 0 the transform isn't invertible, and geometrically it has merged the plane onto a line. Set a and c to the same value with b and d matching and the image collapses. Nothing can undo that, because the info about spot along the lost direction is gone.

Why implementations work backwards

The obvious way to apply a transform is to loop over input pixels, find where each lands, and write it there. This makes a broken image.

When the transform enlarges, adjacent input pixels land more than one pixel apart in the output, leaving gaps that nothing ever writes to — a lattice of holes. When it shrinks, a few inputs land on the same output and fight over it.

Real setups invert the problem. Loop over each output pixel, apply the *inverse* transform to find where it came from in the input, and sample there. each output pixel gets written just once. So There are no holes, and the sampling step is an plain interpolation problem — nearest neighbour or bilinear, with the same trade-offs as resizing.

This is why the code above finds the inverse rather than the forward map, and why an affine transform needs an interpolation choice at all.

Where it shows up

Data boost. Small random rotations, scales and shears applied to training images teach a network that a cat rotated by ten degrees is still a cat. It's the cheapest smoothing there's, and it's affine.

Image registration. Aligning two photos of the same scene means finding the affine transform that best maps one onto the other.

doc scanning. Straightening a page photographed at an angle is affine if the camera was square to the page and projective if it wasn't, which is why scanning apps ask you to mark the corners.

One matrix, applied and measured

The six numbers are one 2×3 mat sending (x, y) to (ax+by+e, cx+dy+f), and rotation — which has no control of its own — is just a given filling of a, b, c, d. A 30-degree rotation is a=d=cos30=0.866, c=sin30=0.5, b=-0.5, and applied to the point (1, 0):

quantityvalue
(1,0) → (a·1, c·1)(0.866, 0.500)
determinant ad − bc1.000

The point lands just where a 30-degree turn puts it, and the determinant is the factor by which area scales. 1 for a pure rotation, because turning changes no areas. 4 if both axes double. Minus if the image is flipped. And 0 when the transform merges the plane onto a line and turns non-invertible. That determinant is why rotation is "a given combination of scale and shear", a fact the mat makes obvious and four separate functions hide. The other thing the mat view clarifies is why setups run *backwards*: mapping input pixels forward leaves holes where enlargement spreads them apart. So Real code loops over *output* pixels, applies the inverse mat to find where each came from, and samples there with an interpolation. Which is why an affine warp needs the same nearest-or-bilinear choice as resizing.

Where it goes wrong with Affine Transforms

Expecting to correct perspective. Affine can't. A photo taken from an angle needs a projective (homography) transform. This Has eight params and doesn't preserve parallelism.

Composing in the wrong order. mat times doesn't commute. Rotate-then-translate and translate-then-rotate give different results, and which one you meant is often clear only after seeing the wrong one.

Rotating about the origin by accident. The formulas rotate about (0, 0), which is a corner. Rotating about the centre means translating the centre to the origin, rotating, and translating back — three mats times'd together.

Losing the corners. A rotated rectangle doesn't fit in the first frame. Either accept the clipping or find the bounding box of the transformed corners and enlarge the canvas first.

Questions people ask about Affine Transforms

What makes a transform affine? It preserves straight lines and parallelism. Rotation, scaling, shear and translation are all affine, and any combination of them is still one 2x3 matrix.

What is the difference between affine and perspective transforms? Affine keeps parallel lines parallel. A perspective transform does not, so a rectangle can become a trapezoid, and it needs a 3x3 matrix with a division step.

Why does the order of transforms matter? Matrix multiplication is not commutative. Rotating then translating puts the shape somewhere different from translating then rotating, so compose the matrices in the order you want them applied.

How do I rotate around the image centre instead of the corner? Translate the centre to the origin, rotate, then translate back. OpenCV's getRotationMatrix2D builds exactly this combined matrix for you.

Why do warps leave black corners or cut off content? The output canvas keeps its original size, so rotated corners fall outside it and uncovered output pixels have no source. Enlarge the canvas or choose a border mode such as reflect.

What does the determinant tell me? Its absolute value is the area scale factor. A determinant of 1 preserves area, 4 quadruples it, and a negative value means the image was mirrored.

Rotation, scale and shear as one matrix

each geometric transform that keeps straight lines straight is one 2x3 mat. Knowing that turns six separate ops into one. This builds the mats, composes them. Works through the two things that trip everyone up -- composition order, and that you've to run the transform backwards.

example_01.pyNumPy
Output

The next block shows that composition order matters and demonstrates rotation about an arbitrary centre point.

example_02.pyNumPy
Output

The next block applies a transform to an image both forwards and backwards, showing why backward mapping avoids holes.

example_03.pyNumPy
Output

The next block shows the family of geometric transforms, from a 2-parameter translation up to an 8-parameter homography, and where spatial transformer networks fit.

example_04.pyNumPy
Output

Check yourself

0 of 3

Answer without scrolling back up.

  1. What does the determinant ad - bc of an affine transform tell you?

  2. Why do implementations iterate over output pixels and apply the inverse transform?

  3. Which of these is NOT an affine transform?

Cheat sheet

Affine Transforms

Build a 2x3 affine matrix from rotation, scale, shear and translation, apply it to an image, and see every pixel land in its new position.

COMPUTER VISION · vizlearn.in/computer_vision/affine_transforms.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.