farm-ng-core
average.h
Go to the documentation of this file.
1 // Copyright (c) 2011, Hauke Strasdat
2 // Copyright (c) 2012, Steven Lovegrove
3 // Copyright (c) 2021, farm-ng, inc.
4 //
5 // Use of this source code is governed by an MIT-style
6 // license that can be found in the LICENSE file or at
7 // https://opensource.org/licenses/MIT.
8 
9 /// @file
10 /// Calculation of biinvariant means.
11 
12 #pragma once
13 
14 #include "sophus/lie/isometry2.h"
15 #include "sophus/lie/isometry3.h"
16 #include "sophus/lie/rotation2.h"
17 #include "sophus/lie/rotation3.h"
18 #include "sophus/lie/scaling.h"
20 #include "sophus/lie/similarity2.h"
21 #include "sophus/lie/similarity3.h"
24 #include "sophus/lie/translation.h"
25 
26 #include <complex>
27 
28 namespace sophus {
29 
30 /// Calculates mean iteratively.
31 ///
32 /// Returns ``nullopt`` if it does not converge.
33 ///
34 template <concepts::Range TSequenceContainer>
36  TSequenceContainer const& foo_from_bar_transforms, int max_num_iterations)
37  -> std::optional<typename TSequenceContainer::value_type> {
38  size_t const k_matrix_dim = std::distance(
39  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
40  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
41 
42  using Group = typename TSequenceContainer::value_type;
43  using Scalar = typename Group::Scalar;
44  using Tangent = Eigen::Vector<Scalar, Group::kDof>;
45 
46  // This implements the algorithm in the beginning of Sec. 4.2 in
47  // ftp://ftp-sop.inria.fr/epidaure/Publications/Arsigny/arsigny_rr_biinvariant_average.pdf.
48  Group foo_from_average = *std::begin(foo_from_bar_transforms);
49  Scalar w = Scalar(1. / k_matrix_dim);
50  for (int i = 0; i < max_num_iterations; ++i) {
52  average.setZero();
53  for (Group const& foo_from_bar : foo_from_bar_transforms) {
54  average += w * (foo_from_average.inverse() * foo_from_bar).log();
55  }
56  Group foo_from_newaverage = foo_from_average * Group::exp(average);
57  if (((foo_from_newaverage.inverse() * foo_from_average).log())
58  .squaredNorm() < kEpsilon<Scalar>) {
59  return foo_from_newaverage;
60  }
61 
62  foo_from_average = foo_from_newaverage;
63  }
64  // LCOV_EXCL_START
65  return std::nullopt;
66  // LCOV_EXCL_STOP
67 }
68 
69 #ifdef DOXYGEN_SHOULD_SKIP_THIS
70 /// Mean implementation for any Lie group.
71 template <class SequenceContainer, class Scalar>
72 optional<typename SequenceContainer::value_type> average(
73  SequenceContainer const& foo_Ts_bar);
74 #else
75 
76 // Mean implementation for Translation.
77 template <
78  concepts::Range TSequenceContainer,
79  int kPointDim = TSequenceContainer::value_type::kDof,
80  class TScalar = typename TSequenceContainer::value_type::Scalar>
81 auto average(TSequenceContainer const& foo_from_bar_transforms)
82  -> std::enable_if_t<
83  std::is_same<
84  typename TSequenceContainer::value_type,
86  std::optional<typename TSequenceContainer::value_type> > {
87  size_t const k_matrix_dim = std::distance(
88  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
89  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
90 
91  Eigen::Vector<TScalar, kPointDim> average;
92  average.setZero();
93  for (Translation<TScalar, kPointDim> const& foo_from_bar :
94  foo_from_bar_transforms) {
95  average += foo_from_bar.params();
96  }
98  average / TScalar(k_matrix_dim));
99 }
100 
101 // Mean implementation for Scaling.
102 template <
103  concepts::Range TSequenceContainer,
104  int kPointDim = TSequenceContainer::value_type::kDof,
105  class TScalar = typename TSequenceContainer::value_type::Scalar>
106 auto average(TSequenceContainer const& foo_from_bar_transforms)
107  -> std::enable_if_t<
108  std::is_same<
109  typename TSequenceContainer::value_type,
110  Scaling<TScalar, kPointDim> >::value,
111  std::optional<typename TSequenceContainer::value_type> > {
112  size_t const k_matrix_dim = std::distance(
113  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
114  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
115 
116  Eigen::Array<TScalar, kPointDim, 1> average;
117  average.setZero();
118  for (Scaling<TScalar, kPointDim> const& foo_from_bar :
119  foo_from_bar_transforms) {
120  average += foo_from_bar.params().array().log();
121  }
122  return Scaling<TScalar, kPointDim>::fromParams(
123  (average / TScalar(k_matrix_dim)).exp().matrix());
124 }
125 
126 // Mean implementation for SO(2).
127 template <
128  concepts::Range TSequenceContainer,
129  class TScalar = typename TSequenceContainer::value_type::Scalar>
130 auto average(TSequenceContainer const& foo_from_bar_transforms)
131  -> std::enable_if_t<
132  std::is_same<
133  typename TSequenceContainer::value_type,
134  Rotation2<TScalar> >::value,
135  std::optional<typename TSequenceContainer::value_type> > {
136  // This implements rotational part of Proposition 12 from Sec. 6.2 of
137  // ftp://ftp-sop.inria.fr/epidaure/Publications/Arsigny/arsigny_rr_biinvariant_average.pdf.
138  size_t const k_matrix_dim = std::distance(
139  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
140  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
141  Rotation2<TScalar> foo_from_average = *std::begin(foo_from_bar_transforms);
142  TScalar w = TScalar(1. / k_matrix_dim);
143 
144  Eigen::Vector<TScalar, 1> average;
145  average.setZero();
146  for (Rotation2<TScalar> const& foo_from_bar : foo_from_bar_transforms) {
147  average += w * (foo_from_average.inverse() * foo_from_bar).log();
148  }
149  return foo_from_average * Rotation2<TScalar>::exp(average);
150 }
151 
152 // Mean implementation for RxSO(2).
153 template <
154  concepts::Range TSequenceContainer,
155  class TScalar = typename TSequenceContainer::value_type::Scalar>
156 auto average(TSequenceContainer const& foo_from_bar_transforms)
157  -> std::enable_if_t<
158  std::is_same<
159  typename TSequenceContainer::value_type,
160  SpiralSimilarity2<TScalar> >::value,
161  std::optional<typename TSequenceContainer::value_type> > {
162  size_t const k_matrix_dim = std::distance(
163  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
164  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
165  SpiralSimilarity2<TScalar> foo_from_average =
166  *std::begin(foo_from_bar_transforms);
167  TScalar w = TScalar(1. / k_matrix_dim);
168 
169  Eigen::Vector2<TScalar> average(TScalar(0), TScalar(0));
170  for (SpiralSimilarity2<TScalar> const& foo_from_bar :
171  foo_from_bar_transforms) {
172  average += w * (foo_from_average.inverse() * foo_from_bar).log();
173  }
174  return foo_from_average * SpiralSimilarity2<TScalar>::exp(average);
175 }
176 
177 namespace details {
178 template <class TScalar>
179 void getQuaternion(TScalar const&);
180 
181 template <class TScalar>
182 auto getUnitQuaternion(Rotation3<TScalar> const& r)
183  -> Eigen::Vector<TScalar, 4> {
184  return r.params().template head<4>();
185 }
186 
187 template <class TScalar>
188 auto getUnitQuaternion(SpiralSimilarity3<TScalar> const& s_r)
189  -> Eigen::Vector<TScalar, 4> {
190  return s_r.params().template head<4>().normalized();
191 }
192 
193 template <
194  concepts::Range TSequenceContainer,
195  class TScalar = typename TSequenceContainer::value_type::Scalar>
196 auto averageUnitQuaternion(TSequenceContainer const& foo_from_bar_transforms)
197  -> Eigen::Vector<TScalar, 4> {
198  // This: http://stackoverflow.com/a/27410865/1221742
199  size_t const k_matrix_dim = std::distance(
200  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
201  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
202  Eigen::Matrix<TScalar, 4, Eigen::Dynamic> q(4, k_matrix_dim);
203  int i = 0;
204  TScalar w = TScalar(1. / k_matrix_dim);
205  for (auto const& foo_from_bar : foo_from_bar_transforms) {
206  q.col(i) = w * details::getUnitQuaternion(foo_from_bar);
207  ++i;
208  }
209 
210  Eigen::Matrix<TScalar, 4, 4> q_qt = q * q.transpose();
211  // TODO: Figure out why we can't use SelfAdjointEigenSolver here.
212  Eigen::EigenSolver<Eigen::Matrix<TScalar, 4, 4> > es(q_qt);
213 
214  std::complex<TScalar> max_eigenvalue = es.eigenvalues()[0];
215  Eigen::Matrix<std::complex<TScalar>, 4, 1> max_eigenvector =
216  es.eigenvectors().col(0);
217 
218  for (int i = 1; i < 4; i++) {
219  if (std::norm(es.eigenvalues()[i]) > std::norm(max_eigenvalue)) {
220  max_eigenvalue = es.eigenvalues()[i];
221  max_eigenvector = es.eigenvectors().col(i);
222  }
223  }
224  Eigen::Vector<TScalar, 4> quat;
225  quat << //
226  max_eigenvector[0].real(), //
227  max_eigenvector[1].real(), //
228  max_eigenvector[2].real(), //
229  max_eigenvector[3].real();
230  return quat;
231 }
232 } // namespace details
233 
234 // Mean implementation for SO(3).
235 //
236 // TODO: Detect degenerated cases and return nullopt.
237 template <
238  concepts::Range TSequenceContainer,
239  class TScalar = typename TSequenceContainer::value_type::Scalar>
240 auto average(TSequenceContainer const& foo_from_bar_transforms)
241  -> std::enable_if_t<
242  std::is_same<
243  typename TSequenceContainer::value_type,
244  Rotation3<TScalar> >::value,
245  std::optional<typename TSequenceContainer::value_type> > {
247  details::averageUnitQuaternion(foo_from_bar_transforms));
248 }
249 
250 // Mean implementation for R x SO(3).
251 template <
252  concepts::Range TSequenceContainer,
253  class TScalar = typename TSequenceContainer::value_type::Scalar>
254 auto average(TSequenceContainer const& foo_from_bar_transforms)
255  -> std::enable_if_t<
256  std::is_same<
257  typename TSequenceContainer::value_type,
258  SpiralSimilarity3<TScalar> >::value,
259  std::optional<typename TSequenceContainer::value_type> > {
260  size_t k_matrix_dim = std::distance(
261  std::begin(foo_from_bar_transforms), std::end(foo_from_bar_transforms));
262 
263  SOPHUS_ASSERT(k_matrix_dim >= 1, "kMatrixDim must be >= 1.");
264  TScalar scale_sum = TScalar(0);
265  using std::exp;
266  using std::log;
267  for (SpiralSimilarity3<TScalar> const& foo_from_bar :
268  foo_from_bar_transforms) {
269  scale_sum += log(foo_from_bar.scale());
270  }
271  return SpiralSimilarity3<TScalar>(
273  details::averageUnitQuaternion(foo_from_bar_transforms)),
274  exp(scale_sum / TScalar(k_matrix_dim)));
275 }
276 
277 template <
278  concepts::Range TSequenceContainer,
279  class TScalar = typename TSequenceContainer::value_type::Scalar>
280 auto average(
281  TSequenceContainer const& foo_from_bar_transforms,
282  int max_num_iterations = 20)
283  -> std::enable_if_t<
284  std::is_same<
285  typename TSequenceContainer::value_type,
286  Isometry2<TScalar> >::value,
287  std::optional<typename TSequenceContainer::value_type> > {
288  // TODO: Implement Proposition 12 from Sec. 6.2 of
289  // ftp://ftp-sop.inria.fr/epidaure/Publications/Arsigny/arsigny_rr_biinvariant_average.pdf.
290  return iterativeMean(foo_from_bar_transforms, max_num_iterations);
291 }
292 
293 template <
294  concepts::Range TSequenceContainer,
295  class TScalar = typename TSequenceContainer::value_type::Scalar>
296 auto average(
297  TSequenceContainer const& foo_from_bar_transforms,
298  int max_num_iterations = 20)
299  -> std::enable_if_t<
300  std::is_same<
301  typename TSequenceContainer::value_type,
302  Similarity2<TScalar> >::value,
303  std::optional<typename TSequenceContainer::value_type> > {
304  return iterativeMean(foo_from_bar_transforms, max_num_iterations);
305 }
306 
307 template <
308  concepts::Range TSequenceContainer,
309  class TScalar = typename TSequenceContainer::value_type::Scalar>
310 auto average(
311  TSequenceContainer const& foo_from_bar_transforms,
312  int max_num_iterations = 20)
313  -> std::enable_if_t<
314  std::is_same<
315  typename TSequenceContainer::value_type,
316  Isometry3<TScalar> >::value,
317  std::optional<typename TSequenceContainer::value_type> > {
318  return iterativeMean(foo_from_bar_transforms, max_num_iterations);
319 }
320 
321 template <
322  concepts::Range TSequenceContainer,
323  class TScalar = typename TSequenceContainer::value_type::Scalar>
324 auto average(
325  TSequenceContainer const& foo_from_bar_transforms,
326  int max_num_iterations = 20)
327  -> std::enable_if_t<
328  std::is_same<
329  typename TSequenceContainer::value_type,
330  Similarity3<TScalar> >::value,
331  std::optional<typename TSequenceContainer::value_type> > {
332  return iterativeMean(foo_from_bar_transforms, max_num_iterations);
333 }
334 
335 template <
336  concepts::Range TSequenceContainer,
337  int kPointDim = TSequenceContainer::value_type::kPointDim,
338  class TScalar = typename TSequenceContainer::value_type::Scalar>
339 auto average(
340  TSequenceContainer const& foo_from_bar_transforms,
341  int max_num_iterations = 20)
342  -> std::enable_if_t<
343  std::is_same<
344  typename TSequenceContainer::value_type,
346  std::optional<typename TSequenceContainer::value_type> > {
347  return iterativeMean(foo_from_bar_transforms, max_num_iterations);
348 }
349 
350 #endif // DOXYGEN_SHOULD_SKIP_THIS
351 
352 } // namespace sophus
similarity3.h
sophus::average
auto average(TSequenceContainer const &foo_from_bar_transforms) -> std::enable_if_t< std::is_same< typename TSequenceContainer::value_type, Translation< TScalar, kPointDim > >::value, std::optional< typename TSequenceContainer::value_type > >
Definition: average.h:81
sophus::Translation
Definition: translation.h:19
SOPHUS_ASSERT
#define SOPHUS_ASSERT(...)
Definition: common.h:40
sophus
Image MutImage, owning images types.
Definition: num_diff.h:20
similarity2.h
sophus::concepts::Range
concept Range
Definition: utils.h:42
sophus::lie::Group< Rotation3, TScalar, lie::Rotation3Impl >::fromParams
static auto fromParams(Params const &params) -> Derived
Definition: lie_group.h:79
isometry3.h
sophus::lie::Group< Rotation2, TScalar, lie::Rotation2Impl >::inverse
auto inverse() const -> Derived
Definition: lie_group.h:127
isometry2.h
sophus::iterativeMean
auto iterativeMean(TSequenceContainer const &foo_from_bar_transforms, int max_num_iterations) -> std::optional< typename TSequenceContainer::value_type >
Calculates mean iteratively.
Definition: average.h:35
sophus::concepts::Tangent
concept Tangent
Definition: params.h:34
sophus::Isometry2
Definition: isometry2.h:22
rotation2.h
spiral_similarity2.h
scaling.h
translation.h
rotation3.h
sophus::Rotation2
Definition: rotation2.h:20
sophus::lie::Group< Rotation2, TScalar, lie::Rotation2Impl >::exp
static auto exp(Tangent const &tangent) -> Derived
Definition: lie_group.h:93
sophus::ScalingTranslation
Definition: scaling_translation.h:19
spiral_similarity3.h
scaling_translation.h