vision_calibration_core/models/
params.rs1use nalgebra::Matrix3;
2use serde::{Deserialize, Serialize};
3
4use super::{
5 BrownConrady5, Camera, FxFyCxCySkew, HomographySensor, IdentitySensor, NoDistortion, Pinhole,
6 ProjectionModel, ScheimpflugParams, SensorModel,
7};
8use crate::Real;
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
12#[serde(tag = "type", rename_all = "snake_case")]
13pub enum ProjectionParams {
14 Pinhole,
16}
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
20#[serde(tag = "type", rename_all = "snake_case")]
21pub enum DistortionParams {
22 None,
24 BrownConrady5 {
26 #[serde(flatten)]
28 params: BrownConrady5<Real>,
29 },
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
34#[serde(tag = "type", rename_all = "snake_case")]
35pub enum SensorParams {
36 Identity,
38 Homography {
40 h: [[Real; 3]; 3],
42 },
43 Scheimpflug {
45 #[serde(flatten)]
47 params: ScheimpflugParams,
48 },
49}
50
51#[derive(Clone, Debug, Serialize, Deserialize)]
53#[serde(tag = "type", rename_all = "snake_case")]
54pub enum IntrinsicsParams {
55 FxFyCxCySkew {
57 #[serde(flatten)]
59 params: FxFyCxCySkew<Real>,
60 },
61}
62
63#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct CameraParams {
66 pub projection: ProjectionParams,
68 pub distortion: DistortionParams,
70 pub sensor: SensorParams,
72 pub intrinsics: IntrinsicsParams,
74}
75
76pub type CameraModel = Camera<Real, AnyProjection, AnyDistortion, AnySensor, AnyIntrinsics>;
78
79impl CameraParams {
80 pub fn build(&self) -> CameraModel {
84 let proj = match self.projection {
85 ProjectionParams::Pinhole => AnyProjection::Pinhole(Pinhole),
86 };
87
88 let dist = match self.distortion {
89 DistortionParams::None => AnyDistortion::None(NoDistortion),
90 DistortionParams::BrownConrady5 { params } => AnyDistortion::BrownConrady5(params),
91 };
92
93 let sensor = match &self.sensor {
94 SensorParams::Identity => AnySensor::Identity(IdentitySensor),
95 SensorParams::Homography { h } => {
96 let h = Matrix3::from_row_slice(&[
97 h[0][0], h[0][1], h[0][2], h[1][0], h[1][1], h[1][2], h[2][0], h[2][1], h[2][2],
98 ]);
99 let h_inv = h.try_inverse().expect("Homography not invertible");
100 AnySensor::Homography(HomographySensor { h, h_inv })
101 }
102 SensorParams::Scheimpflug { params } => AnySensor::Homography(params.compile()),
103 };
104
105 let k = match self.intrinsics {
106 IntrinsicsParams::FxFyCxCySkew { params } => AnyIntrinsics::FxFyCxCySkew(params),
107 };
108
109 Camera::new(proj, dist, sensor, k)
110 }
111}
112
113#[derive(Clone, Debug)]
116#[doc(hidden)]
117pub enum AnyProjection {
118 Pinhole(Pinhole),
119}
120
121impl ProjectionModel<Real> for AnyProjection {
122 fn project_dir(&self, dir_c: &nalgebra::Vector3<Real>) -> Option<nalgebra::Point2<Real>> {
123 match self {
124 AnyProjection::Pinhole(m) => m.project_dir(dir_c),
125 }
126 }
127
128 fn unproject_dir(&self, n: &nalgebra::Point2<Real>) -> nalgebra::Vector3<Real> {
129 match self {
130 AnyProjection::Pinhole(m) => m.unproject_dir(n),
131 }
132 }
133}
134
135#[derive(Clone, Debug)]
136#[doc(hidden)]
137pub enum AnyDistortion {
138 None(NoDistortion),
139 BrownConrady5(BrownConrady5<Real>),
140}
141
142impl super::DistortionModel<Real> for AnyDistortion {
143 fn distort(&self, n: &nalgebra::Point2<Real>) -> nalgebra::Point2<Real> {
144 match self {
145 AnyDistortion::None(m) => m.distort(n),
146 AnyDistortion::BrownConrady5(m) => m.distort(n),
147 }
148 }
149
150 fn undistort(&self, n: &nalgebra::Point2<Real>) -> nalgebra::Point2<Real> {
151 match self {
152 AnyDistortion::None(m) => m.undistort(n),
153 AnyDistortion::BrownConrady5(m) => m.undistort(n),
154 }
155 }
156}
157
158#[derive(Clone, Debug)]
159#[doc(hidden)]
160pub enum AnySensor {
161 Identity(IdentitySensor),
162 Homography(HomographySensor<Real>),
163}
164
165impl SensorModel<Real> for AnySensor {
166 fn normalized_to_sensor(&self, n: &nalgebra::Point2<Real>) -> nalgebra::Point2<Real> {
167 match self {
168 AnySensor::Identity(m) => m.normalized_to_sensor(n),
169 AnySensor::Homography(m) => m.normalized_to_sensor(n),
170 }
171 }
172
173 fn sensor_to_normalized(&self, s: &nalgebra::Point2<Real>) -> nalgebra::Point2<Real> {
174 match self {
175 AnySensor::Identity(m) => m.sensor_to_normalized(s),
176 AnySensor::Homography(m) => m.sensor_to_normalized(s),
177 }
178 }
179}
180
181#[derive(Clone, Debug)]
182#[doc(hidden)]
183pub enum AnyIntrinsics {
184 FxFyCxCySkew(FxFyCxCySkew<Real>),
185}
186
187impl super::IntrinsicsModel<Real> for AnyIntrinsics {
188 fn sensor_to_pixel(&self, sensor: &nalgebra::Point2<Real>) -> nalgebra::Point2<Real> {
189 match self {
190 AnyIntrinsics::FxFyCxCySkew(m) => m.sensor_to_pixel(sensor),
191 }
192 }
193
194 fn pixel_to_sensor(&self, pixel: &nalgebra::Point2<Real>) -> nalgebra::Point2<Real> {
195 match self {
196 AnyIntrinsics::FxFyCxCySkew(m) => m.pixel_to_sensor(pixel),
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn params_build_camera() {
207 let params = CameraParams {
208 projection: ProjectionParams::Pinhole,
209 distortion: DistortionParams::None,
210 sensor: SensorParams::Identity,
211 intrinsics: IntrinsicsParams::FxFyCxCySkew {
212 params: FxFyCxCySkew {
213 fx: 800.0,
214 fy: 810.0,
215 cx: 640.0,
216 cy: 360.0,
217 skew: 0.0,
218 },
219 },
220 };
221 let cam = params.build();
222 let px = cam.project_point_c(&nalgebra::Vector3::new(0.1, 0.2, 1.0));
223 assert!(px.is_some());
224 }
225
226 #[test]
227 fn distortion_params_serde_shape() {
228 let json = r#"{
229 "type": "brown_conrady5",
230 "k1": 0.1,
231 "k2": 0.01,
232 "k3": 0.0,
233 "p1": 0.0,
234 "p2": 0.0,
235 "iters": 4
236 }"#;
237 let cfg: DistortionParams = serde_json::from_str(json).expect("serde should succeed");
238 match cfg {
239 DistortionParams::BrownConrady5 { params } => {
240 assert!((params.k1 - 0.1).abs() < 1e-12);
241 assert!((params.k2 - 0.01).abs() < 1e-12);
242 assert!((params.k3 - 0.0).abs() < 1e-12);
243 assert!((params.p1 - 0.0).abs() < 1e-12);
244 assert!((params.p2 - 0.0).abs() < 1e-12);
245 assert_eq!(params.iters, 4);
246 }
247 DistortionParams::None => panic!("expected BrownConrady5 params, got None"),
248 }
249 }
250}