Camera Matrix

Camera Matrix (Projection Matrix)

Overview of the camera model

The process of projecting a point in 3D space onto a 2D image is expressed as a chain of several coordinate transformations.

World coords Camera coords Normalized Pixel Camera projection model
$\mathbf{P}_w$
$[R\,|\,\mathbf{t}]$
$\mathbf{P}_c$
$(x, y)$
$\div Z_c$
$K$
$(u, v)$
\[\begin{gathered}\mathbf{P}_w=(X,Y,Z,1)^{\top}\\ \mathbf{P}_c = [R\,|\,\mathbf{t}]\,\mathbf{P}_w=(X_c,Y_c,Z_c)^{\top}\\ (x,y)=(X_c/Z_c,\ Y_c/Z_c),\quad (u,v,1)^{\top}=K\,(x,y,1)^{\top}\\ \lambda\,(u,v,1)^{\top} = K[R\,|\,\mathbf{t}]\,\mathbf{P}_w = P\,\mathbf{P}_w\quad(\lambda=Z_c)\end{gathered}\]
$\lambda$: scale factor (= the depth $Z_c$)
$K$: intrinsic matrix (3×3) — camera-specific
$[R\,|\,\mathbf{t}]$: extrinsic parameters (3×4) — camera position & orientation
$P = K[R\,|\,\mathbf{t}]$: camera matrix (3×4)
Figure 1. Coordinate-transformation pipeline of the camera model.

Coordinate-system conventions (used throughout this article)

  • World coordinate system: right-handed with $Y$ pointing up (the absolute reference for 3D space).
  • Camera coordinate system: right-handed, following the OpenCV convention, with $X_c$ to the right, $Y_c$ down, and $Z_c$ forward (= the optical axis = the viewing/depth direction).

$Y_c$ is taken pointing down because the image pixel coordinates $(u, v)$ have their origin at the top-left with $v$ pointing down. This way the mapping $(x, y) \to (u, v)$ through the intrinsic matrix $K$ connects naturally without any sign flips. Points in front of the camera have depth $Z_c > 0$.

Intrinsic matrix K

The intrinsic parameters describe characteristics that are specific to the camera itself.

Intrinsic matrix

$$K = \begin{pmatrix} f_x & s & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix}$$
  • $f_x, f_y$: focal length in pixel units. $f_x = f/p_x$, $f_y = f/p_y$ (where $p_x, p_y$ are the pixel sizes)
  • $(c_x, c_y)$: the principal point (intersection of the optical axis and the image plane)
  • $s$: skew coefficient (non-orthogonality between the pixel $u$ and $v$ axes; $s=0$ for almost all cameras, and $s \neq 0$ only rarely, e.g. for some scanned images)
Image sensor Meaning of the parameters (for square pixels) Typical: 500-2000 pixel
$(c_x, c_y)$
$p_x$
$p_y$
$u$
$v$
$f_x = \dfrac{f}{p_x}$ : horizontal focal length
$f_y = \dfrac{f}{p_y}$ : vertical focal length
usually $f_x \approx f_y$
Figure 2. The intrinsic matrix K and the image sensor.

Example: a typical webcam

For a resolution of 640×480, a sensor size of 3.6mm×2.7mm, and a focal length of f=4mm:

  • $p_x = 3.6/640 = 0.005625$ mm/pixel
  • $f_x = 4/0.005625 \approx 711$ pixel
  • principal point: $(c_x, c_y) \approx (320, 240)$ (ideally the image center)

Extrinsic parameters [R|t]

The extrinsic parameters describe the position and orientation of the camera relative to the world coordinate system.

World origin Camera
$X_w$
$Y_w$
$Z_w$
$X_c$
$Y_c$
$Z_c$ (optical axis)
Coordinate transform $[R\,|\,\mathbf{t}]$
$\mathbf{P}_c = R\,\mathbf{P}_w + \mathbf{t}$
$R$: rotation matrix (3×3)
$\mathbf{t}$: translation vector (3×1)
Figure 3. World-to-camera transformation by the extrinsic parameters $[R\,|\,\mathbf{t}]$. The camera coordinate system is rotated and translated relative to the world (the tilted axes are the effect of the rotation; $[R\,|\,\mathbf{t}]$ maps world coordinates to camera coordinates, whereas the camera's own pose in the world is the inverse: rotation $R^{\top}$ and position $\mathbf{C}=-R^{\top}\mathbf{t}$). The camera sits away from the world origin, with its back to the origin and facing forward along the optical axis $Z_c$. $Z_c$ is the camera's viewing direction (depth direction), in a right-handed system with $X_c$ right and $Y_c$ down (OpenCV convention). The orange dashed curve conceptually represents the coordinate transform $[R\,|\,\mathbf{t}]$ from world to camera coordinates (it is not a spatial displacement vector). The camera origin's world coordinates (the camera position) are $\mathbf{C}=-R^{\top}\mathbf{t}$, while the translation $\mathbf{t}$ itself is the world origin expressed in camera coordinates, a different quantity from $\mathbf{C}$.

Why do $Z_w$ and $Z_c$ point in opposite directions? This is two sides of the same coin as taking the camera's $Y_c$ pointing down. To flip $Y$ from up (world) to down (camera) while staying right-handed, $Z$ must be flipped along with it (flipping $Y$ alone would produce a left-handed system). In other words, $Y_c = -Y_w$ and $Z_c = -Z_w$ occur together as a 180° rotation about the $X$ axis. Put another way, "$Z_c$ pointing forward (into the scene)" and "$Y_c$ pointing down" are a matched set in the OpenCV convention. Under the OpenGL convention ($Y$ up, camera looking along $-Z$), $+Z_c$ points toward the viewer, i.e., the same direction as $Z_w$.

Major coordinate-system conventions (OpenCV, OpenGL, DirectX/Unity)

This article's convention of $X_c$ right, $Y_c$ down, $Z_c$ forward is the OpenCV convention. Other major conventions exist, and which is the majority or minority changes from field to field. The handedness (right- or left-handed) also differs between conventions.

Convention$X$$Y$Direction of $+Z$HandednessFields where it dominatesRepresentative examples
OpenCVrightdownforward = optical axis (into the scene; points in front have $Z_c > 0$)right-handedComputer vision / SLAM / SfM / robot visionOpenCV, COLMAP, most SLAM/SfM, ROS camera optical frame
OpenGLrightupbackward (toward the viewer; optical axis is $-Z$)right-handedCG / renderingOpenGL / WebGL, Three.js, most renderers
DirectX / Unityrightupforward = optical axis (into the scene)left-handedSome CG / game enginesDirect3D, Unity

$X$ right, $Y$ up, $Z$ into the scene (forward) is left-handed, which is the DirectX / Unity convention. Starting from the OpenCV convention ($Y$ down) and flipping only $Y$ to point up flips a single axis, turning right-handed into left-handed and yielding this convention (Unreal is also left-handed, but assigns the axes differently, with $X$ forward and $Z$ up). By contrast, the OpenCV and OpenGL conventions are related by flipping $Y$ and $Z$ together (a 180° rotation about the $X$ axis), so both are right-handed. Because this article is in a computer-vision context, it adopts the OpenCV convention.

Extrinsic matrix

$$[R | \mathbf{t}] = \begin{pmatrix} r_{11} & r_{12} & r_{13} & t_x \\ r_{21} & r_{22} & r_{23} & t_y \\ r_{31} & r_{32} & r_{33} & t_z \end{pmatrix}$$

Transforming a world-coordinate point $\mathbf{P}_w = (X, Y, Z, 1)^T$ into camera coordinates:

$$\mathbf{P}_c = R \mathbf{P}_w + \mathbf{t}$$

Note: $\mathbf{t}$ is not the camera position in the world coordinate system; it is the position of the world origin expressed in the camera coordinate system. The camera's world coordinates are $\mathbf{C} = -R^T \mathbf{t}$.

Even for the same $[R|\mathbf{t}]$, the picture looks different depending on which coordinate system you hold fixed while viewing it. Figure 3 was world-anchored (hold the world fixed and draw the camera's pose within it; this is the view for tasks that deal with camera pose and trajectory). Conversely, if you fix the camera at the origin, then the world coordinate system is placed by rotating and translating it via $[R|\mathbf{t}]$. This is exactly the projection equation $\mathbf{P}_c = R\,\mathbf{P}_w + \mathbf{t}$ (world→camera), which corresponds to the view matrix in rendering.

Camera (fixed) World coordinate system
$\mathbf{P}_c = R\,\mathbf{P}_w + \mathbf{t}$
Fix the camera → the world is placed by $[R\,|\,\mathbf{t}]$
Coordinate transform $[R\,|\,\mathbf{t}]$
$X_c$
$Y_c$
$Z_c$ (optical axis)
$X_w$
$Y_w$
$Z_w$
Figure 4. The same $[R\,|\,\mathbf{t}]$ viewed from the "camera frame" (the reverse of Figure 3: here the camera side is held fixed instead). Fixing the camera at the origin places the world coordinate system by the rotation $R$ and translation $\mathbf{t}$ (the world origin sits at position $\mathbf{t}$ in the camera frame). This is exactly the projection equation $\mathbf{P}_c = R\,\mathbf{P}_w + \mathbf{t}$ (world→camera), which corresponds to the view matrix in rendering. The camera's pose (camera→world) is obtained by the inverse transform $R^{\top},\ \mathbf{C}=-R^{\top}\mathbf{t}$.

Camera matrix P

The camera matrix (projection matrix) is the combination of the intrinsic and extrinsic parameters.

Camera matrix (projection matrix)

$$P = K [R | \mathbf{t}]$$

$P$ is a $3 \times 4$ matrix with 11 degrees of freedom (5 intrinsic + 6 extrinsic).

Projection equation

$$\lambda \begin{pmatrix} u \\ v \\ 1 \end{pmatrix} = P \begin{pmatrix} X \\ Y \\ Z \\ 1 \end{pmatrix}$$

$\lambda = Z_c$ (the depth in the camera coordinate system) is the scale factor.

Expanding:

$$u = \dfrac{p_{11}X + p_{12}Y + p_{13}Z + p_{14}}{p_{31}X + p_{32}Y + p_{33}Z + p_{34}}$$ $$v = \dfrac{p_{21}X + p_{22}Y + p_{23}Z + p_{24}}{p_{31}X + p_{32}Y + p_{33}Z + p_{34}}$$

Common points of confusion (pitfalls)

The same symbol or name often refers to different things in different sources, and this is one of the biggest factors that make learning computer vision hard. Online sources appear to contradict one another mostly because many of them do not state which convention they adopt. First, the names themselves clash across fields:

NameMeaning in one contextMeaning in another context
camera matrixOpenCV: the intrinsic matrix $K$ (3×3). The function argument is even named cameraMatrixHartley–Zisserman (MVG): the projection matrix $P = K[R\,|\,\mathbf{t}]$ (3×4)
projection matrixCV: $P = K[R\,|\,\mathbf{t}]$ (3×4, 3D→2D)OpenGL: the perspective projection matrix (4×4, view frustum → clip space), a different thing
extrinsic parameters / view matrixthe world→camera $[R\,|\,\mathbf{t}]$sometimes means "camera pose" = camera→world (the inverse transform)

Because the same name can refer to different matrices, it is safest to first check the definition used in that particular source. Next come mismatches in how equations are written and in conventions:

  • Row vectors vs. column vectors: the column-vector convention $\mathbf{p}' = R\,\mathbf{p}$ (this article, mathematics, OpenCV) or the row-vector convention $\mathbf{p}' = \mathbf{p}\,R$ (DirectX, etc.)? A transpose swaps the whole equation. On top of that, whether the matrix memory layout is row-major (C / NumPy) or column-major (OpenGL / Eigen default) is yet another separate issue.
  • Direction of the transform: world→camera ($R,\ \mathbf{t}$) or camera→world ($R^{\top},\ \mathbf{C} = -R^{\top}\mathbf{t}$)? The same "extrinsic parameters" can refer to opposite directions (Figures 3 and 4).
  • Active vs. passive rotation: rotating the point (active) or rotating the coordinate system (passive)? $R$ and $R^{\top}$ swap.
  • Handedness and axes of the coordinate system: right- / left-handed, $Y$ up / down, $Z$ forward / backward (the comparison table above).
  • Image coordinates: origin at the top-left (CV) or the bottom-left (OpenGL)? Is the pixel center at integer coordinates or at $+0.5$?
  • Representation of rotation: the rotation order of Euler angles; whether a quaternion has $w$ first or last; Hamilton / JPL convention.

Summary of coordinate systems

World coord. system absolute 3D space Camera coord. system camera center at origin Normalized image coords virtual plane at focal length 1 Pixel coordinates Order of transforms: Degrees of freedom: extrinsic: 6 (3 rot + 3 trans) total: 11
$(X, Y, Z)$
$[R\,|\,\mathbf{t}]$
$(X_c, Y_c, Z_c)$
$\div Z_c$
$(x, y) = (X_c/Z_c,\; Y_c/Z_c)$
$K$
$(u, v)$
1. rigid transform $[R\,|\,\mathbf{t}]$
2. projection $(\div Z)$
3. intrinsic matrix $K$
intrinsic: 5 $(f_x, f_y, c_x, c_y, s)$
Figure 5. Relationship between the world, camera, image, and pixel coordinate systems.

Code example (Python)

import numpy as np

# Intrinsic parameters
fx, fy = 800, 800
cx, cy = 320, 240
K = np.array([
    [fx,  0, cx],
    [ 0, fy, cy],
    [ 0,  0,  1]
])

# Extrinsic parameters (e.g. rotate 45 deg about the Z axis, move to (1,0,5))
theta = np.radians(45)
R = np.array([
    [np.cos(theta), -np.sin(theta), 0],
    [np.sin(theta),  np.cos(theta), 0],
    [0,              0,             1]
])
t = np.array([[1], [0], [5]])

# Extrinsic matrix [R|t]
Rt = np.hstack([R, t])

# Camera matrix P = K[R|t]
P = K @ Rt
print("Camera Matrix P:")
print(P)

# Project a 3D point
P_world = np.array([0, 0, 0, 1])  # world origin
p_homogeneous = P @ P_world
p = p_homogeneous[:2] / p_homogeneous[2]
print(f"Projected point: ({p[0]:.1f}, {p[1]:.1f})")

# Camera position (world coordinates)
camera_position = -R.T @ t.flatten()
print(f"Camera position: {camera_position}")

# Using OpenCV
import cv2
# When using cv2.projectPoints()
rvec, _ = cv2.Rodrigues(R)  # rotation matrix -> rotation vector
tvec = t  # tvec is not the camera position; it is the world origin expressed in the camera frame (= t)
points_3d = np.array([[0, 0, 0]], dtype=np.float32)
points_2d, _ = cv2.projectPoints(points_3d, rvec, tvec, K, None)

Summary

  • The camera matrix $P = K[R|\mathbf{t}]$ is a $3 \times 4$ matrix representing the 3D→2D projection.
  • Intrinsic parameters $K$: focal length and principal point (camera-specific).
  • Extrinsic parameters $[R|\mathbf{t}]$: the camera's position and orientation.
  • The projection can be expressed linearly in homogeneous coordinates.
  • The intrinsic matrix $K$ is found by camera calibration (covered in a later chapter); to begin with, check your understanding with the exercises in the next chapter.