1#![no_std]
2
3use cv_core::nalgebra::{self, Matrix3, MatrixMN, VectorN, U3, U8, U9};
4use cv_core::sample_consensus::Estimator;
5use cv_core::FeatureMatch;
6use cv_pinhole::{EssentialMatrix, NormalizedKeyPoint};
7
8fn encode_epipolar_equation(
9 matches: impl Iterator<Item = FeatureMatch<NormalizedKeyPoint>>,
10) -> MatrixMN<f64, U8, U9> {
11 let mut out: MatrixMN<f64, U8, U9> = nalgebra::zero();
12 for (i, FeatureMatch(a, b)) in (0..8).zip(matches) {
13 let mut row = VectorN::<f64, U9>::zeros();
14 let ap = a.virtual_image_point().coords;
15 let bp = b.virtual_image_point().coords;
16 for j in 0..3 {
17 let v = ap[j] * bp;
18 row.fixed_rows_mut::<U3>(3 * j).copy_from(&v);
19 }
20 out.row_mut(i).copy_from(&row.transpose());
21 }
22 out
23}
24
25#[derive(Copy, Clone, Debug)]
32pub struct EightPoint {
33 pub epsilon: f64,
34 pub iterations: usize,
35}
36
37impl EightPoint {
38 pub fn new() -> Self {
39 Default::default()
40 }
41}
42
43impl Default for EightPoint {
44 fn default() -> Self {
45 Self {
46 epsilon: 1e-9,
47 iterations: 100,
48 }
49 }
50}
51
52impl Estimator<FeatureMatch<NormalizedKeyPoint>> for EightPoint {
53 type Model = EssentialMatrix;
54 type ModelIter = Option<EssentialMatrix>;
55 const MIN_SAMPLES: usize = 8;
56
57 fn estimate<I>(&self, data: I) -> Self::ModelIter
58 where
59 I: Iterator<Item = FeatureMatch<NormalizedKeyPoint>> + Clone,
60 {
61 let epipolar_constraint = encode_epipolar_equation(data);
62 let eet = epipolar_constraint.transpose() * epipolar_constraint;
63 let eigens = eet.try_symmetric_eigen(self.epsilon, self.iterations)?;
64 let eigenvector = eigens
65 .eigenvalues
66 .iter()
67 .enumerate()
68 .min_by_key(|&(_, &n)| float_ord::FloatOrd(n))
69 .map(|(ix, _)| eigens.eigenvectors.column(ix).into_owned())?;
70 let mat = Matrix3::from_iterator(eigenvector.iter().copied());
71 Some(EssentialMatrix(mat))
72 }
73}