similarity_least_squares/
lib.rs1pub extern crate nalgebra;
2
3use nalgebra::{
4 constraint::{DimEq, ShapeConstraint},
5 Dim, Matrix, Matrix2, RawStorageMut, RealField, Rotation2, SimilarityMatrix2,
6 Translation2, SVD, U2,
7};
8
9#[cfg(feature = "std")]
10#[inline]
11pub fn from_point_slices<T: RealField>(
12 from: &[nalgebra::Point2<T>],
13 to: &[nalgebra::Point2<T>],
14 eps: T,
15 max_niter: usize,
16) -> Option<SimilarityMatrix2<T>>
17where
18 nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<U2, nalgebra::Dyn>,
19{
20 from_matrices(
21 point_slice_to_matrix(from),
22 point_slice_to_matrix(to),
23 eps,
24 max_niter,
25 )
26}
27
28#[cfg(feature = "std")]
29#[inline]
30pub fn point_slice_to_matrix<T: RealField>(
31 slice: &[nalgebra::Point2<T>],
32) -> nalgebra::OMatrix<T, U2, nalgebra::Dyn>
33where
34 nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<U2, nalgebra::Dyn>,
35{
36 nalgebra::OMatrix::<T, U2, nalgebra::Dyn>::from_iterator(
37 slice.len(),
38 slice
39 .iter()
40 .map(|p| p.coords.iter())
41 .flatten()
42 .map(|v| v.to_owned()),
43 )
44}
45
46#[inline]
47pub fn from_matrices<T, D1, D2, S>(
48 mut from: Matrix<T, U2, D1, S>,
49 mut to: Matrix<T, U2, D2, S>,
50 eps: T,
51 max_niter: usize,
52) -> Option<SimilarityMatrix2<T>>
53where
54 T: RealField,
55 D1: Dim,
56 D2: Dim,
57 ShapeConstraint: DimEq<D1, D2>,
58 S: RawStorageMut<T, U2, D1> + RawStorageMut<T, U2, D2>,
59{
60 let size_recip = T::from_usize(from.column_iter().count())
61 .expect("Cannot convert usize to T")
62 .recip();
63
64 let mean_from = from.column_mean();
65 let mean_to = to.column_mean();
66
67 for mut col in from.column_iter_mut() {
68 col -= &mean_from;
69 }
70 for mut col in to.column_iter_mut() {
71 col -= &mean_to;
72 }
73
74 let mut sigma = from
75 .column_iter()
76 .fold(T::zero(), |sigma, col| sigma + col.norm_squared());
77
78 let mut cov = from
79 .column_iter()
80 .zip(to.column_iter())
81 .fold(Matrix2::zeros(), |cov, (from_col, to_col)| {
82 cov + (to_col * from_col.transpose())
83 });
84
85 sigma *= size_recip.to_owned();
86 cov.scale_mut(size_recip);
87
88 let det = cov.determinant();
89 let SVD {
90 u,
91 v_t,
92 singular_values,
93 } = cov.try_svd_unordered(true, true, eps, max_niter)?;
94
95 let u = u?;
96 let v_t = v_t?;
97 let d = Matrix2::from_diagonal(&singular_values);
98
99 let mut s = Matrix2::identity();
100
101 if det < T::zero() || (det == T::zero() && (u.determinant() * v_t.determinant()) < T::zero()) {
102 let index = if d[(1, 1)] < d[(0, 0)] {
103 (1, 1)
104 } else {
105 (0, 0)
106 };
107
108 s[index] = T::one().neg();
109 }
110
111 let scaling = if sigma != T::zero() {
112 sigma.recip() * (d * &s).trace()
113 } else {
114 T::one()
115 };
116
117 let rotation = Rotation2::from_matrix_unchecked(u * s * v_t);
118 let translation: Translation2<T> =
119 (mean_to - (&rotation * mean_from).scale(scaling.to_owned())).into();
120
121 Some(SimilarityMatrix2::from_parts(
122 translation,
123 rotation,
124 scaling,
125 ))
126}