1use itertools::izip;
12use nalgebra::DMatrix;
13
14use crate::core::{
15 camera::Intrinsics,
16 candidates::coarse_to_fine as candidates,
17 gradient,
18 inverse_depth::{self, InverseDepth},
19 multires,
20 track::lm_optimizer::{self, LMOptimizerState},
21};
22use crate::math::optimizer::State as _;
23use crate::misc::helper;
24use crate::misc::type_aliases::{Float, Iso3, Mat6, Point2, Vec6};
25
26pub type Levels<T> = Vec<T>;
28
29pub struct Tracker {
32 config: Config,
33 state: State,
34}
35
36pub struct Config {
38 pub nb_levels: usize,
40 pub candidates_diff_threshold: u16,
42 pub depth_scale: Float,
45 pub intrinsics: Intrinsics,
47 pub idepth_variance: Float,
49}
50
51struct State {
53 keyframe_multires_data: MultiresData,
54 keyframe_depth_timestamp: f64,
55 keyframe_img_timestamp: f64,
56 keyframe_pose: Iso3,
57 current_frame_depth_timestamp: f64,
58 current_frame_img_timestamp: f64,
59 current_frame_pose: Iso3,
60}
61
62#[allow(clippy::type_complexity)]
64struct MultiresData {
65 intrinsics_multires: Levels<Intrinsics>,
66 img_multires: Levels<DMatrix<u8>>,
67 usable_candidates_multires: Levels<(Vec<(usize, usize)>, Vec<Float>)>,
68 jacobians_multires: Levels<Vec<Vec6>>,
69 hessians_multires: Levels<Vec<Mat6>>,
70}
71
72impl Config {
73 pub fn init(
75 self,
76 keyframe_depth_timestamp: f64,
77 depth_map: &DMatrix<u16>,
78 keyframe_img_timestamp: f64,
79 img: DMatrix<u8>,
80 ) -> Tracker {
81 let intrinsics_multires = self.intrinsics.clone().multi_res(self.nb_levels);
83 let img_multires = multires::mean_pyramid(self.nb_levels, img);
84 let keyframe_multires_data =
85 precompute_multires_data(&self, depth_map, intrinsics_multires, img_multires);
86
87 Tracker {
89 state: State {
90 keyframe_multires_data,
91 keyframe_depth_timestamp,
92 keyframe_img_timestamp,
93 keyframe_pose: Iso3::identity(),
94 current_frame_depth_timestamp: keyframe_depth_timestamp,
95 current_frame_img_timestamp: keyframe_img_timestamp,
96 current_frame_pose: Iso3::identity(),
97 },
98 config: self,
99 }
100 }
101} #[allow(clippy::used_underscore_binding)]
105fn precompute_multires_data(
106 config: &Config,
107 depth_map: &DMatrix<u16>,
108 intrinsics_multires: Levels<Intrinsics>,
109 img_multires: Levels<DMatrix<u8>>,
110) -> MultiresData {
111 let mut gradients_multires = multires::gradients_xy(&img_multires);
113 gradients_multires.insert(0, gradient::centered(&img_multires[0]));
114 let gradients_squared_norm_multires: Vec<_> = gradients_multires
115 .iter()
116 .map(|(gx, gy)| gradient::squared_norm(gx, gy))
117 .collect();
118
119 let candidates_points = candidates::select(
121 config.candidates_diff_threshold,
122 &gradients_squared_norm_multires,
123 )
124 .pop()
125 .unwrap();
126
127 let from_depth = |z| inverse_depth::from_depth(config.depth_scale, z, config.idepth_variance);
129 let idepth_candidates = helper::zip_mask_map(
130 depth_map,
131 &candidates_points,
132 InverseDepth::Unknown,
133 from_depth,
134 );
135 let fuse = |a, b, c, d| inverse_depth::fuse(a, b, c, d, inverse_depth::strategy_dso_mean);
136 let idepth_multires = multires::limited_sequence(config.nb_levels, idepth_candidates, |m| {
137 multires::halve(m, fuse)
138 });
139 let usable_candidates_multires: Levels<_> = idepth_multires.iter().map(extract_z).collect();
140
141 let jacobians_multires: Levels<Vec<Vec6>> = izip!(
143 &intrinsics_multires,
144 &usable_candidates_multires,
145 &gradients_multires,
146 )
147 .map(|(intrinsics, (coord, _z), (gx, gy))| warp_jacobians(intrinsics, coord, _z, gx, gy))
148 .collect();
149
150 let hessians_multires: Levels<_> = jacobians_multires.iter().map(hessians_vec).collect();
152
153 MultiresData {
155 intrinsics_multires,
156 img_multires,
157 usable_candidates_multires,
158 jacobians_multires,
159 hessians_multires,
160 }
161}
162
163impl Tracker {
164 #[allow(clippy::used_underscore_binding)]
169 #[allow(clippy::cast_precision_loss)]
170 pub fn track(
171 &mut self,
172 depth_time: f64,
173 depth_map: &DMatrix<u16>,
174 img_time: f64,
175 img: DMatrix<u8>,
176 ) {
177 let mut lm_model = self.state.current_frame_pose.inverse() * self.state.keyframe_pose;
178 let img_multires = multires::mean_pyramid(self.config.nb_levels, img);
179 let keyframe_data = &self.state.keyframe_multires_data;
180 let mut optimization_went_well = true;
181 for lvl in (0..self.config.nb_levels).rev() {
182 let obs = lm_optimizer::Obs {
183 intrinsics: &keyframe_data.intrinsics_multires[lvl],
184 template: &keyframe_data.img_multires[lvl],
185 image: &img_multires[lvl],
186 coordinates: &keyframe_data.usable_candidates_multires[lvl].0,
187 _z_candidates: &keyframe_data.usable_candidates_multires[lvl].1,
188 jacobians: &keyframe_data.jacobians_multires[lvl],
189 hessians: &keyframe_data.hessians_multires[lvl],
190 };
191 match LMOptimizerState::iterative_solve(&obs, lm_model) {
192 Ok((lm_state, _)) => {
193 lm_model = lm_state.eval_data.model;
194 }
195 Err(err) => {
196 eprintln!("{}", err);
197 optimization_went_well = false;
198 break;
199 }
200 }
201 }
202
203 self.state.current_frame_depth_timestamp = depth_time;
205 self.state.current_frame_img_timestamp = img_time;
206 if optimization_went_well {
207 self.state.current_frame_pose = self.state.keyframe_pose * lm_model.inverse();
208 }
209
210 let (coordinates, _z_candidates) = keyframe_data.usable_candidates_multires.last().unwrap();
212 let intrinsics = keyframe_data.intrinsics_multires.last().unwrap();
213 let optical_flow_sum: Float = _z_candidates
214 .iter()
215 .zip(coordinates.iter())
216 .map(|(&_z, &(x, y))| {
217 let (u, v) = warp(&lm_model, x as Float, y as Float, _z, intrinsics);
218 (x as Float - u).abs() + (y as Float - v).abs()
219 })
220 .sum();
221 let optical_flow = optical_flow_sum / _z_candidates.len() as Float;
222 eprintln!("Optical_flow: {}", optical_flow);
223
224 let change_keyframe = optical_flow >= 1.0;
225
226 if change_keyframe {
228 let delta_time = depth_time - self.state.keyframe_depth_timestamp;
229 eprintln!("Changing keyframe after: {} seconds", delta_time);
230 self.state.keyframe_multires_data = precompute_multires_data(
231 &self.config,
232 depth_map,
233 keyframe_data.intrinsics_multires.clone(),
234 img_multires,
235 );
236 self.state.keyframe_depth_timestamp = depth_time;
237 self.state.keyframe_img_timestamp = img_time;
238 self.state.keyframe_pose = self.state.current_frame_pose;
239 }
240 } pub fn current_frame(&self) -> (f64, Iso3) {
244 (
245 self.state.current_frame_depth_timestamp,
246 self.state.current_frame_pose,
247 )
248 }
249} #[allow(clippy::used_underscore_binding)]
260fn extract_z(idepth_mat: &DMatrix<InverseDepth>) -> (Vec<(usize, usize)>, Vec<Float>) {
261 let mut u = 0;
262 let mut v = 0;
263 let mut coordinates = Vec::new();
265 let mut _z_vec = Vec::new();
266 let (nb_rows, _) = idepth_mat.shape();
267 for idepth in idepth_mat.iter() {
268 if let InverseDepth::WithVariance(_z, _) = *idepth {
269 coordinates.push((u, v));
270 _z_vec.push(_z);
271 }
272 v += 1;
273 if v >= nb_rows {
274 u += 1;
275 v = 0;
276 }
277 }
278 (coordinates, _z_vec)
279}
280
281#[allow(clippy::used_underscore_binding)]
283#[allow(clippy::cast_precision_loss)]
284fn warp_jacobians(
285 intrinsics: &Intrinsics,
286 coordinates: &[(usize, usize)],
287 _z_candidates: &[Float],
288 grad_x: &DMatrix<i16>,
289 grad_y: &DMatrix<i16>,
290) -> Vec<Vec6> {
291 let (cu, cv) = intrinsics.principal_point;
293 let (fu, fv) = intrinsics.focal;
294 let s = intrinsics.skew;
295
296 coordinates
298 .iter()
299 .zip(_z_candidates.iter())
300 .map(|(&(u, v), &_z)| {
301 let gu = Float::from(grad_x[(v, u)]);
302 let gv = Float::from(grad_y[(v, u)]);
303 warp_jacobian_at(gu, gv, u as Float, v as Float, _z, cu, cv, fu, fv, s)
304 })
305 .collect()
306}
307
308#[allow(clippy::too_many_arguments)]
310#[allow(clippy::many_single_char_names)]
311#[allow(clippy::used_underscore_binding)]
312#[allow(clippy::similar_names)]
313fn warp_jacobian_at(
314 gu: Float,
315 gv: Float,
316 u: Float,
317 v: Float,
318 _z: Float,
319 cu: Float,
320 cv: Float,
321 fu: Float,
322 fv: Float,
323 s: Float,
324) -> Vec6 {
325 let a = u - cu;
327 let b = v - cv;
328 let c = a * fv - s * b;
329 let _fv = 1.0 / fv;
330 let _fuv = 1.0 / (fu * fv);
331
332 Vec6::new(
334 gu * _z * fu, _z * (gu * s + gv * fv), -_z * (gu * a + gv * b), gu * (-a * b * _fv - s) + gv * (-b * b * _fv - fv), gu * (a * c * _fuv + fu) + gv * (b * c * _fuv), gu * (-fu * fu * b + s * c) * _fuv + gv * (c / fu), )
341}
342
343#[allow(clippy::ptr_arg)] fn hessians_vec(jacobians: &Vec<Vec6>) -> Vec<Mat6> {
346 jacobians.iter().map(|j| j * j.transpose()).collect()
348}
349
350#[allow(clippy::used_underscore_binding)]
352fn warp(model: &Iso3, x: Float, y: Float, _z: Float, intrinsics: &Intrinsics) -> (Float, Float) {
353 let x1 = intrinsics.back_project(Point2::new(x, y), 1.0 / _z);
355 let x2 = model * x1;
356 let uvz2 = intrinsics.project(x2);
357 (uvz2.x / uvz2.z, uvz2.y / uvz2.z)
358}