Skip to main content

vision_calibration_core/models/
camera.rs

1use nalgebra::{Point2, Point3, RealField, Vector3};
2use serde::{Deserialize, Serialize};
3
4use super::{DistortionModel, IntrinsicsModel, ProjectionModel, SensorModel};
5
6/// A camera ray represented by its intersection with the z = 1 plane.
7#[derive(Clone, Copy, Debug)]
8pub struct Ray<S: RealField + Copy> {
9    /// Point on the z = 1 plane in camera coordinates.
10    pub point: Vector3<S>,
11}
12
13/// A composable camera model: projection -> distortion -> sensor -> intrinsics.
14#[derive(Clone, Debug, Serialize, Deserialize)]
15pub struct Camera<S, P, D, Sm, K>
16where
17    S: RealField + Copy,
18    P: ProjectionModel<S>,
19    D: DistortionModel<S>,
20    Sm: SensorModel<S>,
21    K: IntrinsicsModel<S>,
22{
23    /// Projection model (e.g. pinhole).
24    pub proj: P,
25    /// Distortion model (e.g. Brown-Conrady).
26    pub dist: D,
27    /// Sensor model (e.g. identity or tilted homography).
28    pub sensor: Sm,
29    /// Intrinsics model (K).
30    pub k: K,
31    _phantom: core::marker::PhantomData<S>,
32}
33
34impl<S, P, D, Sm, K> Camera<S, P, D, Sm, K>
35where
36    S: RealField + Copy,
37    P: ProjectionModel<S>,
38    D: DistortionModel<S>,
39    Sm: SensorModel<S>,
40    K: IntrinsicsModel<S>,
41{
42    /// Build a camera from its component models.
43    pub fn new(proj: P, dist: D, sensor: Sm, k: K) -> Self {
44        Self {
45            proj,
46            dist,
47            sensor,
48            k,
49            _phantom: core::marker::PhantomData,
50        }
51    }
52
53    /// Project a 3D point in camera coordinates into pixel coordinates.
54    ///
55    /// Returns `None` if the point is behind the camera or not projectable.
56    pub fn project_point_c(&self, p_c: &Vector3<S>) -> Option<Point2<S>> {
57        if p_c.z <= S::zero() {
58            return None;
59        }
60        let dir = *p_c;
61        let n_u = self.proj.project_dir(&dir)?;
62        let n_d = self.dist.distort(&n_u);
63        let s = self.sensor.normalized_to_sensor(&n_d);
64        Some(self.k.sensor_to_pixel(&s))
65    }
66
67    /// Project a 3D point in camera coordinates into pixel coordinates.
68    ///
69    /// This is a convenience wrapper around `project_point_c`.
70    pub fn project_point(&self, p_c: &Point3<S>) -> Option<Point2<S>> {
71        self.project_point_c(&p_c.coords)
72    }
73
74    /// Backproject a pixel to a point on the z = 1 plane in camera coordinates.
75    pub fn backproject_pixel(&self, px: &Point2<S>) -> Ray<S> {
76        let s = self.k.pixel_to_sensor(px);
77        let n_d = self.sensor.sensor_to_normalized(&s);
78        let n_u = self.dist.undistort(&n_d);
79        let dir = self.proj.unproject_dir(&n_u);
80        debug_assert!(dir.z != S::zero());
81        let point = dir / dir.z;
82        Ray { point }
83    }
84}