AlignUtils.cpp
Go to the documentation of this file.
1 /*
2  * OpenVINS: An Open Platform for Visual-Inertial Research
3  * Copyright (C) 2018-2023 Patrick Geneva
4  * Copyright (C) 2018-2023 Guoquan Huang
5  * Copyright (C) 2018-2023 OpenVINS Contributors
6  * Copyright (C) 2018-2019 Kevin Eckenhoff
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "AlignUtils.h"
23 
24 using namespace ov_eval;
25 
26 void AlignUtils::align_umeyama(const std::vector<Eigen::Matrix<double, 3, 1>> &data, const std::vector<Eigen::Matrix<double, 3, 1>> &model,
27  Eigen::Matrix<double, 3, 3> &R, Eigen::Matrix<double, 3, 1> &t, double &s, bool known_scale, bool yaw_only) {
28 
29  assert(model.size() == data.size());
30 
31  // Substract mean of each trajectory
32  Eigen::Matrix<double, 3, 1> mu_M = get_mean(model);
33  Eigen::Matrix<double, 3, 1> mu_D = get_mean(data);
34  std::vector<Eigen::Matrix<double, 3, 1>> model_zerocentered, data_zerocentered;
35  for (size_t i = 0; i < model.size(); i++) {
36  model_zerocentered.push_back(model[i] - mu_M);
37  data_zerocentered.push_back(data[i] - mu_D);
38  }
39 
40  // Get correlation matrix
41  double n = model.size();
42  Eigen::Matrix<double, 3, 3> C = Eigen::Matrix<double, 3, 3>::Zero();
43  for (size_t i = 0; i < model_zerocentered.size(); i++) {
44  C.noalias() += model_zerocentered[i] * data_zerocentered[i].transpose();
45  }
46  C *= 1.0 / n;
47 
48  // Get data sigma
49  double sigma2 = 0;
50  for (size_t i = 0; i < data_zerocentered.size(); i++) {
51  sigma2 += data_zerocentered[i].dot(data_zerocentered[i]);
52  }
53  sigma2 *= 1.0 / n;
54 
55  // SVD decomposition
56  Eigen::JacobiSVD<Eigen::Matrix<double, 3, 3>> svd(C, Eigen::ComputeFullV | Eigen::ComputeFullU);
57 
58  Eigen::Matrix<double, 3, 3> U_svd = svd.matrixU();
59  Eigen::Matrix<double, 3, 1> D_svd = svd.singularValues();
60  Eigen::Matrix<double, 3, 3> V_svd = svd.matrixV();
61 
62  Eigen::Matrix<double, 3, 3> S = Eigen::Matrix<double, 3, 3>::Identity();
63  if (U_svd.determinant() * V_svd.determinant() < 0) {
64  S(2, 2) = -1;
65  }
66 
67  // If only yaw, use that specific solver (optimizes over yaw angle)
68  // Else get best full 3 dof rotation
69  if (yaw_only) {
70  Eigen::Matrix<double, 3, 3> rot_C = n * C.transpose();
71  double theta = AlignUtils::get_best_yaw(rot_C);
72  R = ov_core::rot_z(theta);
73  } else {
74  R.noalias() = U_svd * S * V_svd.transpose();
75  }
76 
77  // If known scale, fix it
78  if (known_scale) {
79  s = 1;
80  } else {
81  // Get best scale
82  s = 1.0 / sigma2 * (D_svd.asDiagonal() * S).trace();
83  }
84 
85  // Get best translation
86  t.noalias() = mu_M - s * R * mu_D;
87 
88  // Debug printing
89  // std::stringstream ss;
90  // ss << "R- " << std::endl << R << std::endl;
91  // ss << "t- " << std::endl << t << std::endl;
92  // PRINT_DEBUG(ss.str().c_str());
93 }
94 
95 void AlignUtils::perform_association(double offset, double max_difference, std::vector<double> &est_times, std::vector<double> &gt_times,
96  std::vector<Eigen::Matrix<double, 7, 1>> &est_poses,
97  std::vector<Eigen::Matrix<double, 7, 1>> &gt_poses) {
98  std::vector<Eigen::Matrix3d> est_covori, est_covpos, gt_covori, gt_covpos;
99  AlignUtils::perform_association(offset, max_difference, est_times, gt_times, est_poses, gt_poses, est_covori, est_covpos, gt_covori,
100  gt_covpos);
101 }
102 
103 void AlignUtils::perform_association(double offset, double max_difference, std::vector<double> &est_times, std::vector<double> &gt_times,
104  std::vector<Eigen::Matrix<double, 7, 1>> &est_poses,
105  std::vector<Eigen::Matrix<double, 7, 1>> &gt_poses, std::vector<Eigen::Matrix3d> &est_covori,
106  std::vector<Eigen::Matrix3d> &est_covpos, std::vector<Eigen::Matrix3d> &gt_covori,
107  std::vector<Eigen::Matrix3d> &gt_covpos) {
108 
109  // Temp results which keeps only the matches
110  std::vector<double> est_times_temp, gt_times_temp;
111  std::vector<Eigen::Matrix<double, 7, 1>> est_poses_temp, gt_poses_temp;
112  std::vector<Eigen::Matrix3d> est_covori_temp, est_covpos_temp, gt_covori_temp, gt_covpos_temp;
113 
114  // Iterator over gt (only ever increases to enforce injectivity of matches)
115  size_t gt_pointer = 0;
116 
117  // Try to find closest GT pose for each estimate
118  for (size_t i = 0; i < est_times.size(); i++) {
119 
120  // Default params
121  double best_diff = max_difference;
122  int best_gt_idx = -1;
123 
124  // Increment while too small and is not within our difference threshold
125  while (gt_pointer < gt_times.size() && gt_times.at(gt_pointer) < (est_times.at(i) + offset) &&
126  std::abs(gt_times.at(gt_pointer) - (est_times.at(i) + offset)) > max_difference) {
127  gt_pointer++;
128  }
129 
130  // If we are closer than max difference, see if we can do any better
131  while (gt_pointer < gt_times.size() && std::abs(gt_times.at(gt_pointer) - (est_times.at(i) + offset)) <= max_difference) {
132  // Break if we found a good match but are getting worse, we are done
133  if (std::abs(gt_times.at(gt_pointer) - (est_times.at(i) + offset)) >= best_diff) {
134  break;
135  }
136  // We have a closer match, save it and move on
137  best_diff = std::abs(gt_times.at(gt_pointer) - (est_times.at(i) + offset));
138  best_gt_idx = gt_pointer;
139  gt_pointer++;
140  }
141 
142  // Did we get a valid match
143  if (best_gt_idx != -1) {
144 
145  // Save estimate and gt states for the match
146  est_times_temp.push_back(gt_times.at(best_gt_idx));
147  est_poses_temp.push_back(est_poses.at(i));
148  gt_times_temp.push_back(gt_times.at(best_gt_idx));
149  gt_poses_temp.push_back(gt_poses.at(best_gt_idx));
150 
151  // If we have covariance then also append it
152  // If the groundtruth doesn't have covariance say it is 100% certain
153  if (!est_covori.empty()) {
154  assert(est_covori.size() == est_covpos.size());
155  est_covori_temp.push_back(est_covori.at(i));
156  est_covpos_temp.push_back(est_covpos.at(i));
157  if (gt_covori.empty()) {
158  gt_covori_temp.push_back(Eigen::Matrix3d::Zero());
159  gt_covpos_temp.push_back(Eigen::Matrix3d::Zero());
160  } else {
161  assert(gt_covori.size() == gt_covpos.size());
162  gt_covori_temp.push_back(gt_covori.at(best_gt_idx));
163  gt_covpos_temp.push_back(gt_covpos.at(best_gt_idx));
164  }
165  }
166  }
167  }
168 
169  // Ensure that we have enough associations
170  if (est_times.size() < 3) {
171  PRINT_ERROR(RED "[ALIGN]: Was unable to associate groundtruth and estimate trajectories\n" RESET);
172  PRINT_ERROR(RED "[ALIGN]: %d total matches....\n" RESET, (int)est_times.size());
173  PRINT_ERROR(RED "[ALIGN]: Do the time of the files match??\n" RESET);
174  return;
175  }
176  assert(est_times_temp.size() == gt_times_temp.size());
177  // PRINT_DEBUG("[TRAJ]: %d est poses and %d gt poses => %d
178  // matches\n",(int)est_times.size(),(int)gt_times.size(),(int)est_times_temp.size());
179 
180  // Overwrite with intersected values
181  est_times = est_times_temp;
182  est_poses = est_poses_temp;
183  est_covori = est_covori_temp;
184  est_covpos = est_covpos_temp;
185  gt_times = gt_times_temp;
186  gt_poses = gt_poses_temp;
187  gt_covori = gt_covori_temp;
188  gt_covpos = gt_covpos_temp;
189 }
static void perform_association(double offset, double max_difference, std::vector< double > &est_times, std::vector< double > &gt_times, std::vector< Eigen::Matrix< double, 7, 1 >> &est_poses, std::vector< Eigen::Matrix< double, 7, 1 >> &gt_poses)
Will intersect our loaded data so that we have common timestamps.
Definition: AlignUtils.cpp:95
#define RESET
RED
Evaluation and recording utilities.
static Eigen::Matrix< double, 3, 1 > get_mean(const std::vector< Eigen::Matrix< double, 3, 1 >> &data)
Gets mean of the vector of data.
Definition: AlignUtils.h:65
static void align_umeyama(const std::vector< Eigen::Matrix< double, 3, 1 >> &data, const std::vector< Eigen::Matrix< double, 3, 1 >> &model, Eigen::Matrix< double, 3, 3 > &R, Eigen::Matrix< double, 3, 1 > &t, double &s, bool known_scale, bool yaw_only)
Given a set of points in a model frame and a set of points in a data frame, finds best transform betw...
Definition: AlignUtils.cpp:26
static double get_best_yaw(const Eigen::Matrix< double, 3, 3 > &C)
Gets best yaw from Frobenius problem. Equation (17)-(18) in Zhang et al. 2018 IROS paper...
Definition: AlignUtils.h:53
Eigen::Matrix< double, 3, 3 > rot_z(double t)


ov_eval
Author(s): Patrick Geneva , Kevin Eckenhoff , Guoquan Huang
autogenerated on Wed Jun 21 2023 03:05:40