use rand::{Rng, SeedableRng, rngs::StdRng};
use std::collections::HashMap;
use crate::point::Point;
pub mod brief;
mod constants;
pub trait BinaryDescriptor {
fn get_size(&self) -> u32;
fn hamming_distance(&self, other: &Self) -> u32;
fn get_bit_subset(&self, bits: &[u32]) -> u128;
fn position(&self) -> Point<u32>;
}
pub fn match_binary_descriptors<'a, T: BinaryDescriptor>(
d1: &'a [T],
d2: &'a [T],
threshold: u32,
seed: Option<u64>,
) -> Vec<(&'a T, &'a T)> {
if d1.is_empty() || d2.is_empty() {
return Vec::new();
}
let mut rng = if let Some(s) = seed {
StdRng::seed_from_u64(s)
} else {
SeedableRng::from_os_rng()
};
let (queries, database, swapped) = if d1.len() > d2.len() {
(d2, d1, true)
} else {
(d1, d2, false)
};
let l = 3;
let k = (database.len() as f32).log2() as i32;
let mut hash_tables = Vec::with_capacity(l);
for _ in 0..l {
let bits = (0..k)
.map(|_| rng.random_range(0..queries[0].get_size()))
.collect::<Vec<u32>>();
let mut new_hashmap = HashMap::<u128, Vec<&T>>::with_capacity(database.len());
for d in database.iter() {
let hash = d.get_bit_subset(&bits);
if let Some(v) = new_hashmap.get_mut(&hash) {
v.push(d);
} else {
new_hashmap.insert(hash, vec![d]);
}
}
hash_tables.push((bits, new_hashmap));
}
let mut matches = Vec::with_capacity(queries.len());
for query in queries.iter() {
let mut candidates = Vec::with_capacity(l);
for (bits, table) in hash_tables.iter() {
let query_hash = query.get_bit_subset(bits);
if let Some(m) = table.get(&query_hash) {
for new_candidate in m.clone() {
candidates.push(new_candidate);
}
}
}
let mut best_score = u32::MAX;
let mut best_candidate = None;
for c in candidates {
let distance = query.hamming_distance(c);
if distance < best_score {
best_score = distance;
best_candidate = Some(c);
}
}
if best_score < threshold {
if swapped {
matches.push((best_candidate.unwrap(), query));
} else {
matches.push((query, best_candidate.unwrap()));
}
}
}
matches
}
#[cfg(not(miri))]
#[cfg(test)]
mod benches {
use super::*;
use crate::{binary_descriptors::brief::brief, utils::gray_bench_image};
use test::{Bencher, black_box};
#[bench]
#[ignore]
fn bench_matcher_1000_keypoints_each(b: &mut Bencher) {
let image = gray_bench_image(640, 480);
let mut rng = rand::rng();
let keypoints = (0..1000)
.map(|_| {
Point::new(
rng.random_range(20..image.width() - 20),
rng.random_range(20..image.height() - 20),
)
})
.collect::<Vec<Point<u32>>>();
let (first_descriptors, test_pairs) = brief(&image, &keypoints, 256, None).unwrap();
let (second_descriptors, _) = brief(&image, &keypoints, 256, Some(&test_pairs)).unwrap();
b.iter(|| {
black_box(match_binary_descriptors(
&first_descriptors,
&second_descriptors,
24,
Some(0xc0),
));
});
}
}