ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Eigen.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 // LOCAL
8 #include "Eigen.h"
9 
10 #include "Logging.h"
11 
12 // EIGEN
13 #include <Eigen/Geometry>
14 #include <Eigen/Sparse>
15 #include <functional>
16 
17 namespace cloudViewer {
18 namespace utility {
19 
21 std::tuple<bool, Eigen::VectorXd> SolveLinearSystemPSD(
22  const Eigen::MatrixXd& A,
23  const Eigen::VectorXd& b,
24  bool prefer_sparse /* = false */,
25  bool check_symmetric /* = false */,
26  bool check_det /* = false */,
27  bool check_psd /* = false */) {
28  // PSD implies symmetric
29  check_symmetric = check_symmetric || check_psd;
30  if (check_symmetric && !A.isApprox(A.transpose())) {
31  LogWarning("check_symmetric failed, empty vector will be returned");
32  return std::make_tuple(false, Eigen::VectorXd::Zero(b.rows()));
33  }
34 
35  if (check_det) {
36  double det = A.determinant();
37  if (fabs(det) < 1e-6 || std::isnan(det) || std::isinf(det)) {
38  LogWarning("check_det failed, empty vector will be returned");
39  return std::make_tuple(false, Eigen::VectorXd::Zero(b.rows()));
40  }
41  }
42 
43  // Check PSD: https://stackoverflow.com/a/54569657/1255535
44  if (check_psd) {
45  Eigen::LLT<Eigen::MatrixXd> A_llt(A);
46  if (A_llt.info() == Eigen::NumericalIssue) {
47  LogWarning("check_psd failed, empty vector will be returned");
48  return std::make_tuple(false, Eigen::VectorXd::Zero(b.rows()));
49  }
50  }
51 
52  Eigen::VectorXd x(b.size());
53 
54  if (prefer_sparse) {
55  Eigen::SparseMatrix<double> A_sparse = A.sparseView();
56  // TODO: avoid deprecated API SimplicialCholesky
57  Eigen::SimplicialCholesky<Eigen::SparseMatrix<double>> A_chol;
58  A_chol.compute(A_sparse);
59  if (A_chol.info() == Eigen::Success) {
60  x = A_chol.solve(b);
61  if (A_chol.info() == Eigen::Success) {
62  // Both decompose and solve are successful
63  return std::make_tuple(true, std::move(x));
64  } else {
65  LogWarning("Cholesky solve failed, switched to dense solver");
66  }
67  } else {
68  LogWarning("Cholesky decompose failed, switched to dense solver");
69  }
70  }
71 
72  x = A.ldlt().solve(b);
73  return std::make_tuple(true, std::move(x));
74 }
75 
76 Eigen::Matrix4d TransformVector6dToMatrix4d(const Eigen::Vector6d& input) {
77  Eigen::Matrix4d output;
78  output.setIdentity();
79  output.block<3, 3>(0, 0) =
80  (Eigen::AngleAxisd(input(2), Eigen::Vector3d::UnitZ()) *
81  Eigen::AngleAxisd(input(1), Eigen::Vector3d::UnitY()) *
82  Eigen::AngleAxisd(input(0), Eigen::Vector3d::UnitX()))
83  .matrix();
84  output.block<3, 1>(0, 3) = input.block<3, 1>(3, 0);
85  return output;
86 }
87 
88 Eigen::Vector6d TransformMatrix4dToVector6d(const Eigen::Matrix4d& input) {
89  Eigen::Vector6d output;
90  Eigen::Matrix3d R = input.block<3, 3>(0, 0);
91  double sy = sqrt(R(0, 0) * R(0, 0) + R(1, 0) * R(1, 0));
92  if (!(sy < 1e-6)) {
93  output(0) = atan2(R(2, 1), R(2, 2));
94  output(1) = atan2(-R(2, 0), sy);
95  output(2) = atan2(R(1, 0), R(0, 0));
96  } else {
97  output(0) = atan2(-R(1, 2), R(1, 1));
98  output(1) = atan2(-R(2, 0), sy);
99  output(2) = 0;
100  }
101  output.block<3, 1>(3, 0) = input.block<3, 1>(0, 3);
102  return output;
103 }
104 
105 std::tuple<bool, Eigen::Matrix4d> SolveJacobianSystemAndObtainExtrinsicMatrix(
106  const Eigen::Matrix6d& JTJ, const Eigen::Vector6d& JTr) {
107  bool solution_exist;
108  Eigen::Vector6d x;
109  std::tie(solution_exist, x) = SolveLinearSystemPSD(JTJ, -JTr);
110 
111  if (solution_exist) {
112  Eigen::Matrix4d extrinsic = TransformVector6dToMatrix4d(x);
113  return std::make_tuple(solution_exist, std::move(extrinsic));
114  }
115  return std::make_tuple(false, Eigen::Matrix4d::Identity());
116 }
117 
118 std::tuple<bool, std::vector<Eigen::Matrix4d, Matrix4d_allocator>>
120  const Eigen::VectorXd& JTr) {
121  std::vector<Eigen::Matrix4d, Matrix4d_allocator> output_matrix_array;
122  output_matrix_array.clear();
123  if (JTJ.rows() != JTr.rows() || JTJ.cols() % 6 != 0) {
124  LogWarning(
125  "[SolveJacobianSystemAndObtainExtrinsicMatrixArray] "
126  "Unsupported matrix format.");
127  return std::make_tuple(false, std::move(output_matrix_array));
128  }
129 
130  bool solution_exist;
131  Eigen::VectorXd x;
132  std::tie(solution_exist, x) = SolveLinearSystemPSD(JTJ, -JTr);
133 
134  if (solution_exist) {
135  int nposes = (int)x.rows() / 6;
136  for (int i = 0; i < nposes; i++) {
137  Eigen::Matrix4d extrinsic =
138  TransformVector6dToMatrix4d(x.block<6, 1>(i * 6, 0));
139  output_matrix_array.push_back(extrinsic);
140  }
141  return std::make_tuple(solution_exist, std::move(output_matrix_array));
142  } else {
143  return std::make_tuple(false, std::move(output_matrix_array));
144  }
145 }
146 
147 template <typename MatType, typename VecType>
148 std::tuple<MatType, VecType, double> ComputeJTJandJTr(
149  std::function<void(int, VecType&, double&, double&)> f,
150  int iteration_num,
151  bool verbose /*=true*/) {
152  MatType JTJ;
153  VecType JTr;
154  double r2_sum = 0.0;
155  JTJ.setZero();
156  JTr.setZero();
157 #pragma omp parallel
158  {
159  MatType JTJ_private;
160  VecType JTr_private;
161  double r2_sum_private = 0.0;
162  JTJ_private.setZero();
163  JTr_private.setZero();
164  VecType J_r;
165  double r;
166  double w = 0.0;
167 #pragma omp for nowait
168  for (int i = 0; i < iteration_num; i++) {
169  f(i, J_r, r, w);
170  JTJ_private.noalias() += J_r * w * J_r.transpose();
171  JTr_private.noalias() += J_r * w * r;
172  r2_sum_private += r * r;
173  }
174 #pragma omp critical(ComputeJTJandJTr)
175  {
176  JTJ += JTJ_private;
177  JTr += JTr_private;
178  r2_sum += r2_sum_private;
179  }
180  }
181  if (verbose) {
182  LogDebug("Residual : {:.2e} (# of elements : {:d})",
183  r2_sum / (double)iteration_num, iteration_num);
184  }
185  return std::make_tuple(std::move(JTJ), std::move(JTr), r2_sum);
186 }
187 
188 template <typename MatType, typename VecType>
189 std::tuple<MatType, VecType, double> ComputeJTJandJTr(
190  std::function<
191  void(int,
192  std::vector<VecType, Eigen::aligned_allocator<VecType>>&,
193  std::vector<double>&,
194  std::vector<double>&)> f,
195  int iteration_num,
196  bool verbose /*=true*/) {
197  MatType JTJ;
198  VecType JTr;
199  double r2_sum = 0.0;
200  JTJ.setZero();
201  JTr.setZero();
202 #pragma omp parallel
203  {
204  MatType JTJ_private;
205  VecType JTr_private;
206  double r2_sum_private = 0.0;
207  JTJ_private.setZero();
208  JTr_private.setZero();
209  std::vector<double> r;
210  std::vector<double> w;
211  std::vector<VecType, Eigen::aligned_allocator<VecType>> J_r;
212 #pragma omp for nowait
213  for (int i = 0; i < iteration_num; i++) {
214  f(i, J_r, r, w);
215  for (int j = 0; j < (int)r.size(); j++) {
216  JTJ_private.noalias() += J_r[j] * w[j] * J_r[j].transpose();
217  JTr_private.noalias() += J_r[j] * w[j] * r[j];
218  r2_sum_private += r[j] * r[j];
219  }
220  }
221 #pragma omp critical(ComputeJTJandJTr)
222  {
223  JTJ += JTJ_private;
224  JTr += JTr_private;
225  r2_sum += r2_sum_private;
226  }
227  }
228  if (verbose) {
229  LogDebug("Residual : {:.2e} (# of elements : {:d})",
230  r2_sum / (double)iteration_num, iteration_num);
231  }
232  return std::make_tuple(std::move(JTJ), std::move(JTr), r2_sum);
233 }
234 
235 // clang-format off
236 template std::tuple<Eigen::Matrix6d, Eigen::Vector6d, double> CV_CORE_LIB_API ComputeJTJandJTr(
237  std::function<void(int, Eigen::Vector6d&, double&, double&)> f,
238  int iteration_num, bool verbose);
239 
240 template std::tuple<Eigen::Matrix6d, Eigen::Vector6d, double> CV_CORE_LIB_API ComputeJTJandJTr(
241  std::function<void(int,
242  std::vector<Eigen::Vector6d, Vector6d_allocator>&,
243  std::vector<double>&,
244  std::vector<double>&)> f,
245  int iteration_num, bool verbose);
246 // clang-format on
247 
248 Eigen::Matrix3d RotationMatrixX(double radians) {
249  Eigen::Matrix3d rot;
250  rot << 1, 0, 0, 0, std::cos(radians), -std::sin(radians), 0,
251  std::sin(radians), std::cos(radians);
252  return rot;
253 }
254 
255 Eigen::Matrix3d RotationMatrixY(double radians) {
256  Eigen::Matrix3d rot;
257  rot << std::cos(radians), 0, std::sin(radians), 0, 1, 0, -std::sin(radians),
258  0, std::cos(radians);
259  return rot;
260 }
261 
262 Eigen::Matrix3d RotationMatrixZ(double radians) {
263  Eigen::Matrix3d rot;
264  rot << std::cos(radians), -std::sin(radians), 0, std::sin(radians),
265  std::cos(radians), 0, 0, 0, 1;
266  return rot;
267 }
268 
269 Eigen::Vector3uint8 ColorToUint8(const Eigen::Vector3d& color) {
271  for (int i = 0; i < 3; ++i) {
272  rgb[i] = uint8_t(
273  std::round(std::min(1., std::max(0., color(i))) * 255.));
274  }
275  return rgb;
276 }
277 
278 Eigen::Vector3d ColorToDouble(uint8_t r, uint8_t g, uint8_t b) {
279  return Eigen::Vector3d(r, g, b) / 255.0;
280 }
281 
282 Eigen::Vector3d ColorToDouble(const Eigen::Vector3uint8& rgb) {
283  return ColorToDouble(rgb(0), rgb(1), rgb(2));
284 }
285 
286 template <typename IdxType>
287 Eigen::Matrix3d ComputeCovariance(const std::vector<Eigen::Vector3d>& points,
288  const std::vector<IdxType>& indices) {
289  if (indices.empty()) {
290  return Eigen::Matrix3d::Identity();
291  }
292  Eigen::Matrix3d covariance;
293  Eigen::Matrix<double, 9, 1> cumulants;
294  cumulants.setZero();
295  for (const auto& idx : indices) {
296  const Eigen::Vector3d& point = points[idx];
297  cumulants(0) += point(0);
298  cumulants(1) += point(1);
299  cumulants(2) += point(2);
300  cumulants(3) += point(0) * point(0);
301  cumulants(4) += point(0) * point(1);
302  cumulants(5) += point(0) * point(2);
303  cumulants(6) += point(1) * point(1);
304  cumulants(7) += point(1) * point(2);
305  cumulants(8) += point(2) * point(2);
306  }
307  cumulants /= (double)indices.size();
308  covariance(0, 0) = cumulants(3) - cumulants(0) * cumulants(0);
309  covariance(1, 1) = cumulants(6) - cumulants(1) * cumulants(1);
310  covariance(2, 2) = cumulants(8) - cumulants(2) * cumulants(2);
311  covariance(0, 1) = cumulants(4) - cumulants(0) * cumulants(1);
312  covariance(1, 0) = covariance(0, 1);
313  covariance(0, 2) = cumulants(5) - cumulants(0) * cumulants(2);
314  covariance(2, 0) = covariance(0, 2);
315  covariance(1, 2) = cumulants(7) - cumulants(1) * cumulants(2);
316  covariance(2, 1) = covariance(1, 2);
317  return covariance;
318 }
319 
320 template <typename IdxType>
321 std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
322  const std::vector<Eigen::Vector3d>& points,
323  const std::vector<IdxType>& indices) {
324  Eigen::Vector3d mean;
325  Eigen::Matrix3d covariance;
326  Eigen::Matrix<double, 9, 1> cumulants;
327  cumulants.setZero();
328  for (const auto& idx : indices) {
329  const Eigen::Vector3d& point = points[idx];
330  cumulants(0) += point(0);
331  cumulants(1) += point(1);
332  cumulants(2) += point(2);
333  cumulants(3) += point(0) * point(0);
334  cumulants(4) += point(0) * point(1);
335  cumulants(5) += point(0) * point(2);
336  cumulants(6) += point(1) * point(1);
337  cumulants(7) += point(1) * point(2);
338  cumulants(8) += point(2) * point(2);
339  }
340  cumulants /= (double)indices.size();
341  mean(0) = cumulants(0);
342  mean(1) = cumulants(1);
343  mean(2) = cumulants(2);
344  covariance(0, 0) = cumulants(3) - cumulants(0) * cumulants(0);
345  covariance(1, 1) = cumulants(6) - cumulants(1) * cumulants(1);
346  covariance(2, 2) = cumulants(8) - cumulants(2) * cumulants(2);
347  covariance(0, 1) = cumulants(4) - cumulants(0) * cumulants(1);
348  covariance(1, 0) = covariance(0, 1);
349  covariance(0, 2) = cumulants(5) - cumulants(0) * cumulants(2);
350  covariance(2, 0) = covariance(0, 2);
351  covariance(1, 2) = cumulants(7) - cumulants(1) * cumulants(2);
352  covariance(2, 1) = covariance(1, 2);
353  return std::make_tuple(mean, covariance);
354 }
355 
356 template Eigen::Matrix3d ComputeCovariance(
357  const std::vector<Eigen::Vector3d>& points,
358  const std::vector<size_t>& indices);
359 template std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
360  const std::vector<Eigen::Vector3d>& points,
361  const std::vector<size_t>& indices);
362 template Eigen::Matrix3d ComputeCovariance(
363  const std::vector<Eigen::Vector3d>& points,
364  const std::vector<int>& indices);
365 template std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
366  const std::vector<Eigen::Vector3d>& points,
367  const std::vector<int>& indices);
368 
369 template <typename RealType, typename IdxType>
370 std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
371  const RealType* const points, const std::vector<IdxType>& indices) {
372  Eigen::Vector3d mean;
373  Eigen::Matrix3d covariance;
374  Eigen::Matrix<double, 9, 1> cumulants;
375  cumulants.setZero();
376  for (const auto& idx : indices) {
377  const Eigen::Map<const Eigen::Matrix<RealType, 3, 1>> point(points +
378  3 * idx);
379  cumulants(0) += point(0);
380  cumulants(1) += point(1);
381  cumulants(2) += point(2);
382  cumulants(3) += point(0) * point(0);
383  cumulants(4) += point(0) * point(1);
384  cumulants(5) += point(0) * point(2);
385  cumulants(6) += point(1) * point(1);
386  cumulants(7) += point(1) * point(2);
387  cumulants(8) += point(2) * point(2);
388  }
389  cumulants /= (double)indices.size();
390  mean(0) = cumulants(0);
391  mean(1) = cumulants(1);
392  mean(2) = cumulants(2);
393  covariance(0, 0) = cumulants(3) - cumulants(0) * cumulants(0);
394  covariance(1, 1) = cumulants(6) - cumulants(1) * cumulants(1);
395  covariance(2, 2) = cumulants(8) - cumulants(2) * cumulants(2);
396  covariance(0, 1) = cumulants(4) - cumulants(0) * cumulants(1);
397  covariance(1, 0) = covariance(0, 1);
398  covariance(0, 2) = cumulants(5) - cumulants(0) * cumulants(2);
399  covariance(2, 0) = covariance(0, 2);
400  covariance(1, 2) = cumulants(7) - cumulants(1) * cumulants(2);
401  covariance(2, 1) = covariance(1, 2);
402  return std::make_tuple(mean, covariance);
403 }
404 
405 template std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
406  const float* const points, const std::vector<size_t>& indices);
407 template std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
408  const double* const points, const std::vector<size_t>& indices);
409 template std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
410  const float* const points, const std::vector<int>& indices);
411 template std::tuple<Eigen::Vector3d, Eigen::Matrix3d> ComputeMeanAndCovariance(
412  const double* const points, const std::vector<int>& indices);
413 
414 Eigen::Matrix3d SkewMatrix(const Eigen::Vector3d& vec) {
415  Eigen::Matrix3d skew;
416  // clang-format off
417  skew << 0, -vec.z(), vec.y(),
418  vec.z(), 0, -vec.x(),
419  -vec.y(), vec.x(), 0;
420  // clang-format on
421  return skew;
422 }
423 
424 } // namespace utility
425 } // namespace cloudViewer
#define CV_CORE_LIB_API
Definition: CVCoreLibWin.h:15
int points
math::float4 color
#define LogWarning(...)
Definition: Logging.h:72
#define LogDebug(...)
Definition: Logging.h:90
__host__ __device__ float2 fabs(float2 v)
Definition: cutil_math.h:1254
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
Eigen::Matrix< double, 6, 1 > Vector6d
Definition: Eigen.h:107
Eigen::Matrix< double, 6, 6 > Matrix6d
Extending Eigen namespace by adding frequently used matrix type.
Definition: Eigen.h:106
Eigen::Matrix< uint8_t, 3, 1 > Vector3uint8
Definition: Eigen.h:108
std::tuple< Eigen::Vector3d, Eigen::Matrix3d > ComputeMeanAndCovariance(const std::vector< Eigen::Vector3d > &points, const std::vector< IdxType > &indices)
Function to compute the mean and covariance matrix of a set of points.
Definition: Eigen.cpp:321
std::tuple< bool, std::vector< Eigen::Matrix4d, Matrix4d_allocator > > SolveJacobianSystemAndObtainExtrinsicMatrixArray(const Eigen::MatrixXd &JTJ, const Eigen::VectorXd &JTr)
Definition: Eigen.cpp:119
Eigen::Matrix3d RotationMatrixX(double radians)
Definition: Eigen.cpp:248
std::tuple< bool, Eigen::VectorXd > SolveLinearSystemPSD(const Eigen::MatrixXd &A, const Eigen::VectorXd &b, bool prefer_sparse=false, bool check_symmetric=false, bool check_det=false, bool check_psd=false)
Function to solve Ax=b.
Definition: Eigen.cpp:21
Eigen::Matrix3d RotationMatrixY(double radians)
Definition: Eigen.cpp:255
Eigen::Matrix3d ComputeCovariance(const std::vector< Eigen::Vector3d > &points, const std::vector< IdxType > &indices)
Function to compute the covariance matrix of a set of points.
Definition: Eigen.cpp:287
Eigen::Vector6d TransformMatrix4dToVector6d(const Eigen::Matrix4d &input)
Definition: Eigen.cpp:88
Eigen::Matrix3d SkewMatrix(const Eigen::Vector3d &vec)
Genretate a skew-symmetric matrix from a vector 3x1.
Definition: Eigen.cpp:414
Eigen::Vector3uint8 ColorToUint8(const Eigen::Vector3d &color)
Definition: Eigen.cpp:269
Eigen::Matrix4d TransformVector6dToMatrix4d(const Eigen::Vector6d &input)
Definition: Eigen.cpp:76
Eigen::Vector3d ColorToDouble(uint8_t r, uint8_t g, uint8_t b)
Color conversion from uint8_t 0-255 to double [0,1].
Definition: Eigen.cpp:278
Eigen::Matrix3d RotationMatrixZ(double radians)
Definition: Eigen.cpp:262
std::tuple< bool, Eigen::Matrix4d > SolveJacobianSystemAndObtainExtrinsicMatrix(const Eigen::Matrix6d &JTJ, const Eigen::Vector6d &JTr)
Definition: Eigen.cpp:105
std::tuple< MatType, VecType, double > ComputeJTJandJTr(std::function< void(int, VecType &, double &, double &)> f, int iteration_num, bool verbose=true)
Definition: Eigen.cpp:148
Generic file read and write utility for python interface.
Definition: lsd.c:149