Image Transformation
Image deformations, also called transformations in the computer vision literature (see Szeliski1), fall into the categories shown below:
Each category preserves a different, nested set of geometric properties — every property a more restrictive category preserves is also preserved by every category to its left:
| Property | Translation | Euclidean | Similarity | Affine | Projective |
|---|---|---|---|---|---|
| Straight lines stay straight | Yes | Yes | Yes | Yes | Yes |
| Parallel lines stay parallel | Yes | Yes | Yes | Yes | No |
| Angles preserved | Yes | Yes | Yes | No | No |
| Lengths/distances preserved | Yes | Yes | No | No | No |
| Absolute orientation preserved (no rotation) | Yes | No | No | No | No |
Pure Translation (Rigid Body Motion)
As the simplest of the categories above — no change in shape or size —
dictk.imaging.translate shifts
every pixel by a fixed displacement. This example shifts the image by
dx=-60 pixels in x and dy=+80 pixels in y, representing rigid body
motion where the material moves without deforming.
import dictk
from dictk.imaging import translate, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_translate_original.png")
translated = translate(photo, dx=-60, dy=80)
write_image(translated, "astronaut_translate_rigid_body.png")
Saved: astronaut_translate_original.png, astronaut_translate_rigid_body.png
| Translation | Image |
|---|---|
| Original | ![]() |
| dx=-60, dy=+80 | ![]() |
Pure Rotation
A 30° counterclockwise rotation, another rigid body motion that preserves
distances and angles.
dictk.imaging.rotate pivots on the
image's top-left corner (0, 0), consistent with stretch and
translate's pivot choice in this codebase — unlike the more typical
"object spins in place" rotation about the center, most content swings
away from that fixed corner, similar to a door on a hinge.
import dictk
from dictk.imaging import rotate, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_rotate_original.png")
rotated = rotate(photo, 30.0)
write_image(rotated, "astronaut_rotate_30deg.png")
Saved: astronaut_rotate_original.png, astronaut_rotate_30deg.png
| Rotation | Image |
|---|---|
| Original | ![]() |
| 30° (origin-pivoted) | ![]() |
X-Axis Stretch (Extension)
As a concrete example of the similarity category above,
dictk.imaging.stretch applies a
uniaxial stretch along the x-axis: the image's top-left corner (x=0, y=0)
stays fixed, and content grows away from it, using backward mapping with
bilinear interpolation so the result has no gaps (unlike naively moving
each source pixel forward, which can leave holes). The two stretches
below range from a small, realistic deformation (5%, similar in magnitude
to a modest tensile strain in a materials test) up to a much larger one
(50%).
import dictk
from dictk.imaging import stretch, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_stretch_original.png")
stretch_5pct = stretch(photo, factor_x=1.05)
write_image(stretch_5pct, "astronaut_stretch_x_5pct.png")
stretch_50pct = stretch(photo, factor_x=1.50)
write_image(stretch_50pct, "astronaut_stretch_x_50pct.png")
Saved: astronaut_stretch_original.png, astronaut_stretch_x_5pct.png, astronaut_stretch_x_50pct.png
| Stretch | Image |
|---|---|
| Original | ![]() |
| 5% (factor_x=1.05) | ![]() |
| 50% (factor_x=1.50) | ![]() |
Y-Axis Stretch (Compression)
The same dictk.imaging.stretch
function compresses along the y-axis with factor_y < 1.0. Pivoting on
the origin means the top edge (y=0) stays fixed while content shrinks
toward it, leaving a black margin along the bottom — the mirror image of
the x-axis stretch case, where growth away from the origin never leaves a
gap.
import dictk
from dictk.imaging import stretch, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_compress_original.png")
compress_neg5pct = stretch(photo, factor_y=0.95)
write_image(compress_neg5pct, "astronaut_compress_y_neg5pct.png")
compress_neg50pct = stretch(photo, factor_y=0.50)
write_image(compress_neg50pct, "astronaut_compress_y_neg50pct.png")
Saved: astronaut_compress_original.png, astronaut_compress_y_neg5pct.png, astronaut_compress_y_neg50pct.png
| Compression | Image |
|---|---|
| Original | ![]() |
| -5% (factor_y=0.95) | ![]() |
| -50% (factor_y=0.50) | ![]() |
Simple Shear
A shear deformation with γ = 0.5, where horizontal planes slide relative
to each other by an amount proportional to their y-coordinate — the
higher up a row of pixels, the further it shifts sideways.
dictk.imaging.shear pivots on the
image's top-left corner (0, 0), consistent with the other transform
functions in this codebase.
import dictk
from dictk.imaging import shear, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_shear_original.png")
sheared = shear(photo, shear_x=0.5)
write_image(sheared, "astronaut_shear_x_0.5.png")
Saved: astronaut_shear_original.png, astronaut_shear_x_0.5.png
| Shear | Image |
|---|---|
| Original | ![]() |
| γ = 0.5 (shear_x=0.5) | ![]() |
Complex Deformation
Combines rotation (15°) with anisotropic stretching (1.3x in x, 0.8x in
y) — realistic loading scenarios where materials experience multiple
simultaneous deformation modes, typically the hardest case for
correlation algorithms.
dictk.imaging.complex_deform
composes the two into a single deformation gradient (stretch applied
first, then rotation) and applies it in one backward-mapping pass, so
the result isn't blurred by interpolating twice as calling stretch
and then rotate separately would.
import dictk
from dictk.imaging import complex_deform, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_complex_original.png")
combined = complex_deform(photo, factor_x=1.3, factor_y=0.8, angle=15.0)
write_image(combined, "astronaut_complex_deform.png")
Saved: astronaut_complex_original.png, astronaut_complex_deform.png
| Composed Deformation | Image |
|---|---|
| Original | ![]() |
| factor_x=1.3, factor_y=0.8, angle=15° | ![]() |
Crack Dislocation
A vertical crack splits the image at x = width/2: the left half shifts down 4 pixels and the right half shifts up 4 pixels, producing a displacement field that jumps discontinuously across the crack line — unlike every other example on this page, which deforms smoothly. Standard DIC assumes smooth displacements and cannot capture this jump; cases like this motivate the Heaviside finite-element formulation.
import dictk
from dictk.imaging import crack_dislocation, write_image
photo = dictk.astronaut(300, 300)
write_image(photo, "astronaut_crack_plain_original.png")
cracked_plain = crack_dislocation(photo, offset=4.0)
write_image(cracked_plain, "astronaut_crack_plain_dislocation.png")
Saved: astronaut_crack_plain_original.png, astronaut_crack_plain_dislocation.png
| Crack Dislocation | Image |
|---|---|
| Original | ![]() |
| offset=4 pixels | ![]() |















