slamkit_rs/mapping/
map.rs1use nalgebra as na;
2use std::collections::HashMap;
3
4use super::triangulation::MapPoint;
5use crate::odometry::CameraIntrinsics;
6
7pub struct Map {
9 points: HashMap<usize, MapPoint>,
11 next_id: usize,
13 intrinsics: CameraIntrinsics,
15 min_observations: usize,
17}
18
19impl Map {
20 pub fn new(intrinsics: CameraIntrinsics) -> Self {
22 Self {
23 points: HashMap::new(),
24 next_id: 0,
25 intrinsics,
26 min_observations: 2,
27 }
28 }
29
30 pub fn add_points(&mut self, mut points: Vec<MapPoint>) {
32 for point in points.iter_mut() {
33 point.id = self.next_id;
34 self.points.insert(self.next_id, point.clone());
35 self.next_id += 1;
36 }
37 }
38
39 pub fn points(&self) -> Vec<&MapPoint> {
41 self.points.values().collect()
42 }
43
44 pub fn size(&self) -> usize {
46 self.points.len()
47 }
48
49 fn project_point(
51 &self,
52 point: &na::Point3<f64>,
53 r: &na::Matrix3<f64>,
54 t: &na::Vector3<f64>,
55 ) -> Option<na::Point2<f64>> {
56 let p_cam = r * point + t;
58
59 if p_cam.z <= 0.0 {
61 return None;
62 }
63
64 let x = self.intrinsics.fx * (p_cam.x / p_cam.z) + self.intrinsics.cx;
66 let y = self.intrinsics.fy * (p_cam.y / p_cam.z) + self.intrinsics.cy;
67
68 Some(na::Point2::new(x, y))
69 }
70
71 pub fn find_matches(
73 &mut self,
74 _keypoints: &opencv::core::Vector<opencv::core::KeyPoint>,
75 descriptors: &opencv::core::Mat,
76 pose: &(na::Matrix3<f64>, na::Vector3<f64>),
77 matcher: &mut crate::FeatureMatcher,
78 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
79 let (r, t) = pose;
80 let mut matches = Vec::new();
81
82 let mut visible_points = Vec::new();
84 let mut visible_descriptors = Vec::new();
85
86 for (id, point) in &self.points {
87 if let Some(proj) = self.project_point(&point.position, r, t) {
89 if proj.x >= 0.0 && proj.x < 4000.0 && proj.y >= 0.0 && proj.y < 3000.0 {
91 if let Some(ref desc) = point.descriptor {
92 visible_points.push(*id);
93 visible_descriptors.push(desc.clone());
94 }
95 }
96 }
97 }
98
99 if visible_descriptors.is_empty() {
100 return Ok(matches);
101 }
102
103 let map_desc = descriptors_to_mat(&visible_descriptors)?;
105
106 let raw_matches = matcher.match_descriptors(&map_desc, descriptors)?;
108 let good_matches = matcher.filter_good_matches(&raw_matches, 2.0);
109
110 for m in good_matches.iter() {
112 let map_id = visible_points[m.query_idx as usize];
113 let kp_idx = m.train_idx as usize;
114 matches.push((map_id, kp_idx));
115 }
116
117 Ok(matches)
118 }
119
120 pub fn update_observations(&mut self, matches: &[(usize, usize)]) {
122 for (map_id, _kp_idx) in matches {
123 if let Some(point) = self.points.get_mut(map_id) {
124 point.add_observation();
125 }
126 }
127 }
128
129 pub fn prune_outliers(&mut self) -> usize {
131 let before = self.points.len();
132
133 self.points
134 .retain(|_id, point| point.observations >= self.min_observations);
135
136 before - self.points.len()
137 }
138
139 pub fn stable_points(&self) -> Vec<&MapPoint> {
141 self.points
142 .values()
143 .filter(|p| p.observations >= self.min_observations)
144 .collect()
145 }
146
147 pub fn clear(&mut self) {
149 self.points.clear();
150 self.next_id = 0;
151 }
152}
153
154fn descriptors_to_mat(
156 descriptors: &[Vec<u8>],
157) -> Result<opencv::core::Mat, Box<dyn std::error::Error>> {
158 use opencv::core::{CV_8U, Mat};
159 use opencv::prelude::*;
160
161 if descriptors.is_empty() {
162 return Ok(Mat::default());
163 }
164
165 let rows = descriptors.len() as i32;
166 let cols = descriptors[0].len() as i32;
167 let mut mat =
168 Mat::new_rows_cols_with_default(rows, cols, CV_8U, opencv::core::Scalar::all(0.0))?;
169
170 for (i, desc) in descriptors.iter().enumerate() {
171 for (j, &val) in desc.iter().enumerate() {
172 *mat.at_2d_mut::<u8>(i as i32, j as i32)? = val;
173 }
174 }
175
176 Ok(mat)
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_map_creation() {
185 let cam = CameraIntrinsics::kitti();
186 let map = Map::new(cam);
187 assert_eq!(map.size(), 0);
188 }
189
190 #[test]
191 fn test_add_points() {
192 let cam = CameraIntrinsics::kitti();
193 let mut map = Map::new(cam);
194
195 let points = vec![
196 MapPoint::new(na::Point3::new(1.0, 2.0, 10.0), 0),
197 MapPoint::new(na::Point3::new(2.0, 3.0, 10.0), 1),
198 ];
199
200 map.add_points(points);
201 assert_eq!(map.size(), 2);
202 }
203
204 #[test]
205 fn test_projection() {
206 let cam = CameraIntrinsics::kitti();
207 let map = Map::new(cam);
208
209 let point = na::Point3::new(0.0, 0.0, 10.0);
210 let r = na::Matrix3::identity();
211 let t = na::Vector3::zeros();
212
213 let proj = map.project_point(&point, &r, &t);
214 assert!(proj.is_some());
215
216 let p = proj.unwrap();
217 assert!((p.x - cam.cx).abs() < 1e-6);
218 assert!((p.y - cam.cy).abs() < 1e-6);
219 }
220
221 #[test]
222 fn test_prune_outliers() {
223 let cam = CameraIntrinsics::kitti();
224 let mut map = Map::new(cam);
225
226 let mut points = vec![
227 MapPoint::new(na::Point3::new(1.0, 2.0, 10.0), 0),
228 MapPoint::new(na::Point3::new(2.0, 3.0, 10.0), 1),
229 ];
230 points[0].observations = 5;
231 points[1].observations = 1;
232
233 map.add_points(points);
234 let removed = map.prune_outliers();
235
236 assert_eq!(removed, 1);
237 assert_eq!(map.size(), 1);
238 }
239}