Visual SLAM, Visual Odometry, epipolar geometry, and PnP all take for granted a correct mapping from pixel coordinates into 3D space. But light passing through a lens never forms an image the way a textbook-ideal pinhole would. What is the focal length, in pixel units? How far is the principal point offset from the image center? How much does a straight line bow on the image? Camera calibration is the process of solving for these quantities numerically. Skimp on calibration, and no matter how sophisticated the SLAM or SfM algorithm stacked on top of it is, a systematic error propagates outward from the very foundation.

0. 30-Second Summary

1. Revisiting the Pinhole Model From Coordinates

A world-coordinate point \mathbf{X}=(X,Y,Z,1)^\mathsf{T} (homogeneous coordinates) is projected onto the image, up to scale, in the pinhole model as

\tilde{\mathbf{x}} \sim P\mathbf{X},\qquad P=K[R\mid\mathbf{t}]

R\in SO(3) and \mathbf{t} are the rotation and translation from world coordinates to camera coordinates (the extrinsic parameters), and K is the intrinsic matrix

K=\begin{bmatrix}f_x&s&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}

Here f_x,f_y are the focal lengths in pixel units, (c_x,c_y) is the principal point (where the optical axis intersects the image plane), and s is the skew (nearly zero on most modern sensors). Calibration is the inverse problem of recovering this K, the distortion coefficients, and each shot's R,\mathbf{t}, purely from observed data.

Recovering intrinsic parameters from a checkerboard seen in multiple poses A diagram showing a planar pattern photographed several times at different angles and distances by a fixed camera, and the intrinsic matrix K and distortion coefficients being jointly estimated from each view's homography. Camera (fixed) Pose 1 Pose 2 Pose 3 K, k1,k2,k3 p1,p2 R,t for each pose

As the diagram shows, a single shot alone can't separate the intrinsic parameters from the extrinsic ones. Only by collecting several observations at different poses (tilt, distance) can K be pinned down nearly uniquely — and this is exactly the core idea behind Zhang's method, covered next.

2. A Lens Isn't an Ideal Pinhole: the Distortion Model

Real lenses have radial distortion and tangential distortion. Writing the normalized image coordinates as (x,y)=(X_c/Z_c,\,Y_c/Z_c) and the radius as r^2=x^2+y^2, the Brown–Conrady distortion model can be written as follows.

x_d = x\left(1+k_1r^2+k_2r^4+k_3r^6\right) + 2p_1xy+p_2\left(r^2+2x^2\right)
y_d = y\left(1+k_1r^2+k_2r^4+k_3r^6\right) + p_1\left(r^2+2y^2\right)+2p_2xy

k_1,k_2,k_3 are the radial distortion coefficients, and p_1,p_2 are the tangential distortion coefficients. Radial distortion is a phenomenon where the image shrinks or bulges depending on distance from the lens center, showing up as the pronounced "barrel" distortion seen with wide-angle lenses, or the "pincushion" distortion seen with telephoto lenses. Tangential distortion is a smaller, asymmetric component arising from implementation imperfections where the lens group and the image sensor aren't perfectly parallel. The final pixel coordinates are obtained as \tilde{\mathbf{x}}_{px}=K(x_d,y_d,1)^\mathsf{T}.

Comparing an undistorted grid to barrel-type radial distortion The left side shows an ideal grid whose lines stay straight; the right side shows the same grid appearing as a barrel shape, curving inward more toward the outer edges, due to a lens's radial distortion. Ideal (no distortion) Barrel-type radial distortion

For lenses with an extremely wide field of view, like fisheye lenses, the Brown–Conrady polynomial model tends to diverge near the edges and isn't practical. It's common to use an angle-based equidistant-projection approximation instead, like OpenCV's fisheye model. Rather than "memorizing one distortion model," it's important to keep in mind that the model should be chosen based on the lens's field of view and optical design.

3. Zhang's Method: Calibrating With a Planar Pattern

The most widely implemented method today is the planar-pattern calibration method Zhengyou Zhang published in 2000. No special 3D calibration rig is needed — all it requires is photographing a planar pattern, such as a printed checkerboard, from several poses, moving the camera or the pattern itself.

The mapping from the pattern plane (taking Z=0) at some pose i to the image can be written as a homography using the rotation matrix's first and second columns \mathbf{r}_1,\mathbf{r}_2 and the translation \mathbf{t}.

H_i \sim K\begin{bmatrix}\mathbf{r}_1&\mathbf{r}_2&\mathbf{t}\end{bmatrix}

Here, each pose's H_i can be estimated linearly from the known grid points on the pattern and their image correspondences, using the DLT method covered in the Homography Primer. Using the constraint that the rotation matrix's columns are orthonormal — \mathbf{r}_1^\mathsf{T}\mathbf{r}_2=0,\ \|\mathbf{r}_1\|=\|\mathbf{r}_2\| — gives a linear equation in B=K^{-\mathsf{T}}K^{-1}:

\mathbf{h}_1^\mathsf{T}B\mathbf{h}_2=0,\qquad \mathbf{h}_1^\mathsf{T}B\mathbf{h}_1=\mathbf{h}_2^\mathsf{T}B\mathbf{h}_2

which gives two such equations per pose (\mathbf{h}_1,\mathbf{h}_2 are H_i's first and second columns). Since B is a symmetric matrix with 6 degrees of freedom, given 3 or more poses, B can be found via linear least squares, and then K found in closed form through a procedure equivalent to Cholesky decomposition. This is why Zhang's method only needs "a few shots of a plane." However, if all the poses are nearly parallel to the image plane (fronto-parallel), the equations degenerate, so poses at several different tilts are required.

The closed-form solution is only an initial value that ignores distortion. From here, the standard two-stage approach in practice refines all parameters, including the distortion coefficients, via nonlinear optimization (usually Levenberg–Marquardt) using the reprojection error described in the next section as the objective function.

4. Reprojection Error and Parameter Optimization

Calibration's objective function is to minimize, over every pose and every grid point, the difference between the observed pixel and the projected position computed with the estimated parameters. Writing the observation at pose i, point j as \mathbf{u}_{ij}, and the corresponding known 3D point on the plane as \mathbf{X}_j,

E(K,\boldsymbol{\kappa},\{R_i,\mathbf{t}_i\})= \sum_{i=1}^{n}\sum_{j=1}^{m} \left\|\mathbf{u}_{ij}-\pi_d\!\left(K,\boldsymbol{\kappa},R_i\mathbf{X}_j+\mathbf{t}_i\right)\right\|^2

is what gets minimized. \boldsymbol{\kappa}=(k_1,k_2,k_3,p_1,p_2) is the distortion coefficient vector, and \pi_d is the projection function including distortion. The unknowns are large — K (4–5 degrees of freedom), \boldsymbol{\kappa} (3–5 degrees of freedom), and R_i,\mathbf{t}_i per pose (6 degrees of freedom × number of poses) — but with enough observed points, the problem is well constrained. This formulation can be seen as a special case of the "jointly optimize camera pose and 3D structure" framework covered in the Bundle Adjustment Primer. Because the 3D-point coordinates are known and fixed in calibration, it's an easier subproblem than ordinary bundle adjustment.

It's common to report the root mean square (RMS) of the reprojection error \left\|\mathbf{u}_{ij}-\hat{\mathbf{u}}_{ij}\right\| as "calibration accuracy," but it's risky to call calibration done just by looking at the mean value. Always check the following as well:

5. Implementation Skeleton in OpenCV

The typical single-camera calibration procedure looks like this. Fixing the shooting conditions (resolution, zoom, focus), and photographing the pattern at the image's four corners, center, and several tilts, accounts for 90% of a good calibration.

import cv2 as cv
import numpy as np

pattern_size = (9, 6)  # number of internal corners
objp = np.zeros((pattern_size[0]*pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
objp *= square_size_m  # measured side length of one square [m]

obj_points, img_points = [], []
for gray in calibration_images:  # multiple frames shot at different poses
    found, corners = cv.findChessboardCorners(gray, pattern_size)
    if found:
        corners = cv.cornerSubPix(gray, corners, (11, 11), (-1, -1),
                                   (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001))
        obj_points.append(objp)
        img_points.append(corners)

ret, K, dist, rvecs, tvecs = cv.calibrateCamera(
    obj_points, img_points, gray.shape[::-1], None, None)
# ret is the RMS reprojection error [px]. Also compute per-pose error separately to check.

For wide-angle or fisheye lenses, use the cv.fisheye.calibrate family of APIs rather than the ordinary calibrateCamera. For a stereo rig, use cv.stereoCalibrate to find the relative pose in addition to both cameras' intrinsic parameters, and cv.stereoRectify to align the left and right images to horizontal epipolar lines. This procedure connects directly to the baseline design covered in How Stereo Cameras Work and How Depth Cameras Work.

6. Why Calibration Drift Breaks Downstream Systems

Much of the math in Visual SLAM, VO, and PnP is written under the assumption that calibration values stay constant as long as shooting conditions don't change. Essential Matrix estimation in the Epipolar Geometry Primer assumes normalized coordinates K^{-1}\tilde{\mathbf{x}}; the reprojection-error minimization in the PnP Primer also treats K as known. If K or the distortion coefficients no longer match the actual optical system, every one of these calculations is effectively being carried out with "the wrong ruler."

Specifically, calibration values quietly degrade for reasons such as these:

These errors are hard to notice in a single, isolated pose estimate. But in systems that sequentially integrate pose over time, like VO or SLAM, systematic reprojection error accumulates as drift, producing a distorted map that loop closure can't fully correct. In production systems, it's advisable to monitor calibration drift online using fixed, known 3D features (straight lines on a building, signs of known size), or to build in a routine of periodic recalibration.

7. Common Failures and Countermeasures

Failure Pattern What Happens Countermeasure
Insufficient pose diversity (only front-facing shots) K and distortion become poorly determined, especially k_3 and the principal point Shoot at the image's four corners, center, and several different tilt angles
Pattern warped off a flat plane The very premise of calibration breaks down, spreading a systematic error throughout Mount it on a rigid flat panel; correct printing scale errors by physical measurement
Coarse corner-detection accuracy Raises the floor on reprojection error regardless of the model's expressive power Sub-pixel correction, sufficient resolution, managing focus and exposure
Applying an ordinary pinhole distortion model to a wide-angle lens Divergent error at the image edges, unstable optimization Choose a model matched to the field of view, such as a fisheye model
Not updating K after resizing/cropping Principal point and focal length go out of sync with the scale, causing systematic error in pose estimation Convert K to the corresponding scale on every image transformation
Ignoring rolling shutter The actual projection center differs row by row, even within a single frame Adopt a global shutter, or correct with a row-timing model

8. Summary

Camera calibration is the first step that turns an image from "just a 2D array" into "a geometrically interpretable observation." The pinhole model's intrinsic and extrinsic parameters, together with the radial and tangential distortion coefficients, are recovered from observations at several poses — as in Zhang's planar-pattern method — and finished off with nonlinear optimization of the reprojection error. The quality of this calibration doesn't show up in any single algorithm's own accuracy metric, but it's the premise underlying every equation in every downstream stage — epipolar geometry, PnP, Visual SLAM, SfM, bundle adjustment. Calibration isn't a one-time ritual — it's part of a pipeline that should keep being monitored as shooting conditions change.

References

#Camera Calibration #Zhang's Method #Lens Distortion #OpenCV #Computer Vision