identity
Move, turn, resize, mirror and cut images by changing where every pixel goes.
Geometric transforms change pixel positions without changing the content.
Shift the image position.
Rotate around a point.
Resize the image.
Mirror horizontally or vertically.
Any mix of the above. Parallel lines stay parallel.
A 3-D-like change of viewpoint.
The dashed outline is the original; each card animates its transform as it appears.
Translation shifts the image by \((t_x, t_y)\) pixels.
Transformation matrix
$$M = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \end{bmatrix}$$
import cv2
import numpy as np
# translation matrix (shift right 100, down 50)
M = np.float32([[1, 0, 100], # ans:
[0, 1, 50]]) # ans:
# apply translation
height, width = img.shape[:2]
translated = cv2.warpAffine(img, M, (width, height)) # ans:
# shift left (negative tx)
M = np.float32([[1, 0, -50],
[0, 1, 0]])
# ans: shifts 50 pixels left
Rotation turns the image by an angle \(\theta\) around a centre \((c_x, c_y)\), optionally scaling by \(s\).
cv2.getRotationMatrix2D(center, angle, scale). A positive angle turns the picture anticlockwise.
# get image center
height, width = img.shape[:2]
center = (width // 2, height // 2)
# rotation matrix (45 degrees, no scaling)
M = cv2.getRotationMatrix2D(center, 45, 1.0) # ans:
# ans: angle=45°, scale=1.0 (no resize)
# apply rotation
rotated = cv2.warpAffine(img, M, (width, height)) # ans:
# rotate 90 degrees
M = cv2.getRotationMatrix2D(center, 90, 1.0)
rotated = cv2.warpAffine(img, M, (width, height))
# ans: 90° anticlockwise: positive angles turn anticlockwise
Scaling changes the image size. New pixels fall between old ones, so their values must be interpolated.
Fast, blocky.
INTER_NEAREST
Smooth, fast. The default.
INTER_LINEAR
Smoother, slower.
INTER_CUBIC
Best quality, slowest.
INTER_LANCZOS4
# resize to specific dimensions
resized = cv2.resize(img, (640, 480)) # ans:
# ans: width=640, height=480 (exact size)
# resize by scale factor
scaled = cv2.resize(img, None, fx=0.5, fy=0.5) # ans:
# ans: fx=fy=0.5 means 50% of original size
# resize with interpolation
resized = cv2.resize(img, (320, 240), interpolation=cv2.INTER_CUBIC) # ans:
# ans: INTER_CUBIC for smooth downscaling
# upscale 2x
scaled = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)
# ans: doubles size with bilinear interpolation
Note the order: cv2.resize takes (width, height), but img.shape is (height, width).
Flipping mirrors the image across an axis.
flipCode = 1 (left ↔ right)flipCode = 0 (top ↔ bottom)flipCode = -1 (same as a 180° rotation)
# horizontal flip (left-right)
flipped_h = cv2.flip(img, 1) # ans:
# ans: flipCode=1 for horizontal
# vertical flip (up-down)
flipped_v = cv2.flip(img, 0) # ans:
# ans: flipCode=0 for vertical
# both axes (180° rotation)
flipped_both = cv2.flip(img, -1) # ans:
# ans: flipCode=-1 for both axes
Transpose swaps rows and columns: it reflects the image across its main diagonal, so width and height swap.
Original, 3×4
Transposecv2.transpose(img)
Rotate 90° clockwiseROTATE_90_CLOCKWISE
Both are 4×3, but the pixels differ: a transpose is a 90° rotation plus a flip.
# transpose (swap width and height)
transposed = cv2.transpose(img) # ans:
height, width = img.shape[:2]
# ans: new shape = (width, height, channels)
# transpose vs rotate 90°
transposed = cv2.transpose(img)
rotated = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
# ans: different results! transpose reflects across the diagonal
# convenient rotation functions
rot_90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) # ans:
rot_180 = cv2.rotate(img, cv2.ROTATE_180) # ans:
rot_270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) # ans:
Cropping extracts a region of interest (ROI) with NumPy slicing: img[y1:y2, x1:x2]. No special OpenCV function needed.
# crop region [y1:y2, x1:x2]
cropped = img[100:300, 50:250] # ans:
# ans: rows 100-300, columns 50-250
# crop center region
height, width = img.shape[:2]
x = width // 4
y = height // 4
w = width // 2
h = height // 2
cropped = img[y:y+h, x:x+w] # ans:
# ans: center half of image
Rows come first: y before x. A slice is a view, so use .copy() if you will modify it.
Answer first, then press → to check.
What does translation do?
Shifts the image position by \((t_x, t_y)\).
What is the rotation centre?
The point the image rotates around.
Which interpolation is best for downscaling?
INTER_AREA or INTER_CUBIC (best quality).
What is flipCode=1?
A horizontal flip (left ↔ right).
What is the difference between transpose and rotate?
Transpose reflects across the diagonal; rotate turns without mirroring.
What happens to the size during rotation?
The output keeps the given size, so content may be clipped at the edges.
What are fx and fy in resize?
Scale factors for the x and y directions.
The fastest interpolation method?
INTER_NEAREST (nearest neighbour).
The best quality interpolation?
INTER_LANCZOS4 (slowest but best).
What is warpAffine used for?
Applying affine transformations: translation, rotation, scaling, shear.
# translate right 50, down 30
M = np.float32([[1, 0, 50], # ans:
[0, 1, 30]]) # ans:
h, w = img.shape[:2] # ans:
translated = cv2.warpAffine(img, M, (w, h)) # ans:
# rotate 30 degrees around center
h, w = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 30, 1.0) # ans:
rotated = cv2.warpAffine(img, M, (w, h)) # ans:
# resize to 800x600
resized = cv2.resize(img, (800, 600)) # ans:
# scale down to 25%
scaled = cv2.resize(img, None, fx=0.25, fy=0.25) # ans:
# flip horizontally
flipped = cv2.flip(img, 1) # ans:
# flip vertically
flipped = cv2.flip(img, 0) # ans:
# rotate 90 degrees clockwise
rotated = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) # ans:
# rotate 180 degrees
rotated = cv2.rotate(img, cv2.ROTATE_180) # ans:
# transpose image
transposed = cv2.transpose(img) # ans:
# crop top-left 100x100 region
cropped = img[0:100, 0:100] # ans:
# upscale 3x with cubic interpolation
scaled = cv2.resize(img, None, fx=3, fy=3, interpolation=cv2.INTER_CUBIC) # ans:
# rotate with scale
h, w = img.shape[:2]
M = cv2.getRotationMatrix2D((w//2, h//2), 45, 0.8)
# ans: 45° rotation + 80% scale
rotated = cv2.warpAffine(img, M, (w, h)) # ans:
# combine flip and rotate
flipped = cv2.flip(img, 1)
rotated = cv2.rotate(flipped, cv2.ROTATE_90_CLOCKWISE) # ans:
# resize maintaining aspect ratio
h, w = img.shape[:2]
new_w = 640
new_h = int(h * (new_w / w))
resized = cv2.resize(img, (new_w, new_h)) # ans:
# crop center square
h, w = img.shape[:2]
size = min(h, w)
start_y = (h - size) // 2
start_x = (w - size) // 2
cropped = img[start_y:start_y+size, start_x:start_x+size] # ans:
# translate with border handling
M = np.float32([[1, 0, 100],
[0, 1, 50]])
h, w = img.shape[:2]
translated = cv2.warpAffine(img, M, (w, h), # ans:
borderMode=cv2.BORDER_CONSTANT, # ans:
borderValue=(0, 0, 0)) # ans:
# rotate and expand canvas
angle = 45
h, w = img.shape[:2]
center = (w//2, h//2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
new_w = int(h * sin + w * cos)
new_h = int(h * cos + w * sin)
M[0, 2] += (new_w - w) / 2
M[1, 2] += (new_h - h) / 2
rotated = cv2.warpAffine(img, M, (new_w, new_h)) # ans:
# ans: expands the canvas to fit the rotated image