Composable Camera Pipeline

A camera model maps 3D points in the camera frame to 2D pixel coordinates. In calibration-rs, this mapping is decomposed into four composable stages, each implemented as a trait:

where:

  • projection: maps a 3D direction to normalized coordinates on the image plane
  • distortion: warps normalized coordinates to model lens imperfections
  • sensor: applies a homography for tilted sensor planes (identity for standard cameras)
  • intrinsics: scales and translates to pixel coordinates

The Camera Struct

The camera is a generic struct parameterized over the four model traits:

#![allow(unused)]
fn main() {
pub struct Camera<S, P, D, Sm, K>
where
    S: RealField,
    P: ProjectionModel<S>,
    D: DistortionModel<S>,
    Sm: SensorModel<S>,
    K: IntrinsicsModel<S>,
{
    pub proj: P,
    pub dist: D,
    pub sensor: Sm,
    pub k: K,
}
}

The scalar type S is generic over RealField, enabling the same camera to work with f64 for evaluation and with dual numbers for automatic differentiation.

Forward Projection

Given a 3D point in the camera frame, projection to pixel coordinates proceeds through the four stages:

#![allow(unused)]
fn main() {
pub fn project_point_c(&self, p_c: &Vector3<S>) -> Option<Point2<S>>
}
  1. Projection: Compute normalized coordinates . For pinhole: . Returns None if the point is behind the camera ().

  2. Distortion: Apply lens distortion . Warps normalized coordinates according to the distortion model (e.g., Brown-Conrady radial + tangential).

  3. Sensor transform: Apply sensor model . For standard cameras this is identity; for tilted sensors it applies a homography.

  4. Intrinsics: Map to pixels . Applies focal lengths, principal point, and optional skew.

Inverse (Back-Projection)

The inverse maps a pixel to a ray in the camera frame:

#![allow(unused)]
fn main() {
pub fn backproject_pixel(&self, px: &Point2<S>) -> Ray<S>
}
  1. — pixel to sensor coordinates
  2. — sensor to distorted normalized
  3. — undistort (iterative)
  4. — normalized to 3D direction

The result is a point on the plane in the camera frame, defining a ray from the camera center.

Common Type Aliases

For the most common configuration (pinhole + Brown-Conrady + no tilt):

#![allow(unused)]
fn main() {
type PinholeCamera = Camera<f64, Pinhole, BrownConrady5<f64>,
                            IdentitySensor, FxFyCxCySkew<f64>>;
}

The make_pinhole_camera(k, dist) convenience function constructs this type.

Projecting World Points

To project a world point through a posed camera, first transform from world to camera frame using the extrinsic pose :

Then apply the camera projection:

Trait Composition

Each stage is an independent trait. This design allows mixing and matching:

ProjectionDistortionSensorIntrinsics
PinholeNoDistortionIdentitySensorFxFyCxCySkew
PinholeBrownConrady5IdentitySensorFxFyCxCySkew
PinholeBrownConrady5HomographySensorFxFyCxCySkew

Note: ScheimpflugParams stores the tilt angles and has a compile() method that produces a HomographySensor suitable for the Camera struct. See the Sensor Models chapter.

The subsequent sections detail each stage.