Skip to main content

vision_calibration_core/
view.rs

1//! View-related structures and functions.
2
3use crate::{CorrespondenceView, Error};
4use serde::{Deserialize, Serialize};
5
6/// Single-camera observation view with attached metadata.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct View<Meta> {
9    /// 2D-3D correspondences observed in this view.
10    pub obs: CorrespondenceView,
11    /// Arbitrary metadata payload associated with the view.
12    pub meta: Meta,
13}
14
15impl<Meta> View<Meta> {
16    /// Number of observations in this view.
17    pub fn num_observations(&self) -> usize {
18        self.obs.points_3d.len()
19    }
20
21    /// Create a view from correspondences and metadata.
22    pub fn new(obs: CorrespondenceView, meta: Meta) -> Self {
23        Self { obs, meta }
24    }
25}
26
27impl View<NoMeta> {
28    /// Create a view without metadata.
29    pub fn without_meta(obs: CorrespondenceView) -> Self {
30        Self { obs, meta: NoMeta }
31    }
32}
33
34/// Multi-camera observations for one rig view/frame.
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct RigViewObs {
37    /// Per-camera observation; None if camera didn't observe in this view.
38    pub cameras: Vec<Option<CorrespondenceView>>,
39}
40
41/// One time-synchronized rig frame containing per-camera observations and metadata.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct RigView<Meta> {
44    /// Per-camera observations for this rig frame.
45    pub obs: RigViewObs,
46    /// Arbitrary metadata payload associated with the view.
47    pub meta: Meta,
48}
49
50/// Multi-view dataset for a camera rig.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct RigDataset<Meta> {
53    /// Number of cameras in the rig.
54    pub num_cameras: usize,
55    /// Sequence of rig views.
56    pub views: Vec<RigView<Meta>>,
57}
58
59impl<Meta> RigDataset<Meta> {
60    /// Number of views.
61    pub fn num_views(&self) -> usize {
62        self.views.len()
63    }
64
65    /// Construct a rig dataset and validate per-view camera counts.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`Error::InvalidInput`] if `views` is empty or any view has the
70    /// wrong number of cameras.
71    pub fn new(views: Vec<RigView<Meta>>, num_cameras: usize) -> Result<Self, Error> {
72        if views.is_empty() {
73            return Err(Error::invalid_input("need at least one view"));
74        }
75        for (idx, view) in views.iter().enumerate() {
76            if view.obs.cameras.len() != num_cameras {
77                return Err(Error::invalid_input(format!(
78                    "view {} has {} cameras, expected {}",
79                    idx,
80                    view.obs.cameras.len(),
81                    num_cameras
82                )));
83            }
84        }
85        Ok(Self { num_cameras, views })
86    }
87}
88
89/// Empty metadata marker for views that do not need extra metadata.
90#[derive(Debug, Clone, Default, Serialize, Deserialize)]
91pub struct NoMeta;
92
93/// A planar dataset consisting of multiple views.
94///
95/// Each view observes a planar calibration target in pixel coordinates.
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct PlanarDataset {
98    /// Sequence of planar calibration views.
99    pub views: Vec<View<NoMeta>>,
100}
101
102impl PlanarDataset {
103    /// Construct a planar dataset and validate basic cardinality constraints.
104    ///
105    /// # Errors
106    ///
107    /// Returns [`Error::InsufficientData`] if `views` is empty or any view has
108    /// fewer than 4 point correspondences.
109    pub fn new(views: Vec<View<NoMeta>>) -> Result<Self, Error> {
110        if views.is_empty() {
111            return Err(Error::InsufficientData { need: 1, got: 0 });
112        }
113        for (i, view) in views.iter().enumerate() {
114            if view.obs.len() < 4 {
115                return Err(Error::invalid_input(format!(
116                    "view {} has too few points (need >=4, got {})",
117                    i,
118                    view.obs.len()
119                )));
120            }
121        }
122        Ok(Self { views })
123    }
124
125    /// Number of planar views.
126    pub fn num_views(&self) -> usize {
127        self.views.len()
128    }
129}