Skip to main content
Camera calibration is the process of determining the parameters that relate 3D world coordinates to 2D image coordinates. These parameters fall into two groups: intrinsic parameters (internal optics) and extrinsic parameters (pose in the world).

The camera model

A pinhole camera maps a 3D world point M=(X,Y,Z,1)\mathbf{M} = (X, Y, Z, 1)^\top to a 2D image point m=(u,v,1)\mathbf{m} = (u, v, 1)^\top via: mPM,P=K[R    t]\mathbf{m} \sim P\,\mathbf{M}, \qquad P = K\,\bigl[\,R\;|\;\mathbf{t}\,\bigr]

Intrinsic matrix KK

K=(fxscx0fycy001)K = \begin{pmatrix}f_x & s & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1\end{pmatrix}
ParameterMeaning
fx,fyf_x, f_yFocal lengths in pixel units (horizontal and vertical)
cx,cyc_x, c_yPrincipal point (optical axis in image coordinates)
ssSkew coefficient (nearly zero for modern cameras)

Extrinsic parameters: RR and t\mathbf{t}

RSO(3)R \in SO(3) is the rotation matrix and tR3\mathbf{t} \in \mathbb{R}^3 the translation that express the world frame in the camera frame. Together they form the 3×43\times 4 matrix [Rt][R\,|\,\mathbf{t}]. The full 3×43\times 4 projection matrix PP therefore has 11 degrees of freedom (up to scale).

Linear calibration via DLT

The Direct Linear Transform (DLT) estimates PP from a set of known 3D–2D correspondences {Mimi}\{\mathbf{M}_i \leftrightarrow \mathbf{m}_i\}. Each correspondence yields two linear equations in the 12 entries of PP. Stacking n6n \geq 6 correspondences gives the system: Ap=0,p=vec(P)A\,\mathbf{p} = \mathbf{0}, \qquad \mathbf{p} = \text{vec}(P) The solution is the right singular vector of AA corresponding to the smallest singular value — computed via SVD.
Numerical stability requires normalising the input coordinates before applying DLT (translate centroid to origin, scale so mean distance to origin is 2\sqrt{2}).

Least-squares and SVD

For overdetermined systems Ax=bA\mathbf{x} = \mathbf{b} the least-squares solution minimises Axb2\|A\mathbf{x} - \mathbf{b}\|^2. The SVD decomposition A=UΣVA = U\Sigma V^\top gives the solution directly: x=VΣ1Ub\mathbf{x} = V\boldsymbol{\Sigma}^{-1}U^\top\mathbf{b}. For the homogeneous case (b=0\mathbf{b} = \mathbf{0}) the solution is the last column of VV. Recovering KK, RR, t\mathbf{t} from PP uses RQ decomposition (a variant of QR decomposition).

Nonlinear optimisation and lens distortion

Real lenses introduce radial distortion: straight lines appear curved. The distorted image coordinates (ud,vd)(u_d, v_d) relate to the undistorted (u,v)(u, v) by: ud=u(1+k1r2+k2r4+),r2=u2+v2u_d = u\,(1 + k_1 r^2 + k_2 r^4 + \cdots), \quad r^2 = u^2 + v^2 where k1,k2,k_1, k_2, \ldots are radial distortion coefficients. After the linear DLT initialisation, a nonlinear least-squares optimiser (e.g., Levenberg–Marquardt) jointly refines all parameters including distortion.

RANSAC for robust calibration

When correspondences include outliers, ordinary least-squares gives poor estimates. RANSAC (RANdom SAmple Consensus) iteratively:
1

Sample a minimal set

Randomly pick the minimum number of points needed to fit the model (e.g., 6 for DLT).
2

Fit the model

Compute the candidate parameter vector from the minimal sample.
3

Count inliers

Count how many of the remaining points agree with the model within a threshold ϵ\epsilon.
4

Keep the best model

Retain the hypothesis with the most inliers. After convergence, refit using all inliers.
The RANSAC line-fitting example below illustrates the principle directly:

MATLAB code examples

% Auto-generated by MATLAB cameraCalibrator app

% Define calibration images
imageFileNames = {
    '../images/calib_example/Image1.tif', ...
    '../images/calib_example/Image2.tif', ...
    % ... (15 images total)
    '../images/calib_example/Image15.tif', ...
};

% Detect checkerboard corners
[imagePoints, boardSize, imagesUsed] = detectCheckerboardPoints(imageFileNames);
imageFileNames = imageFileNames(imagesUsed);

% Generate 3D world coordinates (square size = 25 mm)
squareSize  = 25;
worldPoints = generateCheckerboardPoints(boardSize, squareSize);

% Estimate camera parameters (2 radial distortion coefficients)
[cameraParams, imagesUsed, estimationErrors] = estimateCameraParameters( ...
    imagePoints, worldPoints, ...
    'EstimateSkew', false, ...
    'EstimateTangentialDistortion', false, ...
    'NumRadialDistortionCoefficients', 2, ...
    'WorldUnits', 'mm');

% Visualise reprojection errors and extrinsics
figure; showReprojectionErrors(cameraParams, 'BarGraph');
figure; showExtrinsics(cameraParams, 'CameraCentric');
displayErrors(estimationErrors, cameraParams);

% Undistort an image using the calibrated parameters
originalImage   = imread(imageFileNames{1});
undistortedImage = undistortImage(originalImage, cameraParams);

Python resources

Camera calibration example (Colab)

Step-by-step calibration using a checkerboard pattern in Python with OpenCV.

E03 — Calibrate your project camera

In-class exercise: calibrate the camera you plan to use in your course project.

Video lecture

Lecture: Parameter estimation and camera calibration (2021)

Recorded class covering DLT, SVD-based estimation, and RANSAC applied to camera calibration.

Calibration workflow summary

In practice 10–20 images of a calibration pattern (checkerboard) taken from different angles and distances give reliable results. The MATLAB cameraCalibrator app and OpenCV’s calibration routine both accept multiple views.
After calibration the reprojection error is the average Euclidean distance between the observed image points and the points predicted by projecting the 3D world points through the estimated PP. A reprojection error below 1 pixel is considered good.
Wide-angle and fisheye lenses introduce significant radial distortion. For standard lenses in robotics or structure-from-motion, even small distortions compound over large scenes. Always estimate and correct distortion if metric accuracy matters.
Mislabelled or partially occluded corners act as outliers. RANSAC finds the largest set of consistent correspondences before refitting, giving an estimate close to the true parameters even when 30–50 % of matches are corrupted.

Build docs developers (and LLMs) love