util3d_registration.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
29 
32 #include "rtabmap/core/util3d.h"
33 
34 #include <pcl/registration/icp.h>
35 #include <pcl/registration/transformation_estimation_2D.h>
36 #include <pcl/registration/transformation_estimation_svd.h>
37 #include <pcl/sample_consensus/sac_model_registration.h>
38 #include <pcl/sample_consensus/ransac.h>
39 #include <pcl/common/common.h>
41 #include <rtabmap/utilite/UMath.h>
42 
43 namespace rtabmap
44 {
45 
46 namespace util3d
47 {
48 
49 // Get transform from cloud2 to cloud1
51  const pcl::PointCloud<pcl::PointXYZ> & cloud1,
52  const pcl::PointCloud<pcl::PointXYZ> & cloud2)
53 {
54  pcl::registration::TransformationEstimationSVD<pcl::PointXYZ, pcl::PointXYZ> svd;
55 
56  // Perform the alignment
57  Eigen::Matrix4f matrix;
58  svd.estimateRigidTransformation(cloud1, cloud2, matrix);
59  return Transform::fromEigen4f(matrix);
60 }
61 
62 // Get transform from cloud2 to cloud1
64  const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud1,
65  const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud2,
66  double inlierThreshold,
67  int iterations,
68  int refineIterations,
69  double refineSigma,
70  std::vector<int> * inliersOut,
71  cv::Mat * covariance)
72 {
73  //NOTE: this method is a mix of two methods:
74  // - getRemainingCorrespondences() in pcl/registration/impl/correspondence_rejection_sample_consensus.hpp
75  // - refineModel() in pcl/sample_consensus/sac.h
76 
77  if(covariance)
78  {
79  *covariance = cv::Mat::eye(6,6,CV_64FC1);
80  }
81  Transform transform;
82  if(cloud1->size() >=3 && cloud1->size() == cloud2->size())
83  {
84  // RANSAC
85  UDEBUG("iterations=%d inlierThreshold=%f", iterations, inlierThreshold);
86  std::vector<int> source_indices (cloud2->size());
87  std::vector<int> target_indices (cloud1->size());
88 
89  // Copy the query-match indices
90  for (int i = 0; i < (int)cloud1->size(); ++i)
91  {
92  source_indices[i] = i;
93  target_indices[i] = i;
94  }
95 
96  // From the set of correspondences found, attempt to remove outliers
97  // Create the registration model
98  pcl::SampleConsensusModelRegistration<pcl::PointXYZ>::Ptr model;
99  model.reset(new pcl::SampleConsensusModelRegistration<pcl::PointXYZ>(cloud2, source_indices));
100  // Pass the target_indices
101  model->setInputTarget (cloud1, target_indices);
102  // Create a RANSAC model
103  pcl::RandomSampleConsensus<pcl::PointXYZ> sac (model, inlierThreshold);
104  sac.setMaxIterations(iterations);
105 
106  // Compute the set of inliers
107  if(sac.computeModel())
108  {
109  std::vector<int> inliers;
110  Eigen::VectorXf model_coefficients;
111 
112  sac.getInliers(inliers);
113  sac.getModelCoefficients (model_coefficients);
114 
115  if (refineIterations>0)
116  {
117  double error_threshold = inlierThreshold;
118  int refine_iterations = 0;
119  bool inlier_changed = false, oscillating = false;
120  std::vector<int> new_inliers, prev_inliers = inliers;
121  std::vector<size_t> inliers_sizes;
122  Eigen::VectorXf new_model_coefficients = model_coefficients;
123  do
124  {
125  // Optimize the model coefficients
126  model->optimizeModelCoefficients (prev_inliers, new_model_coefficients, new_model_coefficients);
127  inliers_sizes.push_back (prev_inliers.size ());
128 
129  // Select the new inliers based on the optimized coefficients and new threshold
130  model->selectWithinDistance (new_model_coefficients, error_threshold, new_inliers);
131  UDEBUG("RANSAC refineModel: Number of inliers found (before/after): %d/%d, with an error threshold of %f.",
132  (int)prev_inliers.size (), (int)new_inliers.size (), error_threshold);
133 
134  if (new_inliers.empty ())
135  {
136  ++refine_iterations;
137  if (refine_iterations >= refineIterations)
138  {
139  break;
140  }
141  continue;
142  }
143 
144  // Estimate the variance and the new threshold
145  double variance = model->computeVariance ();
146  error_threshold = std::min (inlierThreshold, refineSigma * sqrt(variance));
147 
148  UDEBUG ("RANSAC refineModel: New estimated error threshold: %f (variance=%f) on iteration %d out of %d.",
149  error_threshold, variance, refine_iterations, refineIterations);
150  inlier_changed = false;
151  std::swap (prev_inliers, new_inliers);
152 
153  // If the number of inliers changed, then we are still optimizing
154  if (new_inliers.size () != prev_inliers.size ())
155  {
156  // Check if the number of inliers is oscillating in between two values
157  if (inliers_sizes.size () >= 4)
158  {
159  if (inliers_sizes[inliers_sizes.size () - 1] == inliers_sizes[inliers_sizes.size () - 3] &&
160  inliers_sizes[inliers_sizes.size () - 2] == inliers_sizes[inliers_sizes.size () - 4])
161  {
162  oscillating = true;
163  break;
164  }
165  }
166  inlier_changed = true;
167  continue;
168  }
169 
170  // Check the values of the inlier set
171  for (size_t i = 0; i < prev_inliers.size (); ++i)
172  {
173  // If the value of the inliers changed, then we are still optimizing
174  if (prev_inliers[i] != new_inliers[i])
175  {
176  inlier_changed = true;
177  break;
178  }
179  }
180  }
181  while (inlier_changed && ++refine_iterations < refineIterations);
182 
183  // If the new set of inliers is empty, we didn't do a good job refining
184  if (new_inliers.empty ())
185  {
186  UWARN ("RANSAC refineModel: Refinement failed: got an empty set of inliers!");
187  }
188 
189  if (oscillating)
190  {
191  UDEBUG("RANSAC refineModel: Detected oscillations in the model refinement.");
192  }
193 
194  std::swap (inliers, new_inliers);
195  model_coefficients = new_model_coefficients;
196  }
197 
198  if (inliers.size() >= 3)
199  {
200  if(inliersOut)
201  {
202  *inliersOut = inliers;
203  }
204  if(covariance)
205  {
206  double variance = model->computeVariance();
207  UASSERT(uIsFinite(variance));
208  *covariance *= variance;
209  }
210 
211  // get best transformation
212  Eigen::Matrix4f bestTransformation;
213  bestTransformation.row (0) = model_coefficients.segment<4>(0);
214  bestTransformation.row (1) = model_coefficients.segment<4>(4);
215  bestTransformation.row (2) = model_coefficients.segment<4>(8);
216  bestTransformation.row (3) = model_coefficients.segment<4>(12);
217 
218  transform = Transform::fromEigen4f(bestTransformation);
219  UDEBUG("RANSAC inliers=%d/%d tf=%s", (int)inliers.size(), (int)cloud1->size(), transform.prettyPrint().c_str());
220 
221  return transform.inverse(); // inverse to get actual pose transform (not correspondences transform)
222  }
223  else
224  {
225  UDEBUG("RANSAC: Model with inliers < 3");
226  }
227  }
228  else
229  {
230  UDEBUG("RANSAC: Failed to find model");
231  }
232  }
233  else
234  {
235  UDEBUG("Not enough points to compute the transform");
236  }
237  return Transform();
238 }
239 
240 template<typename PointNormalT>
242  const typename pcl::PointCloud<PointNormalT>::ConstPtr & cloudA,
243  const typename pcl::PointCloud<PointNormalT>::ConstPtr & cloudB,
244  double maxCorrespondenceDistance,
245  double maxCorrespondenceAngle,
246  double & variance,
247  int & correspondencesOut)
248 {
249  variance = 1;
250  correspondencesOut = 0;
251  typename pcl::registration::CorrespondenceEstimation<PointNormalT, PointNormalT>::Ptr est;
252  est.reset(new pcl::registration::CorrespondenceEstimation<PointNormalT, PointNormalT>);
253  const typename pcl::PointCloud<PointNormalT>::ConstPtr & target = cloudA->size()>cloudB->size()?cloudA:cloudB;
254  const typename pcl::PointCloud<PointNormalT>::ConstPtr & source = cloudA->size()>cloudB->size()?cloudB:cloudA;
255  est->setInputTarget(target);
256  est->setInputSource(source);
257  pcl::Correspondences correspondences;
258  est->determineCorrespondences(correspondences, maxCorrespondenceDistance);
259 
260  if(correspondences.size())
261  {
262  std::vector<double> distances(correspondences.size());
263  correspondencesOut = 0;
264  for(unsigned int i=0; i<correspondences.size(); ++i)
265  {
266  distances[i] = correspondences[i].distance;
267  if(maxCorrespondenceAngle <= 0.0)
268  {
269  ++correspondencesOut;
270  }
271  else
272  {
273  Eigen::Vector4f v1(
274  target->at(correspondences[i].index_match).normal_x,
275  target->at(correspondences[i].index_match).normal_y,
276  target->at(correspondences[i].index_match).normal_z,
277  0);
278  Eigen::Vector4f v2(
279  source->at(correspondences[i].index_query).normal_x,
280  source->at(correspondences[i].index_query).normal_y,
281  source->at(correspondences[i].index_query).normal_z,
282  0);
283  float angle = pcl::getAngle3D(v1, v2);
284  if(angle < maxCorrespondenceAngle)
285  {
286  ++correspondencesOut;
287  }
288  }
289  }
290  if(correspondencesOut)
291  {
292  distances.resize(correspondencesOut);
293 
294  //variance
295  std::sort(distances.begin (), distances.end ());
296  double median_error_sqr = distances[distances.size () >> 1];
297  variance = (2.1981 * median_error_sqr);
298  }
299  }
300 }
301 
303  const pcl::PointCloud<pcl::PointNormal>::ConstPtr & cloudA,
304  const pcl::PointCloud<pcl::PointNormal>::ConstPtr & cloudB,
305  double maxCorrespondenceDistance,
306  double maxCorrespondenceAngle,
307  double & variance,
308  int & correspondencesOut)
309 {
310  computeVarianceAndCorrespondencesImpl<pcl::PointNormal>(cloudA, cloudB, maxCorrespondenceDistance, maxCorrespondenceAngle, variance, correspondencesOut);
311 }
312 
314  const pcl::PointCloud<pcl::PointXYZINormal>::ConstPtr & cloudA,
315  const pcl::PointCloud<pcl::PointXYZINormal>::ConstPtr & cloudB,
316  double maxCorrespondenceDistance,
317  double maxCorrespondenceAngle,
318  double & variance,
319  int & correspondencesOut)
320 {
321  computeVarianceAndCorrespondencesImpl<pcl::PointXYZINormal>(cloudA, cloudB, maxCorrespondenceDistance, maxCorrespondenceAngle, variance, correspondencesOut);
322 }
323 
324 template<typename PointT>
326  const typename pcl::PointCloud<PointT>::ConstPtr & cloudA,
327  const typename pcl::PointCloud<PointT>::ConstPtr & cloudB,
328  double maxCorrespondenceDistance,
329  double & variance,
330  int & correspondencesOut)
331 {
332  variance = 1;
333  correspondencesOut = 0;
334  typename pcl::registration::CorrespondenceEstimation<PointT, PointT>::Ptr est;
335  est.reset(new pcl::registration::CorrespondenceEstimation<PointT, PointT>);
336  est->setInputTarget(cloudA->size()>cloudB->size()?cloudA:cloudB);
337  est->setInputSource(cloudA->size()>cloudB->size()?cloudB:cloudA);
338  pcl::Correspondences correspondences;
339  est->determineCorrespondences(correspondences, maxCorrespondenceDistance);
340 
341  if(correspondences.size()>=3)
342  {
343  std::vector<double> distances(correspondences.size());
344  for(unsigned int i=0; i<correspondences.size(); ++i)
345  {
346  distances[i] = correspondences[i].distance;
347  }
348 
349  //variance
350  std::sort(distances.begin (), distances.end ());
351  double median_error_sqr = distances[distances.size () >> 1];
352  variance = (2.1981 * median_error_sqr);
353  }
354 
355  correspondencesOut = (int)correspondences.size();
356 }
357 
359  const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloudA,
360  const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloudB,
361  double maxCorrespondenceDistance,
362  double & variance,
363  int & correspondencesOut)
364 {
365  computeVarianceAndCorrespondencesImpl<pcl::PointXYZ>(cloudA, cloudB, maxCorrespondenceDistance, variance, correspondencesOut);
366 }
367 
369  const pcl::PointCloud<pcl::PointXYZI>::ConstPtr & cloudA,
370  const pcl::PointCloud<pcl::PointXYZI>::ConstPtr & cloudB,
371  double maxCorrespondenceDistance,
372  double & variance,
373  int & correspondencesOut)
374 {
375  computeVarianceAndCorrespondencesImpl<pcl::PointXYZI>(cloudA, cloudB, maxCorrespondenceDistance, variance, correspondencesOut);
376 }
377 
378 // return transform from source to target (All points must be finite!!!)
379 template<typename PointT>
380 Transform icpImpl(const typename pcl::PointCloud<PointT>::ConstPtr & cloud_source,
381  const typename pcl::PointCloud<PointT>::ConstPtr & cloud_target,
382  double maxCorrespondenceDistance,
383  int maximumIterations,
384  bool & hasConverged,
385  pcl::PointCloud<PointT> & cloud_source_registered,
386  float epsilon,
387  bool icp2D)
388 {
389  pcl::IterativeClosestPoint<PointT, PointT> icp;
390  // Set the input source and target
391  icp.setInputTarget (cloud_target);
392  icp.setInputSource (cloud_source);
393 
394  if(icp2D)
395  {
396  typename pcl::registration::TransformationEstimation2D<PointT, PointT>::Ptr est;
397  est.reset(new pcl::registration::TransformationEstimation2D<PointT, PointT>);
398  icp.setTransformationEstimation(est);
399  }
400 
401  // Set the max correspondence distance to 5cm (e.g., correspondences with higher distances will be ignored)
402  icp.setMaxCorrespondenceDistance (maxCorrespondenceDistance);
403  // Set the maximum number of iterations (criterion 1)
404  icp.setMaximumIterations (maximumIterations);
405  // Set the transformation epsilon (criterion 2)
406  icp.setTransformationEpsilon (epsilon*epsilon);
407  // Set the euclidean distance difference epsilon (criterion 3)
408  //icp.setEuclideanFitnessEpsilon (1);
409  //icp.setRANSACOutlierRejectionThreshold(maxCorrespondenceDistance);
410 
411  // Perform the alignment
412  icp.align (cloud_source_registered);
413  hasConverged = icp.hasConverged();
414  return Transform::fromEigen4f(icp.getFinalTransformation());
415 }
416 
417 // return transform from source to target (All points must be finite!!!)
418 Transform icp(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud_source,
419  const pcl::PointCloud<pcl::PointXYZ>::ConstPtr & cloud_target,
420  double maxCorrespondenceDistance,
421  int maximumIterations,
422  bool & hasConverged,
423  pcl::PointCloud<pcl::PointXYZ> & cloud_source_registered,
424  float epsilon,
425  bool icp2D)
426 {
427  return icpImpl(cloud_source, cloud_target, maxCorrespondenceDistance, maximumIterations, hasConverged, cloud_source_registered, epsilon, icp2D);
428 }
429 
430 // return transform from source to target (All points must be finite!!!)
431 Transform icp(const pcl::PointCloud<pcl::PointXYZI>::ConstPtr & cloud_source,
432  const pcl::PointCloud<pcl::PointXYZI>::ConstPtr & cloud_target,
433  double maxCorrespondenceDistance,
434  int maximumIterations,
435  bool & hasConverged,
436  pcl::PointCloud<pcl::PointXYZI> & cloud_source_registered,
437  float epsilon,
438  bool icp2D)
439 {
440  return icpImpl(cloud_source, cloud_target, maxCorrespondenceDistance, maximumIterations, hasConverged, cloud_source_registered, epsilon, icp2D);
441 }
442 
443 // return transform from source to target (All points/normals must be finite!!!)
444 template<typename PointNormalT>
446  const typename pcl::PointCloud<PointNormalT>::ConstPtr & cloud_source,
447  const typename pcl::PointCloud<PointNormalT>::ConstPtr & cloud_target,
448  double maxCorrespondenceDistance,
449  int maximumIterations,
450  bool & hasConverged,
451  pcl::PointCloud<PointNormalT> & cloud_source_registered,
452  float epsilon,
453  bool icp2D)
454 {
455  pcl::IterativeClosestPoint<PointNormalT, PointNormalT> icp;
456  // Set the input source and target
457  icp.setInputTarget (cloud_target);
458  icp.setInputSource (cloud_source);
459 
460  typename pcl::registration::TransformationEstimationPointToPlaneLLS<PointNormalT, PointNormalT>::Ptr est;
461  est.reset(new pcl::registration::TransformationEstimationPointToPlaneLLS<PointNormalT, PointNormalT>);
462  icp.setTransformationEstimation(est);
463 
464  // Set the max correspondence distance to 5cm (e.g., correspondences with higher distances will be ignored)
465  icp.setMaxCorrespondenceDistance (maxCorrespondenceDistance);
466  // Set the maximum number of iterations (criterion 1)
467  icp.setMaximumIterations (maximumIterations);
468  // Set the transformation epsilon (criterion 2)
469  icp.setTransformationEpsilon (epsilon*epsilon);
470  // Set the euclidean distance difference epsilon (criterion 3)
471  //icp.setEuclideanFitnessEpsilon (1);
472  //icp.setRANSACOutlierRejectionThreshold(maxCorrespondenceDistance);
473 
474  // Perform the alignment
475  icp.align (cloud_source_registered);
476  hasConverged = icp.hasConverged();
477  Transform t = Transform::fromEigen4f(icp.getFinalTransformation());
478 
479  if(icp2D)
480  {
481  // FIXME probably an estimation approach already 2D like in icp() version above exists.
482  t = t.to3DoF();
483  }
484 
485  return t;
486 }
487 
488 // return transform from source to target (All points/normals must be finite!!!)
490  const pcl::PointCloud<pcl::PointNormal>::ConstPtr & cloud_source,
491  const pcl::PointCloud<pcl::PointNormal>::ConstPtr & cloud_target,
492  double maxCorrespondenceDistance,
493  int maximumIterations,
494  bool & hasConverged,
495  pcl::PointCloud<pcl::PointNormal> & cloud_source_registered,
496  float epsilon,
497  bool icp2D)
498 {
499  return icpPointToPlaneImpl(cloud_source, cloud_target, maxCorrespondenceDistance, maximumIterations, hasConverged, cloud_source_registered, epsilon, icp2D);
500 }
501 // return transform from source to target (All points/normals must be finite!!!)
503  const pcl::PointCloud<pcl::PointXYZINormal>::ConstPtr & cloud_source,
504  const pcl::PointCloud<pcl::PointXYZINormal>::ConstPtr & cloud_target,
505  double maxCorrespondenceDistance,
506  int maximumIterations,
507  bool & hasConverged,
508  pcl::PointCloud<pcl::PointXYZINormal> & cloud_source_registered,
509  float epsilon,
510  bool icp2D)
511 {
512  return icpPointToPlaneImpl(cloud_source, cloud_target, maxCorrespondenceDistance, maximumIterations, hasConverged, cloud_source_registered, epsilon, icp2D);
513 }
514 
515 }
516 
517 }
Transform RTABMAP_EXP transformFromXYZCorrespondencesSVD(const pcl::PointCloud< pcl::PointXYZ > &cloud1, const pcl::PointCloud< pcl::PointXYZ > &cloud2)
static Transform fromEigen4f(const Eigen::Matrix4f &matrix)
Definition: Transform.cpp:385
std::string prettyPrint() const
Definition: Transform.cpp:295
GLM_FUNC_DECL genType min(genType const &x, genType const &y)
Transform icpPointToPlaneImpl(const typename pcl::PointCloud< PointNormalT >::ConstPtr &cloud_source, const typename pcl::PointCloud< PointNormalT >::ConstPtr &cloud_target, double maxCorrespondenceDistance, int maximumIterations, bool &hasConverged, pcl::PointCloud< PointNormalT > &cloud_source_registered, float epsilon, bool icp2D)
GLM_FUNC_DECL vecType< T, P > sqrt(vecType< T, P > const &x)
Transform RTABMAP_EXP icpPointToPlane(const pcl::PointCloud< pcl::PointNormal >::ConstPtr &cloud_source, const pcl::PointCloud< pcl::PointNormal >::ConstPtr &cloud_target, double maxCorrespondenceDistance, int maximumIterations, bool &hasConverged, pcl::PointCloud< pcl::PointNormal > &cloud_source_registered, float epsilon=0.0f, bool icp2D=false)
void RTABMAP_EXP computeVarianceAndCorrespondences(const pcl::PointCloud< pcl::PointNormal >::ConstPtr &cloudA, const pcl::PointCloud< pcl::PointNormal >::ConstPtr &cloudB, double maxCorrespondenceDistance, double maxCorrespondenceAngle, double &variance, int &correspondencesOut)
Basic mathematics functions.
GLM_FUNC_DECL T angle(detail::tquat< T, P > const &x)
Transform RTABMAP_EXP transformFromXYZCorrespondences(const pcl::PointCloud< pcl::PointXYZ >::ConstPtr &cloud1, const pcl::PointCloud< pcl::PointXYZ >::ConstPtr &cloud2, double inlierThreshold=0.02, int iterations=100, int refineModelIterations=10, double refineModelSigma=3.0, std::vector< int > *inliers=0, cv::Mat *variance=0)
bool uIsFinite(const T &value)
Definition: UMath.h:55
#define UASSERT(condition)
Transform icpImpl(const typename pcl::PointCloud< PointT >::ConstPtr &cloud_source, const typename pcl::PointCloud< PointT >::ConstPtr &cloud_target, double maxCorrespondenceDistance, int maximumIterations, bool &hasConverged, pcl::PointCloud< PointT > &cloud_source_registered, float epsilon, bool icp2D)
Transform RTABMAP_EXP icp(const pcl::PointCloud< pcl::PointXYZ >::ConstPtr &cloud_source, const pcl::PointCloud< pcl::PointXYZ >::ConstPtr &cloud_target, double maxCorrespondenceDistance, int maximumIterations, bool &hasConverged, pcl::PointCloud< pcl::PointXYZ > &cloud_source_registered, float epsilon=0.0f, bool icp2D=false)
const char * source
Definition: lz4hc.h:181
void computeVarianceAndCorrespondencesImpl(const typename pcl::PointCloud< PointNormalT >::ConstPtr &cloudA, const typename pcl::PointCloud< PointNormalT >::ConstPtr &cloudB, double maxCorrespondenceDistance, double maxCorrespondenceAngle, double &variance, int &correspondencesOut)
Transform to3DoF() const
Definition: Transform.cpp:210
#define UDEBUG(...)
ULogger class and convenient macros.
#define UWARN(...)
Transform inverse() const
Definition: Transform.cpp:178


rtabmap
Author(s): Mathieu Labbe
autogenerated on Mon Dec 14 2020 03:37:06