What each number does
Set everything 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 horizontally. 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 transformation.
b shears in y, tilting columns instead of rows.
e and f translate, in pixels, and are the only two that do not interact with the others.
Rotation is not its own control because it does not 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 particular combination of scale and shear, which is the sort of fact the matrix 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 is the factor by which area is multiplied.
A pure rotation has determinant 1, because rotating does not change area. Doubling both scales gives 4. A negative determinant means the image has been flipped — orientation reversed.
At determinant 0 the transform is not invertible, and geometrically it has collapsed the plane onto a line. Set a and c to the same value with b and d matching and the image degenerates. Nothing can undo that, because the information about position along the lost direction is gone.
Why implementations work backwards
The obvious way to apply a transform is to loop over input pixels, compute where each lands, and write it there. This produces 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, several inputs land on the same output and fight over it.
Real implementations invert the problem. Loop over every output pixel, apply the *inverse* transform to find where it came from in the input, and sample there. Every output pixel gets written exactly once, so there are no holes, and the sampling step is an ordinary interpolation problem — nearest neighbour or bilinear, with the same trade-offs as [resizing](resizing_and_interpolation.html).
This is why the code above computes the inverse rather than the forward map, and why an affine transform needs an interpolation choice at all.
Where it shows up
Data augmentation. 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 is the cheapest regularisation there is, and it is affine.
Image registration. Aligning two photographs of the same scene means finding the affine transform that best maps one onto the other.
Document scanning. Straightening a page photographed at an angle is affine if the camera was square to the page and projective if it was not, which is why scanning apps ask you to mark the corners.
Where it goes wrong
Expecting to correct perspective. Affine cannot. A photograph taken from an angle needs a projective (homography) transform, which has eight parameters and does not preserve parallelism.
Composing in the wrong order. Matrix multiplication does not commute. Rotate-then-translate and translate-then-rotate give different results, and which one you meant is usually 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 matrices multiplied together.
Losing the corners. A rotated rectangle does not fit in the original frame. Either accept the clipping or compute the bounding box of the transformed corners and enlarge the canvas first.