1use crate::corners_fast9::Corner;
2use image::{GrayImage, imageops};
3#[cfg(all(not(feature = "nalgebra033"), feature = "nalgebra034"))]
4use nalgebra as na;
5#[cfg(feature = "nalgebra033")]
6use nalgebra_033 as na;
7
8use rayon::prelude::*;
9use std::collections::HashMap;
10use std::ops::AddAssign;
11
12use crate::{
13 image_utilities::{self, HalfSize},
14 patch,
15};
16
17use log::info;
18
19pub struct PatchTracker {
20 last_keypoint_id: usize,
21 tracked_points_map: HashMap<usize, na::Affine2<f32>>,
22 previous_image_pyramid: Vec<GrayImage>,
23 grid_size: u32,
24 levels: u32,
25}
26impl Default for PatchTracker {
27 fn default() -> Self {
28 Self::new(4, 20)
29 }
30}
31impl PatchTracker {
32 pub fn new(levels: u32, grid_size: u32) -> Self {
33 Self {
34 last_keypoint_id: 0,
35 tracked_points_map: HashMap::new(),
36 previous_image_pyramid: Vec::new(),
37 grid_size,
38 levels,
39 }
40 }
41 pub fn process_frame(&mut self, greyscale_image: &GrayImage) {
42 let current_image_pyramid: Vec<GrayImage> =
44 build_image_pyramid(greyscale_image, self.levels);
45
46 if !self.previous_image_pyramid.is_empty() {
47 info!("old points {}", self.tracked_points_map.len());
48 self.tracked_points_map = track_points(
50 &self.previous_image_pyramid,
51 ¤t_image_pyramid,
52 &self.tracked_points_map,
53 );
54 info!("tracked old points {}", self.tracked_points_map.len());
55 }
56 let new_points = detect_keypoints(
58 &self.tracked_points_map,
59 ¤t_image_pyramid,
60 self.grid_size,
61 );
62 for point in &new_points {
63 let mut v = na::Affine2::<f32>::identity();
64
65 v.matrix_mut_unchecked().m13 = point.x as f32;
66 v.matrix_mut_unchecked().m23 = point.y as f32;
67 self.tracked_points_map.insert(self.last_keypoint_id, v);
68 self.last_keypoint_id += 1;
69 }
70
71 self.previous_image_pyramid = current_image_pyramid;
73 }
74 pub fn get_track_points(&self) -> HashMap<usize, (f32, f32)> {
75 self.tracked_points_map
76 .iter()
77 .map(|(k, v)| (*k, (v.matrix().m13, v.matrix().m23)))
78 .collect()
79 }
80 pub fn remove_id(&mut self, ids: &[usize]) {
81 for id in ids {
82 self.tracked_points_map.remove(id);
83 }
84 }
85 pub fn add_points(&mut self, points: Vec<(f32, f32)>) {
86 for (x, y) in points {
87 let mut v = na::Affine2::<f32>::identity();
88 v.matrix_mut_unchecked().m13 = x;
89 v.matrix_mut_unchecked().m23 = y;
90 self.tracked_points_map.insert(self.last_keypoint_id, v);
91 self.last_keypoint_id += 1;
92 }
93 }
94}
95
96pub struct StereoPatchTracker {
97 last_keypoint_id: usize,
98 tracked_points_map_cam0: HashMap<usize, na::Affine2<f32>>,
99 previous_image_pyramid0: Vec<GrayImage>,
100 tracked_points_map_cam1: HashMap<usize, na::Affine2<f32>>,
101 previous_image_pyramid1: Vec<GrayImage>,
102 grid_size: u32,
103 levels: u32,
104}
105impl Default for StereoPatchTracker {
106 fn default() -> Self {
107 Self::new(4, 20)
108 }
109}
110impl StereoPatchTracker {
111 pub fn new(levels: u32, grid_size: u32) -> Self {
112 Self {
113 last_keypoint_id: 0,
114 tracked_points_map_cam0: HashMap::new(),
115 previous_image_pyramid0: Vec::new(),
116 tracked_points_map_cam1: HashMap::new(),
117 previous_image_pyramid1: Vec::new(),
118 grid_size,
119 levels,
120 }
121 }
122 pub fn process_frame(&mut self, greyscale_image0: &GrayImage, greyscale_image1: &GrayImage) {
123 let current_image_pyramid0: Vec<GrayImage> =
125 build_image_pyramid(greyscale_image0, self.levels);
126 let current_image_pyramid1: Vec<GrayImage> =
127 build_image_pyramid(greyscale_image1, self.levels);
128
129 if !self.previous_image_pyramid0.is_empty() {
131 info!("old points {}", self.tracked_points_map_cam0.len());
132 self.tracked_points_map_cam0 = track_points(
134 &self.previous_image_pyramid0,
135 ¤t_image_pyramid0,
136 &self.tracked_points_map_cam0,
137 );
138 self.tracked_points_map_cam1 = track_points(
139 &self.previous_image_pyramid1,
140 ¤t_image_pyramid1,
141 &self.tracked_points_map_cam1,
142 );
143 info!("tracked old points {}", self.tracked_points_map_cam0.len());
144 }
145 let new_points0 = detect_keypoints(
147 &self.tracked_points_map_cam0,
148 ¤t_image_pyramid0,
149 self.grid_size,
150 );
151 let tmp_tracked_points0: HashMap<usize, _> = new_points0
152 .iter()
153 .enumerate()
154 .map(|(i, point)| {
155 let mut v = na::Affine2::<f32>::identity();
156 v.matrix_mut_unchecked().m13 = point.x as f32;
157 v.matrix_mut_unchecked().m23 = point.y as f32;
158 (i, v)
159 })
160 .collect();
161
162 let tmp_tracked_points1 = track_points(
163 ¤t_image_pyramid0,
164 ¤t_image_pyramid1,
165 &tmp_tracked_points0,
166 );
167
168 for (key0, pt0) in tmp_tracked_points0 {
169 if let Some(pt1) = tmp_tracked_points1.get(&key0) {
170 self.tracked_points_map_cam0
171 .insert(self.last_keypoint_id, pt0);
172 self.tracked_points_map_cam1
173 .insert(self.last_keypoint_id, *pt1);
174 self.last_keypoint_id += 1;
175 }
176 }
177
178 self.previous_image_pyramid0 = current_image_pyramid0;
180 self.previous_image_pyramid1 = current_image_pyramid1;
181 }
182 pub fn get_track_points(&self) -> [HashMap<usize, (f32, f32)>; 2] {
183 let tracked_pts0 = self
184 .tracked_points_map_cam0
185 .iter()
186 .map(|(k, v)| (*k, (v.matrix().m13, v.matrix().m23)))
187 .collect();
188 let tracked_pts1 = self
189 .tracked_points_map_cam1
190 .iter()
191 .map(|(k, v)| (*k, (v.matrix().m13, v.matrix().m23)))
192 .collect();
193 [tracked_pts0, tracked_pts1]
194 }
195 pub fn remove_id(&mut self, ids: &[usize]) {
196 for id in ids {
197 self.tracked_points_map_cam0.remove(id);
198 self.tracked_points_map_cam1.remove(id);
199 }
200 }
201}
202
203pub fn build_image_pyramid(greyscale_image: &GrayImage, levels: u32) -> Vec<GrayImage> {
204 let mut out = Vec::with_capacity(levels as usize);
205 const FILTER_TYPE: imageops::FilterType = imageops::FilterType::Triangle;
206 out.push(greyscale_image.clone());
207 (1..levels).for_each(|_| {
208 let last_img = out.last().unwrap();
209 let (w, h) = last_img.dimensions();
210 if w % 2 == 0 && h % 2 == 0 {
211 out.push(last_img.half_size());
212 } else {
213 let new_w = w / 2;
214 let new_h = h / 2;
215 out.push(imageops::resize(last_img, new_w, new_h, FILTER_TYPE))
216 }
217 });
218 out
219}
220
221fn detect_keypoints(
222 tracked_points_map: &HashMap<usize, na::Affine2<f32>>,
223 image_pyramid: &[GrayImage],
224 grid_size: u32,
225) -> Vec<Corner> {
226 let num_points_in_cell = 1;
227 let current_corners: Vec<Corner> = tracked_points_map
228 .values()
229 .map(|v| {
230 Corner::new(
231 v.matrix().m13.round() as u32,
232 v.matrix().m23.round() as u32,
233 0.0,
234 )
235 })
236 .collect();
237 let detect_level = if image_pyramid.len() > 1 { 1 } else { 0 };
239 let detect_image = &image_pyramid[detect_level];
240 let detect_scale = 1 << detect_level;
241
242 image_utilities::detect_key_points(
243 &image_pyramid[0],
244 detect_image,
245 detect_scale,
246 grid_size,
247 ¤t_corners,
248 num_points_in_cell,
249 )
250}
251pub fn track_points(
252 image_pyramid0: &[GrayImage],
253 image_pyramid1: &[GrayImage],
254 transform_maps0: &HashMap<usize, na::Affine2<f32>>,
255) -> HashMap<usize, na::Affine2<f32>> {
256 let transform_maps1: HashMap<usize, na::Affine2<f32>> = transform_maps0
257 .par_iter()
258 .filter_map(|(k, v)| {
259 if let Some(new_v) = track_one_point(image_pyramid0, image_pyramid1, v) {
260 if let Some(old_v) = track_one_point(image_pyramid1, image_pyramid0, &new_v)
262 && (v.matrix() - old_v.matrix())
263 .fixed_view::<2, 1>(0, 2)
264 .norm_squared()
265 < 0.4
266 {
267 return Some((*k, new_v));
268 }
269 }
270 None
271 })
272 .collect();
273
274 transform_maps1
275}
276pub fn track_one_point(
277 image_pyramid0: &[GrayImage],
278 image_pyramid1: &[GrayImage],
279 transform0: &na::Affine2<f32>,
280) -> Option<na::Affine2<f32>> {
281 let levels = image_pyramid0.len() as u32;
282 assert!(levels == image_pyramid1.len() as u32);
283 let mut patch_valid = true;
284 let mut transform1 = na::Affine2::<f32>::identity();
285 transform1.matrix_mut_unchecked().m13 = transform0.matrix().m13;
286 transform1.matrix_mut_unchecked().m23 = transform0.matrix().m23;
287
288 for i in (0..levels).rev() {
289 let scale_down = 1 << i;
290
291 transform1.matrix_mut_unchecked().m13 /= scale_down as f32;
292 transform1.matrix_mut_unchecked().m23 /= scale_down as f32;
293
294 let pattern = patch::Pattern52::new(
295 &image_pyramid0[i as usize],
296 transform0.matrix().m13 / scale_down as f32,
297 transform0.matrix().m23 / scale_down as f32,
298 );
299 patch_valid &= pattern.valid;
300 if patch_valid {
301 patch_valid &=
303 track_point_at_level(&image_pyramid1[i as usize], &pattern, &mut transform1);
304 if !patch_valid {
305 return None;
306 }
307 } else {
308 return None;
309 }
310
311 transform1.matrix_mut_unchecked().m13 *= scale_down as f32;
312 transform1.matrix_mut_unchecked().m23 *= scale_down as f32;
313 }
315 let new_r_mat = transform0.matrix() * transform1.matrix();
316 transform1.matrix_mut_unchecked().m11 = new_r_mat.m11;
317 transform1.matrix_mut_unchecked().m12 = new_r_mat.m12;
318 transform1.matrix_mut_unchecked().m21 = new_r_mat.m21;
319 transform1.matrix_mut_unchecked().m22 = new_r_mat.m22;
320 Some(transform1)
321}
322
323pub fn track_point_at_level(
324 grayscale_image: &GrayImage,
325 dp: &patch::Pattern52,
326 transform: &mut na::Affine2<f32>,
327) -> bool {
328 let optical_flow_max_iterations = 5;
330 let patten = na::SMatrix::<f32, 52, 2>::from_fn(|i, j| {
331 patch::Pattern52::PATTERN_RAW[i][j] / dp.pattern_scale_down
332 })
333 .transpose();
334 for _iteration in 0..optical_flow_max_iterations {
337 let mut transformed_pat = transform.matrix().fixed_view::<2, 2>(0, 0) * patten;
338 for i in 0..52 {
339 transformed_pat
340 .column_mut(i)
341 .add_assign(transform.matrix().fixed_view::<2, 1>(0, 2));
342 }
343 if let Some(res) = dp.residual(grayscale_image, &transformed_pat) {
346 let inc = -dp.h_se2_inv_j_se2_t * res;
347
348 if !inc.iter().all(|x| x.is_finite()) {
350 return false;
351 }
352 if inc.norm() > 1e6 {
353 return false;
354 }
355 let new_trans = transform.matrix() * image_utilities::se2_exp_matrix(&inc);
356 *transform = na::Affine2::<f32>::from_matrix_unchecked(new_trans);
357 let filter_margin = 2;
358 if !image_utilities::inbound(
359 grayscale_image,
360 transform.matrix_mut_unchecked().m13,
361 transform.matrix_mut_unchecked().m23,
362 filter_margin,
363 ) {
364 return false;
365 }
366 }
367 }
368
369 true
370}