Pinhole camera model

The pinhole camera is the standard model for most cameras used in SLAM. Light from a 3D point passes through a small hole (the optical centre) and projects onto an image plane. The model describes image projection: how a point in the 3D world maps to a 2D pixel.

Coordinate Systems

Four coordinate systems are involved:

  1. World frame Xw=[Xw,Yw,Zw]T\mathbf{X}_w = [X_w, Y_w, Z_w]^T: fixed reference frame.
  2. Camera frame Xc=[Xc,Yc,Zc]T\mathbf{X}_c = [X_c, Y_c, Z_c]^T: ZcZ_c is the optical axis (pointing forward).
  3. Image plane x=[x,y]T\mathbf{x}' = [x', y']^T: metric coordinates on the image plane.
  4. Pixel frame u=[u,v]T\mathbf{u} = [u, v]^T: discrete pixel coordinates.

The usual convention is XcX_c pointing right, YcY_c pointing down, ZcZ_c pointing forward — matching pixel coordinates uu (right) and vv (down) with the origin at the top-left corner of the image.

Projection Pipeline

Step 1: World to Camera. The rigid body transformation (extrinsic parameters) converts a world point to the camera frame:

Xc=RXw+t\mathbf{X}_c = R\mathbf{X}_w + \mathbf{t}

Step 2: Camera to Image Plane (perspective division).

x=XcZc,y=YcZcx' = \frac{X_c}{Z_c}, \qquad y' = \frac{Y_c}{Z_c}

This division by depth is the source of perspective effects — and of the nonlinearity that makes vision geometry interesting. The coordinates (x,y)(x', y') are called normalized coordinates: they are what remains of a pixel once the intrinsics are stripped off, and most geometric derivations (essential matrix, triangulation) are cleanest in this frame.

Step 3: Image Plane to Pixel (intrinsic parameters).

u=fxx+cx,v=fyy+cyu = f_x \cdot x' + c_x, \qquad v = f_y \cdot y' + c_y

where fx,fyf_x, f_y are focal lengths in pixels and (cx,cy)(c_x, c_y) is the principal point. The pixel focal length relates the physical focal length ff (in mm) to the pixel size: fx=f/(pixel width)f_x = f / (\text{pixel width}), which is why fxfyf_x \neq f_y when pixels are not square.

Matrix Form

Combining all steps in homogeneous coordinates:

Zc[uv1]=[fx0cx0fycy001]K[Rt][XwYwZw1]Z_c \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \underbrace{\begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix}}_{\mathbf{K}} \begin{bmatrix} R & \mathbf{t} \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix}

The matrix K\mathbf{K} is the camera intrinsic matrix, and P=K[Rt]P = \mathbf{K}[R|\mathbf{t}] is the 3×43 \times 4 camera projection matrix. In the general case K\mathbf{K} includes a skew parameter (usually zero for modern cameras).

A direct NumPy translation of the pipeline:

import numpy as np

def project(K, R, t, X_w):
    X_c = R @ X_w + t          # world -> camera
    x_n = X_c[:2] / X_c[2]     # perspective division (normalized coords)
    u   = K[0, 0] * x_n[0] + K[0, 2]
    v   = K[1, 1] * x_n[1] + K[1, 2]
    return np.array([u, v])

Back-projection: the inverse is a ray

Projection destroys one dimension: a pixel does not determine a 3D point, only a ray. Given pixel u\mathbf{u}, the ray of possible 3D points in the camera frame is

Xc(λ)=λK1[uv1],λ>0\mathbf{X}_c(\lambda) = \lambda\,\mathbf{K}^{-1}\begin{bmatrix}u\\v\\1\end{bmatrix}, \qquad \lambda > 0

Recovering the missing depth λ\lambda is exactly what triangulation (multiple views), stereo (a second calibrated camera), or a depth sensor provides. The field of view follows from the same geometry: FoVx=2arctan ⁣(W2fx)\mathrm{FoV}_x = 2\arctan\!\big(\tfrac{W}{2f_x}\big) for image width WW.

Common pitfalls

Why it matters for SLAM

The projection function π()\pi(\cdot) defined by this model is at the heart of every visual SLAM system: the reprojection error e=zπ(TX)\mathbf{e} = \mathbf{z} - \pi(T\mathbf{X}) minimized in bundle adjustment is nothing more than “project the map point with the pinhole model, compare against the measured pixel”. Triangulation, PnP, and epipolar geometry are all derived from the same equation, so deriving this pipeline from scratch is the single most valuable exercise at this level.