slamkit_rs/feature/
matcher.rs1use opencv::{
2 core::{Mat, Vector},
3 features2d::BFMatcher,
4 prelude::*,
5};
6
7pub struct FeatureMatcher {
10 matcher: opencv::core::Ptr<BFMatcher>,
11}
12
13impl FeatureMatcher {
14 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
16 let matcher = BFMatcher::create(
17 opencv::core::NORM_HAMMING,
18 false, )?;
20 Ok(Self { matcher })
21 }
22
23 pub fn match_descriptors(
25 &mut self,
26 desc1: &Mat,
27 desc2: &Mat,
28 ) -> Result<Vector<opencv::core::DMatch>, Box<dyn std::error::Error>> {
29 if desc1.empty() || desc2.empty() {
30 return Ok(Vector::new());
31 }
32
33 let mut matches = Vector::new();
34 self.matcher
35 .train_match(desc1, desc2, &mut matches, &Mat::default())?;
36 Ok(matches)
37 }
38
39 pub fn filter_good_matches(
41 &self,
42 matches: &Vector<opencv::core::DMatch>,
43 ratio: f32,
44 ) -> Vector<opencv::core::DMatch> {
45 if matches.is_empty() {
46 return Vector::new();
47 }
48
49 let mut min_dist = f32::MAX;
51 for m in matches.iter() {
52 if m.distance < min_dist {
53 min_dist = m.distance;
54 }
55 }
56 let threshold = (ratio * min_dist).max(30.0);
59 let mut good = Vector::new();
60 for m in matches.iter() {
61 if m.distance < threshold {
62 good.push(m);
63 }
64 }
65 good
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_matcher_creation() {
75 let matcher = FeatureMatcher::new();
76 assert!(matcher.is_ok());
77 }
78
79 #[test]
80 fn test_empty_match() {
81 let mut matcher = FeatureMatcher::new().unwrap();
82 let empty = Mat::default();
83 let result = matcher.match_descriptors(&empty, &empty);
84 assert!(result.is_ok());
85 assert_eq!(result.unwrap().len(), 0);
86 }
87}