Batch Affine Transform with PyTorch
Implement batch affine transformations on 2D points using PyTorch tensors.
Given a batch of 2D points and a 2Γ3 affine transformation matrix, apply the transformation to all points efficiently using matrix operations.
An affine transformation combines linear transformation (rotation, scaling, shearing) with translation:
(xβ²yβ²β)=(acβbdβtxβtyββ)βxy1ββ
Or equivalently: xβ²=ax+by+txβ, yβ²=cx+dy+tyβ
Key insight: For N points, convert to homogeneous coordinates (add column of 1s) and multiply with the transformation matrix transposed: Pβ²=Phββ MT
where Phβ is (N, 3) and MT is (3, 2), giving Pβ² as (N, 2).
Example:
points = [[1, 0], [0, 1], [1, 1]] transform = [[1, 0, 10], [0, 1, 20]] # Translation by (10, 20)
[[11.0, 20.0], [10.0, 21.0], [11.0, 21.0]]
Affine matrix (translation only): M=(10β01β1020β)
For each point [x, y], compute: xβ²=1β x+0β y+10=x+10 yβ²=0β x+1β y+20=y+20
Results:
- [1, 0] β [1+10, 0+20] = [11, 20]
- [0, 1] β [0+10, 1+20] = [10, 21]
- [1, 1] β [1+10, 1+20] = [11, 21]
Constraints:
- points: Tensor of shape (N, 2) containing 2D points
- transform: Tensor of shape (2, 3) - affine transformation matrix
- Return: Transformed points as tensor of shape (N, 2)
- Round to 4 decimal places
You want to apply a single 2Γ3 affine transform to a batch of 2D points using PyTorch tensor ops only (no Python loops), by using homogeneous coordinates and a single matrix multiply.
1. Background Knowledge
An affine transformation in 2D is a function that maps a point (x,y) to a new point (xβ²,yβ²) by combining a linear transform (rotation, scaling, shear) and a translation:
(xβ²yβ²β)=(acβbdβtxβtyββ)βxy1ββThe 2Γ2 block (acβbdβ) handles the linear part; (txβ,tyβ) is the translation.
To express this as a single matrix multiplication, we use homogeneous coordinates: we append a 1 to each 2D point so that each point becomes (x,y,1). For a batch of N points, stacking them gives a tensor Phβ of shape (N,3). The affine matrix M has shape (2,3). Using PyTorchβs row-major convention for batched points, the natural operation is:
Pβ²=Phββ MTwhere PhββRNΓ3, MTβR3Γ2, giving Pβ²βRNΓ2.
In deep learning / CV code, this pattern shows up everywhere: you keep all points in a tensor of shape (N,2), convert once to homogeneous coordinates, and then use vectorized matrix multiplication so the operation is fast and GPU-friendly.
2. Algorithm / Approach
General pattern:
- Input shapes:
- Points: tensor points of shape (N, 2) (each row [x, y]).
- Affine matrix: tensor M of shape (2, 3) (as in the formula).
- Convert to homogeneous coordinates:
- Build a tensor ones of shape (N, 1) filled with 1.
- Concatenate: P_h = torch.cat([points, ones], dim=1) β shape (N, 3).
- Matrix multiply:
- Use M.T (shape (3, 2)).
- P_prime = P_h @ M.T β shape (N, 2).
This is a standard βaugment-then-matmulβ pattern for applying affine transforms to batches of vectors.
3. Step-by-Step Strategy
- Understand input tensors:
- Confirm points has shape (N, 2) and dtype (e.g., torch.float32).
- Confirm M has shape (2, 3) and same dtype/device.
- Create homogeneous coordinates:
N = points.shape
ones = torch.ones(N, 1, device=points.device, dtype=points.dtype)
P_h = torch.cat([points, ones], dim=1) # (N, 3)
- Apply affine transform:
M_T = M.transpose(0, 1) # (3, 2)
P_prime = P_h @ M_T # (N, 2)
- Return result:
- Output P_prime as the transformed points.
- (Optional) Batch of transforms:
- If you later extend to a batch of matrices (B, 2, 3) and points (B, N, 2), you will use broadcasting or einsum / bmm, but the idea remains: augment with ones, then multiply.
4. Common Pitfalls
-
Shape mismatches:
-
Using M instead of M.T: (N, 3) @ (2, 3) is invalid; you need (N, 3) @ (3, 2).
-
Concatenating along wrong dimension (e.g., dim=0 instead of dim=1).
-
Forgetting homogeneous coordinate:
-
If you multiply (N, 2) by a (2, 2) linear matrix, you only get rotation/scale/shear, no translation.
-
Device/dtype mismatch:
-
Creating ones on CPU while points is on GPU, or using different dtypes; always match device and dtype.
-
Incorrect point layout:
-
Some APIs store as (2, N) instead of (N, 2). Ensure you know whether points are stored as rows or columns and adjust matmul accordingly.
5. Time & Space Complexity
Let N be the number of points.
-
Time complexity:
-
Creating homogeneous coordinates: O(N).
-
Matrix multiplication (NΓ3)β (3Γ2): O(N).
-
Overall: O(N).
-
Space complexity:
-
Storing input points: O(N).
-
Homogeneous points P_h: O(N).
-
Output P_prime: O(N).
-
Overall additional memory beyond inputs: O(N).