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}\tilde{\mathbf{P}}_w=(X,Y,Z,1)^{\top}\\ \mathbf{P}_c = [R\,|\,\mathbf{t}]\,\tilde{\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}]\,\tilde{\mathbf{P}}_w = P\,\tilde{\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 reference frame chosen for the 3D scene).
  • 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). The depth $Z_c$ is the coordinate along the optical axis, which in general differs from the distance $\sqrt{X_c^2+Y_c^2+Z_c^2}$ from the camera center to the point.

$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$ can be assumed for almost all cameras)
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 and faces forward along the optical axis $Z_c$ (in this figure its orientation is drawn independently of the origin). $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. Keeping the $X$ axes aligned, 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$. For a general pose $R$, the relation between the world and camera axes is set by the camera pose.

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)^T$ (in homogeneous coordinates $\tilde{\mathbf{P}}_w = (X, Y, Z, 1)^T$) into camera coordinates:

$$\mathbf{P}_c = R \mathbf{P}_w + \mathbf{t} = [R\,|\,\mathbf{t}]\,\tilde{\mathbf{P}}_w$$

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 coordinate-transform equation $\mathbf{P}_c = R\,\mathbf{P}_w + \mathbf{t}$ (world→camera), which corresponds to the view matrix in rendering (the projection to 2D is the later step of dividing by $Z_c$).

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 coordinate-transform 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 up to an overall scale (5 intrinsic + 6 extrinsic; 10 if the skew is fixed at $s = 0$).

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. We consider points in front of the camera ($Z_c > 0$); the identity holds for this article's $K$ (with 1 in the bottom-right entry) and $P = K[R|\mathbf{t}]$ (scaling $P$ by a constant scales $\lambda$ by the same 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}'^{\top} = \mathbf{p}^{\top} R^{\top}$ (writing the same transform with row vectors transposes the matrix too; 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 draw the same world→camera transform with a different side held fixed; they are not inverses of each other.)
  • 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 reference 3D frame Camera coord. system camera center at origin Normalized image coords virtual plane at Z_c = 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]
], dtype=float)

# Extrinsic parameters (a world→camera example: R = 45 deg rotation about Z, t = (1, 0, 5); the camera position is -R^T t, not t)
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], [0.0], [5.0]])

# 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], [1, 0, 0]], dtype=np.float32)  # the origin and a point that shows the rotation
points_2d, _ = cv2.projectPoints(points_3d, rvec, tvec, K, None)
print(points_2d.reshape(-1, 2))  # [[480. 240.] [593.1 353.1]] (the origin matches the matrix computation above)

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.
  • In homogeneous coordinates the projection is a matrix product followed by division by the third component.
  • The intrinsic matrix $K$ is found by camera calibration (covered in a later chapter); the exercises chapter is in preparation.

Frequently Asked Questions

What does each element of the intrinsic matrix K mean?

The intrinsic matrix $K$ has the form $\begin{pmatrix} f_x & s & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix}$. Here $f_x, f_y$ are the focal length in pixel units, $(c_x, c_y)$ are the image coordinates of the principal point (the intersection of the optical axis and the image plane), and $s$ is the skew (0 when the pixel axes are orthogonal, which can be assumed for almost all cameras). For cameras with non-square pixels, $f_x \neq f_y$.

What is the difference between the extrinsic parameters [R|t] and the intrinsic matrix K?

The extrinsic parameters $[R|\mathbf{t}]$ describe the camera's pose (its position and orientation relative to the world coordinate system) and change every time the camera moves. The intrinsic matrix $K$ describes the camera's optical characteristics (focal length, principal point, etc.) and can be treated as constant as long as the zoom, focus, image resolution and cropping are unchanged. Once $K$ is determined by calibration under those conditions, only the extrinsic parameters need to be estimated afterward.