1#[derive(Debug, Clone, PartialEq)]
3pub struct CameraIntrinsics {
4 pub fx: f32,
6 pub fy: f32,
8 pub cx: f32,
10 pub cy: f32,
12 pub k1: f32,
14 pub k2: f32,
16 pub p1: f32,
18 pub p2: f32,
20}
21
22impl CameraIntrinsics {
23 pub fn new(fx: f32, fy: f32, cx: f32, cy: f32) -> Self {
25 Self {
26 fx,
27 fy,
28 cx,
29 cy,
30 k1: 0.0,
31 k2: 0.0,
32 p1: 0.0,
33 p2: 0.0,
34 }
35 }
36}
37
38pub fn undistort_points(points: &[(f32, f32)], intrinsics: &CameraIntrinsics) -> Vec<(f32, f32)> {
45 let CameraIntrinsics {
46 fx,
47 fy,
48 cx,
49 cy,
50 k1,
51 k2,
52 p1,
53 p2,
54 } = *intrinsics;
55
56 points
57 .iter()
58 .map(|&(px, py)| {
59 let x = (px - cx) / fx;
61 let y = (py - cy) / fy;
62
63 let mut xd = x;
65 let mut yd = y;
66 for _ in 0..5 {
67 let r2 = xd * xd + yd * yd;
68 let radial = 1.0 + k1 * r2 + k2 * r2 * r2;
69 let dx = 2.0 * p1 * xd * yd + p2 * (r2 + 2.0 * xd * xd);
70 let dy = p1 * (r2 + 2.0 * yd * yd) + 2.0 * p2 * xd * yd;
71 xd = (x - dx) / radial;
72 yd = (y - dy) / radial;
73 }
74
75 (xd * fx + cx, yd * fy + cy)
76 })
77 .collect()
78}
79
80pub fn project_points(
83 points_3d: &[(f32, f32, f32)],
84 intrinsics: &CameraIntrinsics,
85) -> Vec<(f32, f32)> {
86 let CameraIntrinsics {
87 fx,
88 fy,
89 cx,
90 cy,
91 k1,
92 k2,
93 p1,
94 p2,
95 } = *intrinsics;
96
97 points_3d
98 .iter()
99 .map(|&(x, y, z)| {
100 let xn = x / z;
101 let yn = y / z;
102
103 let r2 = xn * xn + yn * yn;
104 let radial = 1.0 + k1 * r2 + k2 * r2 * r2;
105 let xd = xn * radial + 2.0 * p1 * xn * yn + p2 * (r2 + 2.0 * xn * xn);
106 let yd = yn * radial + p1 * (r2 + 2.0 * yn * yn) + 2.0 * p2 * xn * yn;
107
108 (fx * xd + cx, fy * yd + cy)
109 })
110 .collect()
111}
112
113pub fn triangulate_points(
128 pts_a: &[(f32, f32)],
129 pts_b: &[(f32, f32)],
130 baseline: f32,
131 focal: f32,
132) -> Vec<(f32, f32, f32)> {
133 pts_a
134 .iter()
135 .zip(pts_b.iter())
136 .map(|(&(ax, ay), &(bx, _by))| {
137 let d = ax - bx;
138 if d <= 0.0 {
139 return (0.0, 0.0, 0.0);
140 }
141 let z = baseline * focal / d;
142 let x = ax * z / focal;
143 let y = ay * z / focal;
144 (x, y, z)
145 })
146 .collect()
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 fn approx_eq(a: f32, b: f32, eps: f32) -> bool {
154 (a - b).abs() < eps
155 }
156
157 #[test]
158 fn test_project_points_no_distortion() {
159 let intr = CameraIntrinsics::new(500.0, 500.0, 320.0, 240.0);
160 let pts = vec![(0.0f32, 0.0, 1.0)];
161 let proj = project_points(&pts, &intr);
162 assert!(approx_eq(proj[0].0, 320.0, 1e-5));
163 assert!(approx_eq(proj[0].1, 240.0, 1e-5));
164 }
165
166 #[test]
167 fn test_project_and_undistort_roundtrip_no_distortion() {
168 let intr = CameraIntrinsics::new(600.0, 600.0, 300.0, 200.0);
169 let pts_3d = vec![(1.0f32, -0.5, 3.0), (-2.0, 1.0, 5.0)];
170 let projected = project_points(&pts_3d, &intr);
171 let undistorted = undistort_points(&projected, &intr);
173 for (p, u) in projected.iter().zip(undistorted.iter()) {
174 assert!(approx_eq(p.0, u.0, 1e-4));
175 assert!(approx_eq(p.1, u.1, 1e-4));
176 }
177 }
178
179 #[test]
180 fn test_undistort_with_distortion() {
181 let intr = CameraIntrinsics {
182 fx: 500.0,
183 fy: 500.0,
184 cx: 320.0,
185 cy: 240.0,
186 k1: 0.1,
187 k2: 0.01,
188 p1: 0.0,
189 p2: 0.0,
190 };
191 let pts = vec![(320.0f32, 240.0)];
193 let result = undistort_points(&pts, &intr);
194 assert!(approx_eq(result[0].0, 320.0, 1e-4));
195 assert!(approx_eq(result[0].1, 240.0, 1e-4));
196 }
197
198 #[test]
199 fn test_triangulate_known_depth() {
200 let pts_a = vec![(250.0f32, 100.0)];
202 let pts_b = vec![(200.0f32, 100.0)];
203 let result = triangulate_points(&pts_a, &pts_b, 0.1, 500.0);
204 assert!(approx_eq(result[0].2, 1.0, 1e-5));
205 assert!(approx_eq(result[0].0, 0.5, 1e-5));
207 }
208
209 #[test]
210 fn test_triangulate_zero_disparity() {
211 let pts_a = vec![(100.0f32, 100.0)];
212 let pts_b = vec![(100.0f32, 100.0)];
213 let result = triangulate_points(&pts_a, &pts_b, 0.1, 500.0);
214 assert!(approx_eq(result[0].0, 0.0, 1e-5));
215 assert!(approx_eq(result[0].1, 0.0, 1e-5));
216 assert!(approx_eq(result[0].2, 0.0, 1e-5));
217 }
218}