AliceVision
Photogrammetric Computer Vision Framework
numeric.hpp
1 // This file is part of the AliceVision project.
2 // Copyright (c) 2016 AliceVision contributors.
3 // Copyright (c) 2012 openMVG contributors.
4 // Copyright (c) 2007 libmv contributors.
5 // This Source Code Form is subject to the terms of the Mozilla Public License,
6 // v. 2.0. If a copy of the MPL was not distributed with this file,
7 // You can obtain one at https://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 // AliceVision does not support Eigen with alignment, unless C++17 aligned new feature is enabled.
12 // So ensure Eigen is used with the correct flags.
13 #ifndef SWIG
14 #ifndef ALICEVISION_EIGEN_REQUIRE_ALIGNMENT
15  #ifndef EIGEN_MAX_ALIGN_BYTES
16  #error "EIGEN_MAX_ALIGN_BYTES is not defined"
17  #elif EIGEN_MAX_ALIGN_BYTES != 0
18  #error "EIGEN_MAX_ALIGN_BYTES is defined but not 0"
19  #endif
20 
21  #ifndef EIGEN_MAX_STATIC_ALIGN_BYTES
22  #error "EIGEN_MAX_STATIC_ALIGN_BYTES is not defined"
23  #elif EIGEN_MAX_STATIC_ALIGN_BYTES != 0
24  #error "EIGEN_MAX_STATIC_ALIGN_BYTES is defined but not 0"
25  #endif
26 #endif
27 #endif
28 
29 //--
30 // Eigen
31 // http://eigen.tuxfamily.org/dox-devel/QuickRefPage.html
32 //--
33 #include <Eigen/Core>
34 #include <Eigen/Eigenvalues>
35 #include <Eigen/Geometry>
36 #include <Eigen/LU>
37 #include <Eigen/QR>
38 #include <Eigen/SparseCore>
39 #include <Eigen/SVD>
40 #include <Eigen/StdVector>
41 #include <boost/math/constants/constants.hpp>
42 
43 #include <algorithm>
44 #include <cmath>
45 #include <numeric>
46 #include <string>
47 #include <iostream>
48 #include <vector>
49 
50 namespace aliceVision {
51 
52 // Check MSVC
53 #if _WIN32 || _WIN64
54  #if _WIN64
55  #define ENV64BIT
56  #else
57  #define ENV32BIT
58  #endif
59 #endif
60 
61 // Check GCC
62 #if __GNUC__
63  #if __x86_64__ || __ppc64__ || _LP64
64  #define ENV64BIT
65  #else
66  #define ENV32BIT
67  #endif
68 #endif
69 
70 using Eigen::Map;
71 
72 using EigenDoubleTraits = Eigen::NumTraits<double>;
73 
74 using Vec3 = Eigen::Vector3d;
75 using Vec3i = Eigen::Vector3i;
76 using Vec3f = Eigen::Vector3f;
77 
78 using Vec2i = Eigen::Vector2i;
79 using Vec2f = Eigen::Vector2f;
80 
81 using Vec9 = Eigen::Matrix<double, 9, 1>;
82 
83 using Quaternion = Eigen::Quaternion<double>;
84 
85 using Mat3 = Eigen::Matrix<double, 3, 3>;
86 
87 #if defined(ENV32BIT)
88 using Mat23 = Eigen::Matrix<double, 2, 3, Eigen::DontAlign>;
89 using Mat34 = Eigen::Matrix<double, 3, 4, Eigen::DontAlign>;
90 using Vec2 = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
91 using Vec4 = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>;
92 using Vec6 = Eigen::Matrix<double, 6, 1, Eigen::DontAlign>;
93 #else // 64 bits compiler
94 using Mat23 = Eigen::Matrix<double, 2, 3>;
95 using Mat34 = Eigen::Matrix<double, 3, 4>;
96 using Vec2 = Eigen::Vector2d;
97 using Vec4 = Eigen::Vector4d;
98 using Vec6 = Eigen::Matrix<double, 6, 1>;
99 #endif
100 
101 using Mat4 = Eigen::Matrix<double, 4, 4>;
102 using Matu = Eigen::Matrix<unsigned int, Eigen::Dynamic, Eigen::Dynamic>;
103 
104 using RMat3 = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>;
105 
106 //-- General purpose Matrix and Vector
107 using Mat = Eigen::MatrixXd;
108 using Vec = Eigen::VectorXd;
109 using Vecu = Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>;
110 using Matf = Eigen::MatrixXf;
111 using Vecf = Eigen::VectorXf;
112 using Vecb = Eigen::Matrix<bool, Eigen::Dynamic, 1>;
113 
114 using Mat2X = Eigen::Matrix<double, 2, Eigen::Dynamic>;
115 using Mat3X = Eigen::Matrix<double, 3, Eigen::Dynamic>;
116 using Mat4X = Eigen::Matrix<double, 4, Eigen::Dynamic>;
117 
118 using MatX9 = Eigen::Matrix<double, Eigen::Dynamic, 9>;
119 using Mat9 = Eigen::Matrix<double, 9, 9>;
120 
121 //-- Sparse Matrix (Column major, and row major)
122 using sMat = Eigen::SparseMatrix<double>;
123 using sRMat = Eigen::SparseMatrix<double, Eigen::RowMajor>;
124 
125 //--------------
126 //-- Function --
127 //--------------
128 
130 
131 template<typename T>
132 inline T Square(T x)
133 {
134  return x * x;
135 }
136 
138 
139 template<typename T>
140 inline T clamp(const T& val, const T& min, const T& max)
141 {
142  return std::max(min, std::min(val, max));
143  //(val < min) ? val : ((val>max) ? val : max);
144 }
145 
146 inline bool isSimilar(double a, double b)
147 {
148  const double diff = a - b;
149  return std::abs(diff) < 1e-8;
150 }
151 inline bool isSimilar(float a, float b)
152 {
153  const float diff = a - b;
154  return std::abs(diff) < 1e-8f;
155 }
156 
162 Mat23 SkewMatMinimal(const Vec2& x);
163 
169 Mat3 CrossProductMatrix(const Vec3& x);
170 
171 // Create a rotation matrix around axis X with the provided radian angle
172 Mat3 RotationAroundX(double angle);
173 
174 // Create a rotation matrix around axis Y with the provided radian angle
175 Mat3 RotationAroundY(double angle);
176 
177 // Create a rotation matrix around axis Z with the provided radian angle
178 Mat3 RotationAroundZ(double angle);
179 
180 Mat3 rotationXYZ(double angleX, double angleY, double angleZ);
181 
182 // Degree to Radian (suppose input in [0;360])
183 template<typename T>
184 inline T degreeToRadian(T degree)
185 {
186  static_assert(std::is_floating_point<T>::value, "degreeToRadian: must be floating point.");
187  return degree * boost::math::constants::pi<T>() / 180.0;
188 }
189 
190 // Radian to degree
191 template<typename T>
192 inline T radianToDegree(T radian)
193 {
194  static_assert(std::is_floating_point<T>::value, "radianToDegree: must be floating point.");
195  return radian / boost::math::constants::pi<T>() * 180.0;
196 }
197 
200 double getRotationMagnitude(const Mat3& R2);
201 
209 double rotationDifference(const Mat3& R1, const Mat3& R2);
210 
211 inline double SIGN(double x) { return x < 0.0 ? -1.0 : 1.0; }
212 
213 // L1 norm = Sum (|x0| + |x1| + |xn|)
214 
215 template<typename TVec>
216 inline double NormL1(const TVec& x)
217 {
218  return x.array().abs().sum();
219 }
220 
221 // L2 norm = Sqrt (Sum (x0^2 + x1^2 + xn^2))
222 
223 template<typename TVec>
224 inline double NormL2(const TVec& x)
225 {
226  return x.norm();
227 }
228 
229 // LInfinity norm = max (|x0|, |x1|, ..., |xn|)
230 
231 template<typename TVec>
232 inline double NormLInfinity(const TVec& x)
233 {
234  return x.array().abs().maxCoeff();
235 }
236 
237 template<typename TVec>
238 inline double DistanceL1(const TVec& x, const TVec& y)
239 {
240  return (x - y).array().abs().sum();
241 }
242 
243 template<typename TVec>
244 inline double DistanceL2(const TVec& x, const TVec& y)
245 {
246  return (x - y).norm();
247 }
248 
249 template<typename TVec>
250 inline double DistanceLInfinity(const TVec& x, const TVec& y)
251 {
252  return NormLInfinity(x - y);
253 }
254 
255 template<typename TVec>
256 inline bool AreVecNearEqual(const TVec& x, const TVec& y, const double epsilon)
257 {
258  assert(x.cols() == y.cols());
259  for (typename TVec::Index i = 0; i < x.cols(); ++i)
260  {
261  if ((y(i) - epsilon > x(i)) || (x(i) > y(i) + epsilon))
262  return false;
263  }
264  return true;
265 }
266 
267 template<typename TMat>
268 inline bool AreMatNearEqual(const TMat& X, const TMat& Y, const double epsilon)
269 {
270  assert(X.cols() == Y.cols());
271  assert(X.rows() == Y.rows());
272  for (typename TMat::Index i = 0; i < X.rows(); ++i)
273  {
274  for (typename TMat::Index j = 0; j < X.cols(); ++j)
275  {
276  if ((Y(i, j) - epsilon > X(i, j)) || (X(i, j) > Y(i, j) + epsilon))
277  return false;
278  }
279  }
280  return true;
281 }
282 
283 // Make a rotation matrix such that center becomes the direction of the
284 // positive z-axis, and y is oriented close to up by default.
285 Mat3 LookAt(const Vec3& center, const Vec3& up = Vec3::UnitY());
286 
287 Mat3 LookAt2(const Vec3& eyePosition3D, const Vec3& center3D = Vec3::Zero(), const Vec3& upVector3D = Vec3::UnitY());
288 
289 #define SUM_OR_DYNAMIC(x, y) (x == Eigen::Dynamic || y == Eigen::Dynamic) ? Eigen::Dynamic : (x + y)
290 
291 template<typename Derived1, typename Derived2>
293 {
294  using Scalar = typename Derived1::Scalar;
295 
296  enum
297  {
298  RowsAtCompileTime = Derived1::RowsAtCompileTime,
299  ColsAtCompileTime = SUM_OR_DYNAMIC(Derived1::ColsAtCompileTime, Derived2::ColsAtCompileTime),
300  Options = Derived1::Flags & Eigen::RowMajorBit ? Eigen::RowMajor : 0,
301  MaxRowsAtCompileTime = Derived1::MaxRowsAtCompileTime,
302  MaxColsAtCompileTime = SUM_OR_DYNAMIC(Derived1::MaxColsAtCompileTime, Derived2::MaxColsAtCompileTime)
303  };
304  typedef Eigen::Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime> type;
305 };
306 
307 template<typename Derived1, typename Derived2>
308 typename hstack_return<Derived1, Derived2>::type HStack(const Eigen::MatrixBase<Derived1>& lhs, const Eigen::MatrixBase<Derived2>& rhs)
309 {
310  typename hstack_return<Derived1, Derived2>::type res;
311  res.resize(lhs.rows(), lhs.cols() + rhs.cols());
312  res << lhs, rhs;
313  return res;
314 }
315 
316 template<typename Derived1, typename Derived2>
318 {
319  using Scalar = typename Derived1::Scalar;
320 
321  enum
322  {
323  RowsAtCompileTime = SUM_OR_DYNAMIC(Derived1::RowsAtCompileTime, Derived2::RowsAtCompileTime),
324  ColsAtCompileTime = Derived1::ColsAtCompileTime,
325  Options = Derived1::Flags & Eigen::RowMajorBit ? Eigen::RowMajor : 0,
326  MaxRowsAtCompileTime = SUM_OR_DYNAMIC(Derived1::MaxRowsAtCompileTime, Derived2::MaxRowsAtCompileTime),
327  MaxColsAtCompileTime = Derived1::MaxColsAtCompileTime
328  };
329  typedef Eigen::Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime> type;
330 };
331 
332 template<typename Derived1, typename Derived2>
333 typename vstack_return<Derived1, Derived2>::type VStack(const Eigen::MatrixBase<Derived1>& lhs, const Eigen::MatrixBase<Derived2>& rhs)
334 {
335  typename vstack_return<Derived1, Derived2>::type res;
336  res.resize(lhs.rows() + rhs.rows(), lhs.cols());
337  res << lhs, rhs;
338  return res;
339 }
340 #undef SUM_OR_DYNAMIC
341 
342 template<typename TMat>
343 inline double FrobeniusNorm(const TMat& A)
344 {
345  return A.norm();
346 }
347 
348 template<typename TMat>
349 inline double FrobeniusDistance(const TMat& A, const TMat& B)
350 {
351  return FrobeniusNorm(A - B);
352 }
353 
354 template<class TMat>
355 double CosinusBetweenMatrices(const TMat& a, const TMat& b)
356 {
357  return (a.array() * b.array()).sum() / FrobeniusNorm(a) / FrobeniusNorm(b);
358 }
359 
369 template<typename T>
370 void pick(std::vector<T>& result, const std::vector<T>& input, const std::vector<typename std::vector<T>::size_type>& selection)
371 {
372  result.reserve(selection.size());
373  std::transform(
374  selection.begin(), selection.end(), std::back_inserter(result), [&input](typename std::vector<T>::size_type idx) { return input.at(idx); });
375 }
376 
377 void MeanAndVarianceAlongRows(const Mat& A, Vec* mean_pointer, Vec* variance_pointer);
378 
379 bool exportMatToTextFile(const Mat& mat, const std::string& filename, const std::string& sPrefix = "A");
380 
381 inline int is_finite(const double val)
382 {
383 #ifdef _MSC_VER
384  return _finite(val);
385 #else
386  return std::isfinite(val);
387 #endif
388 }
389 
403 template<typename T>
404 void SplitRange(const T range_start, const T range_end, const int nb_split, std::vector<T>& d_range)
405 {
406  const T range_length = range_end - range_start;
407  if (range_length < nb_split)
408  {
409  d_range.push_back(range_start);
410  d_range.push_back(range_end);
411  }
412  else
413  {
414  const T delta_range = range_length / nb_split;
415 
416  d_range.push_back(range_start);
417  for (int i = 1; i < nb_split; ++i)
418  {
419  d_range.push_back(range_start + i * delta_range);
420  }
421  d_range.push_back(range_end);
422  }
423 }
424 
425 template<class T>
426 constexpr T divideRoundUp(T x, T y)
427 {
428  static_assert(std::is_integral<T>::value, "divideRoundUp only works with integer arguments");
429  assert(y != 0); // Prevents division by zero
430  const auto xPos = x >= 0;
431  const auto yPos = y >= 0;
432  if (xPos == yPos)
433  {
434  return x / y + T((x % y) != 0);
435  }
436  else
437  {
438  // negative result, rounds towards zero anyways
439  return x / y;
440  }
441 }
442 
452 
453 } // namespace aliceVision
aliceVision::vstack_return
Definition: numeric.hpp:317
aliceVision::hstack_return
Definition: numeric.hpp:292
aliceVision::rotationDifference
double rotationDifference(const Mat3 &R1, const Mat3 &R2)
Compute the angle between two rotation matrices.
Definition: numeric.cpp:47
aliceVision
Definition: checkerDetector.cpp:32
aliceVision::makeRandomOperationsReproducible
void makeRandomOperationsReproducible()
Definition: numeric.cpp:146
aliceVision::SplitRange
void SplitRange(const T range_start, const T range_end, const int nb_split, std::vector< T > &d_range)
Definition: numeric.hpp:404
aliceVision::clamp
T clamp(const T &val, const T &min, const T &max)
Clamp return the number if inside range, else min or max range.
Definition: numeric.hpp:140
aliceVision::getRotationMagnitude
double getRotationMagnitude(const Mat3 &R2)
Definition: numeric.cpp:39
aliceVision::CrossProductMatrix
Mat3 CrossProductMatrix(const Vec3 &x)
Create a cross product matrix from a 3d vector.
Definition: numeric.cpp:24
aliceVision::Square
T Square(T x)
Return the square of a number.
Definition: numeric.hpp:132
aliceVision::pick
void pick(std::vector< T > &result, const std::vector< T > &input, const std::vector< typename std::vector< T >::size_type > &selection)
Given a vector of element and a vector containing a selection of its indices, it returns a new vector...
Definition: numeric.hpp:370
aliceVision::SkewMatMinimal
Mat23 SkewMatMinimal(const Vec2 &x)
Create a minimal skew matrix from a 2d vector.
Definition: numeric.cpp:17