Foundations

Camera Intrinsic and Extrinsic Parameters

The parameters of the pinhole camera matrix P = K[R | t], split into intrinsics K, which map camera coordinates to pixels, and extrinsics R and t, which place the camera in the world.

intermediate

A camera’s parameters say how a point in the world becomes a pixel. They split into two groups: extrinsic parameters, which say where the camera is and which way it points, and intrinsic parameters, which describe the camera itself, turning a direction in front of the camera into a pixel position. Together they form the 3×43 \times 4 camera matrix P=K[R∣t]P = K[R \mid \mathbf{t}], which extends the pinhole camera model from a camera fixed at the origin with metric image coordinates to a real camera anywhere in the scene producing pixel coordinates.

Definition

The extrinsic parameters are a rotation RR and a translation t\mathbf{t} that transform a point from world coordinates to camera coordinates. The intrinsic parameters are the focal lengths in pixels fxf_x and fyf_y, the principal point (cx,cy)(c_x, c_y), and the skew ss, collected in the upper-triangular intrinsic matrix (or calibration matrix) KK. Lens distortion coefficients are usually counted as further intrinsic parameters. Intrinsics stay fixed as the camera moves, as long as the focus and zoom do not change; extrinsics change with every movement.

Intuition

Imaging a point happens in three steps. First, express the point relative to the camera: where it lies to the camera’s right, below it, and in front of it. That is a rigid motion, the extrinsics. Second, project it through the pinhole: divide by depth to get its direction, a 2D point on a plane one unit in front of the camera. Third, convert that direction into a pixel: scale by the focal length in pixels and shift so that the origin is at the image corner instead of the optical axis. That is the job of the intrinsics. Moving the camera changes only the first step; swapping the lens or resizing the image changes only the third.

Formal Definition

The camera matrix

With a world point X=(X,Y,Z)⊤\mathbf{X} = (X, Y, Z)^\top and its pixel (u,v)(u, v) in homogeneous coordinates,

λ[uv1]=K [ R∣t ][X1],K=[fxscx0fycy001],\lambda \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = K \,[\,R \mid \mathbf{t}\,] \begin{bmatrix} \mathbf{X} \\ 1 \end{bmatrix}, \qquad K = \begin{bmatrix} f_x & s & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix},

where λ\lambda is the depth of the point in the camera frame. This is the form used by Hartley and Zisserman (2004) and, without skew, by OpenCV’s calib3d module.

Extrinsics

The extrinsics map world coordinates to camera coordinates:

Xc=R X+t.\mathbf{X}_c = R\,\mathbf{X} + \mathbf{t}.

RR is a 3×33 \times 3 rotation matrix (R⊤R=IR^\top R = I, det⁡R=1\det R = 1) and t\mathbf{t} is the position of the world origin expressed in camera coordinates. It is not the camera’s position. The camera center C\mathbf{C} is the world point that maps to Xc=0\mathbf{X}_c = \mathbf{0}, so

C=−R⊤t,P=KR [ I∣−C ].\mathbf{C} = -R^\top \mathbf{t}, \qquad P = K R\,[\,I \mid -\mathbf{C}\,].

The rows of RR are the camera’s axes written in world coordinates; the third row is the direction of the optical axis. The homogeneous camera center (C,1)(\mathbf{C}, 1) is the null vector of PP: P (C,1)⊤=0P\,(\mathbf{C}, 1)^\top = \mathbf{0}, so the center is the one point whose image is undefined.

Intrinsics

In camera coordinates the projection to pixels is

u=fxXcZc+sYcZc+cx,v=fyYcZc+cy.u = f_x \frac{X_c}{Z_c} + s \frac{Y_c}{Z_c} + c_x, \qquad v = f_y \frac{Y_c}{Z_c} + c_y.
  • Focal lengths in pixels. A lens of focal length ff (in millimeters) on a sensor with pixel pitch px×pyp_x \times p_y (in millimeters per pixel) gives fx=f/pxf_x = f / p_x and fy=f/pyf_y = f / p_y. Hartley and Zisserman write the same thing as αx=fmx\alpha_x = f m_x, with mxm_x pixels per unit length. The ratio fy/fxf_y / f_x is the aspect ratio, 1 for square pixels.
  • Principal point. (cx,cy)(c_x, c_y) is the pixel where the optical axis meets the image. It is usually near the image center but rarely exactly at it.
  • Skew. ss is nonzero only if the pixel grid’s axes are not perpendicular. For modern sensors s=0s = 0, and many libraries, OpenCV included, omit it.

Normalized image coordinates

Applying K−1K^{-1} to a pixel removes the intrinsics:

x^=K−1[uv1]=[Xc/ZcYc/Zc1].\hat{\mathbf{x}} = K^{-1} \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \begin{bmatrix} X_c / Z_c \\ Y_c / Z_c \\ 1 \end{bmatrix}.

These normalized coordinates are what an ideal camera with f=1f = 1 and the principal point at the origin would measure, and x^\hat{\mathbf{x}} is the direction of the ray through the pixel in the camera frame. Methods that care only about rays, such as relative pose estimation with the essential matrix and triangulation, work in normalized coordinates once KK is known.

Distortion

Real lenses add lens distortion, which OpenCV models as a nonlinear function applied to the normalized coordinates (Xc/Zc,Yc/Zc)(X_c/Z_c, Y_c/Z_c) before KK: radial coefficients k1,…,k6k_1, \dots, k_6, tangential coefficients p1,p2p_1, p_2, thin-prism coefficients s1,…,s4s_1, \dots, s_4, and two sensor-tilt angles. Because these depend on the lens and not on the pose, they are intrinsic parameters, but they are not part of KK, and PP alone no longer describes the camera once they are nonzero.

Properties

Degrees of freedom

KK has 5 parameters (fx,fy,cx,cy,sf_x, f_y, c_x, c_y, s), RR has 3 (a rotation), and t\mathbf{t} has 3, for 11 in total. This matches PP: it has 12 entries but is defined only up to scale, leaving 11 (Hartley and Zisserman, 2004). Conversely, any 3×43 \times 4 matrix whose left 3×33 \times 3 block is invertible can be written, up to scale, as K[R∣t]K[R \mid \mathbf{t}], as the decomposition below shows.

Field of view

For an image WW pixels wide and HH high, with the principal point at the center,

θx=2arctan⁡ ⁣(W2fx),θy=2arctan⁡ ⁣(H2fy).\theta_x = 2 \arctan\!\left( \frac{W}{2 f_x} \right), \qquad \theta_y = 2 \arctan\!\left( \frac{H}{2 f_y} \right).

The field of view depends only on the ratio of image size to focal length, so it is unchanged by resizing.

Resizing and cropping

KK belongs to a specific image resolution. Scaling an image by a factor aa multiplies fxf_x, fyf_y, cxc_x, and cyc_y by aa, as the OpenCV documentation notes; non-uniform scaling multiplies the xx and yy entries by their own factors. This is exact if pixel centers lie at half-integer coordinates, so that the image spans [0,W][0, W]. If instead pixel centers lie at integers, as in OpenCV, the image spans [−12,W−12][-\tfrac{1}{2}, W - \tfrac{1}{2}], and a resize that maps the image’s outer edges onto each other sends xx to a (x+12)−12a\,(x + \tfrac{1}{2}) - \tfrac{1}{2}; the principal point then becomes c′=a (c+12)−12c' = a\,(c + \tfrac{1}{2}) - \tfrac{1}{2}, a sub-pixel difference that matters for precise work (see digital image for the two conventions). Cropping a window whose top-left corner is at (x0,y0)(x_0, y_0) leaves the focal lengths unchanged and shifts the principal point to (cx−x0, cy−y0)(c_x - x_0,\, c_y - y_0). Using a KK calibrated at one resolution on images of another is a common silent error.

Decomposing P

Given a camera matrix, for example one estimated from point correspondences, KK, RR, and t\mathbf{t} can be recovered. Write P=[ M∣p4 ]P = [\,M \mid \mathbf{p}_4\,] with M=KRM = KR. An RQ decomposition factors MM into an upper-triangular matrix times an orthogonal matrix, which is exactly the form KRKR (Hartley and Zisserman, 2004). The factorization is unique only up to the signs of the columns of KK and rows of RR, so the signs are fixed by requiring a positive diagonal in KK and det⁡R=+1\det R = +1, flipping the overall sign of PP if necessary, since PP and −P-P represent the same camera. Then t=K−1p4\mathbf{t} = K^{-1}\mathbf{p}_4, and dividing KK by its bottom-right entry restores the usual normalization. OpenCV provides decomposeProjectionMatrix, built on its RQDecomp3x3.

Conventions

The world-to-camera transform [R∣t][R \mid \mathbf{t}] used above is the convention of Hartley and Zisserman and of OpenCV, whose documentation describes RR and t\mathbf{t} as the change of coordinates from world to camera. Many robotics and graphics tools instead store the camera pose, the camera-to-world transform

X=Rwc Xc+twc,Rwc=R⊤,twc=C=−R⊤t,\mathbf{X} = R_{wc}\,\mathbf{X}_c + \mathbf{t}_{wc}, \qquad R_{wc} = R^\top, \quad \mathbf{t}_{wc} = \mathbf{C} = -R^\top \mathbf{t},

whose translation is the camera’s position. Both are 4×44 \times 4 rigid transforms and each is the inverse of the other. Confusing them produces cameras that sit in the wrong place and point the wrong way, often without any error message. Axis orientation is a separate convention: OpenCV’s camera frame has YY down and ZZ forward, while OpenGL’s camera looks along −Z-Z with YY up (see pinhole camera model). When combining data from different sources, check which transform direction and which axes each one uses.

Examples

The NumPy code below builds KK from a lens and sensor, sets up world-to-camera extrinsics, projects points, recovers the camera center, computes the field of view, shows how KK changes when the image is halved and cropped, and decomposes PP back into KK, RR, and t\mathbf{t}.

import numpy as np
from scipy.linalg import rq
from scipy.spatial.transform import Rotation

np.set_printoptions(precision=3, suppress=True)

# Intrinsics: 4 mm lens, 2 um pixels, 1280 x 960 image
f_mm, pitch_mm = 4.0, 0.002
fx = fy = f_mm / pitch_mm                       # 2000 px
K = np.array([[fx, 0.0, 640.0],
              [0.0, fy, 480.0],
              [0.0, 0.0, 1.0]])

# Extrinsics, world-to-camera: X_cam = R X_world + t
R = Rotation.from_euler("xyz", [5, -5, 2], degrees=True).as_matrix()
C_true = np.array([1.0, -0.5, -6.0])            # camera centre in the world
t = -R @ C_true
P = K @ np.hstack([R, t[:, None]])              # 3x4 camera matrix

def project(P, X):
    x = np.hstack([X, np.ones((len(X), 1))]) @ P.T
    return x[:, :2] / x[:, 2:]

X = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 1.0]])
print("pixels:\n", project(P, X))
print("depths Z_cam:", (X @ R.T + t)[:, 2])

# Camera centre: C = -R^T t, which is also the null vector of P
_, _, Vt = np.linalg.svd(P)
print("C = -R^T t:", -R.T @ t, " null(P):", Vt[-1, :3] / Vt[-1, 3])

# Field of view from f and the image width
print("horizontal FOV: %.2f deg" % np.degrees(2 * np.arctan(1280 / (2 * fx))))

# Halve the resolution, then crop a window starting at pixel (100, 50)
S = np.diag([0.5, 0.5, 1.0])
K_half = S @ K
K_crop = np.array([[1, 0, -100], [0, 1, -50], [0, 0, 1]]) @ K_half
print("K at 640 x 480:\n", K_half)
print("K after crop:\n", K_crop)
print("halved pixels match:", np.allclose(project(S @ P, X), 0.5 * project(P, X)))

def decompose(P):
    """Split P = K [R | t] using an RQ decomposition of its left 3x3 block."""
    K, R = rq(P[:, :3])
    D = np.diag(np.sign(np.diag(K)))            # force a positive diagonal on K
    K, R = K @ D, D @ R                         # D @ D = I, so K @ R is unchanged
    if np.linalg.det(R) < 0:                    # P is known only up to sign
        K, R = -K, -R
    t = np.linalg.solve(K, P[:, 3])
    return K / K[2, 2], R, t

for c in (1.0, -3.0):                           # any nonzero scale of P
    K_est, R_est, t_est = decompose(c * P)
    print(f"scale {c}: K =\n", np.round(K_est, 6) + 0.0)
    print("  R ok:", np.allclose(R_est, R), " t ok:", np.allclose(t_est, t))

Output:

pixels:
 [[125.966 453.64 ]
 [465.418 465.616]
 [169.255 716.709]]
depths Z_cam: [5.911 5.998 6.99 ]
C = -R^T t: [ 1.  -0.5 -6. ]  null(P): [ 1.  -0.5 -6. ]
horizontal FOV: 35.49 deg
K at 640 x 480:
 [[1000.    0.  320.]
 [   0. 1000.  240.]
 [   0.    0.    1.]]
K after crop:
 [[1000.    0.  220.]
 [   0. 1000.  190.]
 [   0.    0.    1.]]
halved pixels match: True
scale 1.0: K =
 [[2000.    0.  640.]
 [   0. 2000.  480.]
 [   0.    0.    1.]]
  R ok: True  t ok: True
scale -3.0: K =
 [[2000.    0.  640.]
 [   0. 2000.  480.]
 [   0.    0.    1.]]
  R ok: True  t ok: True

The translation t\mathbf{t} is not the camera position; −R⊤t-R^\top\mathbf{t} is, and it agrees with the null vector of PP. The decomposition recovers KK, RR, and t\mathbf{t} even from −3P-3P: the sign fix makes KK and RR come out the same, and dividing by KK’s bottom-right entry and solving with the unnormalized KK cancel the scale.

Common Misconceptions

  • “t\mathbf{t} is where the camera is.” In the world-to-camera convention, t\mathbf{t} is the world origin in camera coordinates. The camera center is −R⊤t-R^\top\mathbf{t}.
  • “The focal length on the lens is fxf_x.” fxf_x is in pixels and depends on the pixel pitch and on any resizing of the image. A 4 mm lens on 2 µm pixels gives 2000 pixels, which becomes 1000 if the image is downsampled by two.
  • “The principal point is the image center.” It is close to the center but usually offset by some pixels, and assuming the center is a frequent source of systematic error in metric work.
  • “KK is a property of the camera alone.” It is a property of the camera at a given focus, zoom, and image resolution. Refocusing, zooming, digital cropping, or resizing all change it.
  • “Every pose matrix means the same thing.” A 4×44 \times 4 matrix may be world-to-camera or camera-to-world, and its camera axes may follow the vision or the graphics convention. The matrix itself does not say which.

Where It Is Used

Intrinsics and extrinsics are estimated by camera calibration, which observes a known target, usually a planar pattern, in several views and fits all parameters, including lens distortion, by minimizing reprojection error. Once KK is known, two-view geometry can be reduced to the essential matrix in normalized coordinates, and points seen in several calibrated views can be triangulated. Perspective-n-Point estimates the extrinsics of a calibrated camera from known 3D points, which is how augmented reality anchors virtual content and how visual localization works. Structure from motion and SLAM estimate the extrinsics of every frame, and sometimes the intrinsics too, by bundle adjustment; filtering approaches such as the extended Kalman filter track camera pose over time. Multi-camera rigs on vehicles and robots store each camera’s intrinsics together with its extrinsics relative to the rig, and rendering pipelines use the same parameters to place a virtual camera.

Related

  • Lens Distortion

    The systematic deviation of a real lens from the pinhole model's straight-line projection, which bends straight lines in the image and is modeled, estimated, and removed as part of camera calibration.

  • Homogeneous Coordinates

    Representing a point by a vector with one extra coordinate, defined only up to scale, so that translations, perspective projections, and points at infinity can all be handled with matrix multiplication.

  • Projective Geometry

    The geometry of properties that survive perspective projection, such as straightness, incidence, and the cross-ratio, which underlies how computer vision models cameras and relates images.

  • Digital Image

    A digital image is a grid of samples of a continuous scene, each quantized to a finite set of values and stored as an array of numbers.

References

  1. Hartley, R. & Zisserman, A. (2004). Multiple View Geometry in Computer Vision (2nd ed.). Cambridge University Press.
  2. Szeliski, R. (2022). Computer Vision: Algorithms and Applications (2nd ed.). Springer.
  3. OpenCV. Camera Calibration and 3D Reconstruction (calib3d module documentation), OpenCV 4.x.