use nalgebra as na;
use std::collections::HashMap;
use super::triangulation::MapPoint;
use crate::odometry::CameraIntrinsics;
pub struct Map {
points: HashMap<usize, MapPoint>,
next_id: usize,
intrinsics: CameraIntrinsics,
min_observations: usize,
}
impl Map {
pub fn new(intrinsics: CameraIntrinsics) -> Self {
Self {
points: HashMap::new(),
next_id: 0,
intrinsics,
min_observations: 2,
}
}
pub fn add_points(&mut self, mut points: Vec<MapPoint>) {
for point in points.iter_mut() {
point.id = self.next_id;
self.points.insert(self.next_id, point.clone());
self.next_id += 1;
}
}
pub fn points(&self) -> Vec<&MapPoint> {
self.points.values().collect()
}
pub fn size(&self) -> usize {
self.points.len()
}
fn project_point(
&self,
point: &na::Point3<f64>,
r: &na::Matrix3<f64>,
t: &na::Vector3<f64>,
) -> Option<na::Point2<f64>> {
let p_cam = r * point + t;
if p_cam.z <= 0.0 {
return None;
}
let x = self.intrinsics.fx * (p_cam.x / p_cam.z) + self.intrinsics.cx;
let y = self.intrinsics.fy * (p_cam.y / p_cam.z) + self.intrinsics.cy;
Some(na::Point2::new(x, y))
}
pub fn find_matches(
&mut self,
_keypoints: &opencv::core::Vector<opencv::core::KeyPoint>,
descriptors: &opencv::core::Mat,
pose: &(na::Matrix3<f64>, na::Vector3<f64>),
matcher: &mut crate::FeatureMatcher,
) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
let (r, t) = pose;
let mut matches = Vec::new();
let mut visible_points = Vec::new();
let mut visible_descriptors = Vec::new();
for (id, point) in &self.points {
if let Some(proj) = self.project_point(&point.position, r, t) {
if proj.x >= 0.0 && proj.x < 4000.0 && proj.y >= 0.0 && proj.y < 3000.0 {
if let Some(ref desc) = point.descriptor {
visible_points.push(*id);
visible_descriptors.push(desc.clone());
}
}
}
}
if visible_descriptors.is_empty() {
return Ok(matches);
}
let map_desc = descriptors_to_mat(&visible_descriptors)?;
let raw_matches = matcher.match_descriptors(&map_desc, descriptors)?;
let good_matches = matcher.filter_good_matches(&raw_matches, 2.0);
for m in good_matches.iter() {
let map_id = visible_points[m.query_idx as usize];
let kp_idx = m.train_idx as usize;
matches.push((map_id, kp_idx));
}
Ok(matches)
}
pub fn update_observations(&mut self, matches: &[(usize, usize)]) {
for (map_id, _kp_idx) in matches {
if let Some(point) = self.points.get_mut(map_id) {
point.add_observation();
}
}
}
pub fn prune_outliers(&mut self) -> usize {
let before = self.points.len();
self.points
.retain(|_id, point| point.observations >= self.min_observations);
before - self.points.len()
}
pub fn stable_points(&self) -> Vec<&MapPoint> {
self.points
.values()
.filter(|p| p.observations >= self.min_observations)
.collect()
}
pub fn clear(&mut self) {
self.points.clear();
self.next_id = 0;
}
}
fn descriptors_to_mat(
descriptors: &[Vec<u8>],
) -> Result<opencv::core::Mat, Box<dyn std::error::Error>> {
use opencv::core::{CV_8U, Mat};
use opencv::prelude::*;
if descriptors.is_empty() {
return Ok(Mat::default());
}
let rows = descriptors.len() as i32;
let cols = descriptors[0].len() as i32;
let mut mat =
Mat::new_rows_cols_with_default(rows, cols, CV_8U, opencv::core::Scalar::all(0.0))?;
for (i, desc) in descriptors.iter().enumerate() {
for (j, &val) in desc.iter().enumerate() {
*mat.at_2d_mut::<u8>(i as i32, j as i32)? = val;
}
}
Ok(mat)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map_creation() {
let cam = CameraIntrinsics::kitti();
let map = Map::new(cam);
assert_eq!(map.size(), 0);
}
#[test]
fn test_add_points() {
let cam = CameraIntrinsics::kitti();
let mut map = Map::new(cam);
let points = vec![
MapPoint::new(na::Point3::new(1.0, 2.0, 10.0), 0),
MapPoint::new(na::Point3::new(2.0, 3.0, 10.0), 1),
];
map.add_points(points);
assert_eq!(map.size(), 2);
}
#[test]
fn test_projection() {
let cam = CameraIntrinsics::kitti();
let map = Map::new(cam);
let point = na::Point3::new(0.0, 0.0, 10.0);
let r = na::Matrix3::identity();
let t = na::Vector3::zeros();
let proj = map.project_point(&point, &r, &t);
assert!(proj.is_some());
let p = proj.unwrap();
assert!((p.x - cam.cx).abs() < 1e-6);
assert!((p.y - cam.cy).abs() < 1e-6);
}
#[test]
fn test_prune_outliers() {
let cam = CameraIntrinsics::kitti();
let mut map = Map::new(cam);
let mut points = vec![
MapPoint::new(na::Point3::new(1.0, 2.0, 10.0), 0),
MapPoint::new(na::Point3::new(2.0, 3.0, 10.0), 1),
];
points[0].observations = 5;
points[1].observations = 1;
map.add_points(points);
let removed = map.prune_outliers();
assert_eq!(removed, 1);
assert_eq!(map.size(), 1);
}
}