RANSAC Homography Estimation
Implement RANSAC for homography estimation between two images, a crucial task in computer vision that involves finding the transformation matrix mapping points between planes. A homography H (3Γ3 matrix) maps points between planes: pβ²=Hp.
The Direct Linear Transform (DLT) algorithm estimates H from 4+ correspondences by setting up a linear system Ah=0 and solving using Singular Value Decomposition (SVD).
- Set up linear system Ah=0 from point correspondences
- Solve using SVD to find h, the last row of VT RANSAC makes this robust to mismatched correspondences.
This technique is widely used in image stitching and object recognition.
Example:
src = [[0,0], [1,0], [1,1], [0,1], [10,10]] dst = [[0,0], [2,0], [2,2], [0,2], [5,5]] # 2x scale + outlier threshold = 1.0
H β [[2,0,0],[0,2,0],[0,0,1]], 4 inliers
First 4 point pairs indicate 2Γ scaling. Last pair [10,10]β[5,5] is inconsistent (would be 0.5Γ scale).
RANSAC finds the homography explaining 4/5 correspondences.
Constraints:
- src_points: Points in first image [[x1,y1], ...]
- dst_points: Corresponding points in second image
- threshold: Reprojection error threshold
- n_iter: RANSAC iterations
- Return: Dict with 'H' (3Γ3 matrix) and 'inliers'
More from CV: Model Fitting and Optimization
- Background Knowledge
A homography is a 3Γ3 projective transformation that maps points from one plane to another in homogeneous coordinates: pβ²βΌHp, where p=(x,y,1)T and pβ²=(xβ²,yβ²,1)T. It can represent rotations, translations, scaling, and perspective distortions between two views of a planar scene or images taken by a rotating camera. Because H is defined up to scale, it has 8 degrees of freedom (9 parameters minus 1 scale).
With a set of point correspondences (xiβ,yiβ)β(xiβ²β,yiβ²β), homography estimation can be cast as a linear problem using the DLT (Direct Linear Transform) algorithm. Each correspondence provides two linear equations in the 8 unknowns, so at least 4 correspondences are needed. DLT builds a matrix A such that Ah=0, where h is the 9-vector formed by stacking the entries of H. The solution is obtained using SVD as the right singular vector corresponding to the smallest singular value.
In practice, many correspondences are noisy and contain outliers (wrong matches). Directly running DLT on all points often fails. RANSAC (Random Sample Consensus) makes the estimation robust by repeatedly sampling minimal subsets (4 points), estimating a candidate H, then counting how many points are consistent with it (inliers) according to a reprojection error threshold. The model with the largest inlier set is chosen, and a final refined homography is typically re-estimated using all inliers.
- Algorithm / General Approach
For this problem, the general pattern is:
- Use RANSAC to search over many possible homographies:
- Randomly sample 4 correspondences.
- Estimate a homography from these using DLT.
- Evaluate how many correspondences agree with this homography (inliers).
- After RANSAC finishes:
- Take the best inlier set (largest consensus).
- Re-estimate the homography using all inliers (again via DLT, now overdetermined).
This is a classic βrobust model fittingβ pattern: alternate between (a) random minimal subset sampling to generate hypotheses, and (b) model verification by counting inliers under an error threshold.
- Step-by-Step Strategy
A. Represent correspondences
- Input: two lists of corresponding points {(xiβ,yiβ)}i=1Nβ in image 1 and {(xiβ²β,yiβ²β)}i=1Nβ in image 2.
- Store them as arrays of shape (N,2).
B. Implement DLT homography estimation
Given at least 4 correspondences:
- For each pair (x,y)β(xβ²,yβ²), build two rows of A:
- Stack these rows for all correspondences into matrix A of size (2NΓ9).
- Compute SVD: A=UΞ£VT.
- Let h be the last column of V (or last row of VT); reshape h into 3Γ3 matrix H.
- Optionally normalize H (e.g., divide by H2,2β or H2,3β) so that scale is fixed.
Code sketch (DLT):
import numpy as np
def dlt_homography(src_pts, dst_pts):
# src_pts, dst_pts: (N, 2), N >= 4
N = src_pts.shape
A = []
for i in range(N):
x, y = src_pts[i]
xp, yp = dst_pts[i]
A.append([-x, -y, -1, 0, 0, 0, x*xp, y*xp, xp])
A.append([0, 0, 0, -x, -y, -1, x*yp, y*yp, yp])
A = np.asarray(A)
_, _, Vt = np.linalg.svd(A)
h = Vt[-1]
H = h.reshape(3, 3)
return H / H[2, 2]
C. Define reprojection error and inliers
Given H and a correspondence (x,y)β(xβ²,yβ²):
- Map source to destination:
- Compute error, e.g., Euclidean distance:
- A point is an inlier if e<threshold (e.g., a few pixels).
D. RANSAC loop
Parameters:
- max_iters: maximum number of iterations.
- threshold: reprojection error threshold.
- Optionally confidence and an estimated outlier ratio to adaptively set iterations.
Algorithm:
def ransac_homography(src_pts, dst_pts, max_iters=1000, threshold=3.0):
N = src_pts.shape
best_H = None
best_inliers = None
best_inlier_count = 0
for _ in range(max_iters):
# 1. Randomly sample 4 distinct indices
idx = np.random.choice(N, 4, replace=False)
H_candidate = dlt_homography(src_pts[idx], dst_pts[idx])
# 2. Compute reprojection errors for all points
ones = np.ones((N, 1))
pts_h = np.hstack([src_pts, ones]) # (N, 3)
proj = (H_candidate @ pts_h.T).T # (N, 3)
proj_xy = proj[:, :2] / proj[:, 2:3] # divide by w
errors = np.linalg.norm(proj_xy - dst_pts, axis=1)
# 3. Determine inliers
inliers = errors < threshold
inlier_count = np.sum(inliers)
# 4. Update best model
if inlier_count > best_inlier_count:
best_inlier_count = inlier_count
best_inliers = inliers
best_H = H_candidate
# 5. Re-estimate H using all inliers
if best_inliers is not None and np.sum(best_inliers) >= 4:
best_H = dlt_homography(src_pts[best_inliers], dst_pts[best_inliers])
return best_H, best_inliers
You can refine iteration count using:
Nitersβ=log(1β(1βΟ΅)s)log(1βp)βwhere p is desired success probability, Ο΅ is outlier ratio, and s=4 (sample size).
- Common Pitfalls
- Degenerate samples: Four points that are nearly collinear produce an unstable homography. You should detect and skip such samples or rely on numerical conditioning (SVD often exposes degeneracies via tiny singular values).
- Lack of normalization: Not normalizing coordinates (centering and scaling) can lead to numerical instability in DLT. A more robust implementation uses Hartley normalization (shift to zero mean, scale so average distance is 2β), applies DLT, and denormalizes.
- Too tight / too loose threshold:
- Too small: almost no inliers; RANSAC fails.
- Too large: many outliers classified as inliers; homography is inaccurate. Choose based on expected pixel noise.
- Not re-estimating on all inliers: The homography from a single 4-point sample is noisy; always recompute H using all inliers at the end.