Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
fricp.hpp
1/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2010-, Open Perception, Inc.
6 *
7 * All rights reserved.
8 */
9
10#pragma once
11
12#include <pcl/common/common.h>
13#include <pcl/common/transforms.h>
14#include <pcl/search/auto.h>
15#include <pcl/types.h>
16
17#include <Eigen/Eigenvalues>
18#include <Eigen/SVD>
19#include <unsupported/Eigen/MatrixFunctions>
20
21#include <algorithm>
22#include <array>
23#include <cmath>
24#include <limits>
25#include <numeric>
26#include <utility>
27#include <vector>
28namespace pcl {
29
30template <typename PointSource, typename PointTarget, typename Scalar>
33{
34 this->reg_name_ = "FastRobustIterativeClosestPoint";
35 this->max_iterations_ = 50;
36 this->transformation_epsilon_ = static_cast<Scalar>(1e-6);
37 this->min_number_correspondences_ = 4;
38}
39
40template <typename PointSource, typename PointTarget, typename Scalar>
41void
47
48template <typename PointSource, typename PointTarget, typename Scalar>
49typename FastRobustIterativeClosestPoint<PointSource,
50 PointTarget,
51 Scalar>::RobustFunction
57
58template <typename PointSource, typename PointTarget, typename Scalar>
59void
65
66template <typename PointSource, typename PointTarget, typename Scalar>
67bool
73
74template <typename PointSource, typename PointTarget, typename Scalar>
75void
77 setAndersonHistorySize(std::size_t history)
78{
79 anderson_history_ = std::max<std::size_t>(1, history);
80}
81
82template <typename PointSource, typename PointTarget, typename Scalar>
83std::size_t
89
90template <typename PointSource, typename PointTarget, typename Scalar>
91void
94{
95 nu_begin_ratio_ = std::max(ratio, same_threshold_);
96}
97
98template <typename PointSource, typename PointTarget, typename Scalar>
99void
101 setDynamicWelschEndRatio(double ratio)
102{
103 nu_end_ratio_ = std::max(ratio, same_threshold_);
104}
105
106template <typename PointSource, typename PointTarget, typename Scalar>
107void
109 setDynamicWelschDecay(double ratio)
110{
111 nu_decay_ratio_ = std::max(0.0, std::min(1.0, ratio));
112 if (nu_decay_ratio_ < same_threshold_)
113 nu_decay_ratio_ = 0.5;
114}
115
116template <typename PointSource, typename PointTarget, typename Scalar>
117void
120{
122
123 auto convergence_criteria = this->getConvergeCriteria();
124 if (convergence_criteria) {
125 convergence_criteria->setMaximumIterations(this->max_iterations_);
126 convergence_criteria->setRelativeMSE(this->euclidean_fitness_epsilon_);
127 convergence_criteria->setTranslationThreshold(this->transformation_epsilon_);
128 if (this->transformation_rotation_epsilon_ > 0) {
129 convergence_criteria->setRotationThreshold(
130 this->transformation_rotation_epsilon_);
131 }
132 convergence_criteria->setConvergenceState(
134 Scalar>::CONVERGENCE_CRITERIA_NOT_CONVERGED);
135 }
136
137 if (!this->input_ || !this->target_) {
138 if (convergence_criteria) {
139 convergence_criteria->setConvergenceState(
141 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
142 }
143 PCL_ERROR("[pcl::%s::computeTransformation] Invalid input clouds.\n",
144 this->getClassName().c_str());
145 return;
146 }
147
148 if (this->input_->empty() || this->target_->empty()) {
149 if (convergence_criteria) {
150 convergence_criteria->setConvergenceState(
152 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
153 }
154 PCL_ERROR("[pcl::%s::computeTransformation] Empty input point clouds.\n",
155 this->getClassName().c_str());
156 return;
157 }
158
159 std::vector<pcl::uindex_t> source_indices;
160 if (this->indices_ && !this->indices_->empty())
161 source_indices.assign(this->indices_->begin(), this->indices_->end());
162 else {
163 source_indices.resize(this->input_->size());
164 std::iota(source_indices.begin(), source_indices.end(), 0);
165 }
166
167 if (source_indices.size() <
168 static_cast<std::size_t>(this->min_number_correspondences_)) {
169 PCL_ERROR("[pcl::%s::computeTransformation] Not enough source points (%zu).\n",
170 this->getClassName().c_str(),
171 source_indices.size());
172 if (convergence_criteria) {
173 convergence_criteria->setConvergenceState(
175 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
176 }
177 return;
178 }
179
180 const std::size_t source_size = source_indices.size();
181 const std::size_t target_size = this->target_->size();
182
183 if (target_size < static_cast<std::size_t>(this->min_number_correspondences_)) {
184 PCL_ERROR("[pcl::%s::computeTransformation] Not enough target points (%zu).\n",
185 this->getClassName().c_str(),
186 target_size);
187 if (convergence_criteria) {
188 convergence_criteria->setConvergenceState(
190 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
191 }
192 return;
193 }
194
195 Matrix3Xd source_mat(3, source_size);
196 for (std::size_t i = 0; i < source_size; ++i) {
197 const auto& pt = (*this->input_)[source_indices[i]];
198 assert(pcl::isFinite(pt) && "FRICP requires finite source points (no NaN/Inf)");
199 source_mat.col(i) = pt.getVector3fMap().template cast<double>();
200 }
201 Vector3d source_mean = source_mat.rowwise().mean();
202 source_mat.colwise() -= source_mean;
203
204 Matrix3Xd target_mat(3, target_size);
205 for (std::size_t i = 0; i < target_size; ++i) {
206 const auto& pt = (*this->target_)[i];
207 assert(pcl::isFinite(pt) && "FRICP requires finite target points (no NaN/Inf)");
208 target_mat.col(i) = pt.getVector3fMap().template cast<double>();
209 }
210 Vector3d target_mean = target_mat.rowwise().mean();
211 target_mat.colwise() -= target_mean;
212
215 target_centered->resize(target_size);
216 for (std::size_t i = 0; i < target_size; ++i) {
217 (*target_centered)[i].x = static_cast<float>(target_mat(0, i));
218 (*target_centered)[i].y = static_cast<float>(target_mat(1, i));
219 (*target_centered)[i].z = static_cast<float>(target_mat(2, i));
220 }
221
223 pcl::search::autoSelectMethod<pcl::PointXYZ>(target_centered, true));
224
225 Matrix4d transform_centered = convertGuessToCentered(guess, source_mean, target_mean);
226 Matrix4d svd_transform = transform_centered;
227 Matrix4d previous_transform = transform_centered;
228
229 Matrix3Xd matched_targets(3, source_size);
230 VectorXd residuals(source_size);
231 if (!updateCorrespondences(transform_centered,
232 source_mat,
233 target_mat,
234 *tree,
235 matched_targets,
236 residuals,
237 this->correspondences_.get())) {
238 PCL_ERROR(
239 "[pcl::%s::computeTransformation] Failed to initialize correspondences.\n",
240 this->getClassName().c_str());
241 if (convergence_criteria) {
242 convergence_criteria->setConvergenceState(
244 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
245 }
246 return;
247 }
248
249 const bool use_welsch = (robust_function_ == RobustFunction::WELSCH);
250 double nu_limit = 1.0;
251 double nu_current = 1.0;
252 if (use_welsch) {
253 const double neighbor_med = findKNearestMedian(*target_centered, *tree, 7);
254 std::vector<double> residual_values(static_cast<std::size_t>(residuals.size()));
255 for (Eigen::Index i = 0; i < residuals.size(); ++i)
256 residual_values[static_cast<std::size_t>(i)] = std::sqrt(residuals(i));
257 const double residual_med =
258 pcl::computeMedian(residual_values.begin(), residual_values.end());
259 nu_limit = std::max(nu_end_ratio_ * neighbor_med, same_threshold_);
260 nu_current = std::max(nu_begin_ratio_ * residual_med, nu_limit);
261 }
262
263 this->nr_iterations_ = 0;
264 this->converged_ = false;
265
266 double last_energy = std::numeric_limits<double>::max();
267 if (use_anderson_) {
268 const Matrix4d log_state = matrixLog(transform_centered);
269 anderson_.init(anderson_history_, 16, log_state.data());
270 }
271
272 bool outer_done = false;
273 bool converged = false;
274
275 while (!outer_done) {
276 for (int iter = 0; iter < this->max_iterations_; ++iter) {
277 double energy =
278 use_welsch ? computeEnergy(residuals, nu_current) : residuals.sum();
279 if (use_anderson_) {
280 if (energy <= last_energy) {
281 last_energy = energy;
282 }
283 else {
284 transform_centered = svd_transform;
285 const Matrix4d log_state = matrixLog(transform_centered);
286 anderson_.replace(log_state.data());
287 if (!updateCorrespondences(transform_centered,
288 source_mat,
289 target_mat,
290 *tree,
291 matched_targets,
292 residuals,
293 this->correspondences_.get())) {
294 PCL_ERROR("[pcl::%s::computeTransformation] Unable to recompute "
295 "correspondences during fallback.\n",
296 this->getClassName().c_str());
297 if (convergence_criteria) {
298 convergence_criteria->setConvergenceState(
300 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
301 }
302 return;
303 }
304 energy = use_welsch ? computeEnergy(residuals, nu_current) : residuals.sum();
305 last_energy = energy;
306 }
307 }
308 else {
309 last_energy = energy;
310 }
311
312 VectorXd weights = use_welsch ? computeWeights(residuals, nu_current)
313 : VectorXd::Ones(residuals.size());
314 Matrix4d candidate =
315 computeWeightedRigidTransform(source_mat, matched_targets, weights);
316 svd_transform = candidate;
317 transform_centered = candidate;
318
319 if (use_anderson_) {
320 const Matrix4d log_matrix = matrixLog(transform_centered);
321 const Eigen::VectorXd& accelerated = anderson_.compute(log_matrix.data());
322 transform_centered = Eigen::Map<const Matrix4d>(accelerated.data()).exp();
323 }
324
325 const Matrix4d delta_transform =
326 transform_centered * previous_transform.inverse();
327 this->transformation_ = delta_transform.template cast<Scalar>();
328
329 if (!updateCorrespondences(transform_centered,
330 source_mat,
331 target_mat,
332 *tree,
333 matched_targets,
334 residuals,
335 this->correspondences_.get())) {
336 PCL_ERROR("[pcl::%s::computeTransformation] Failed to update "
337 "correspondences.\n",
338 this->getClassName().c_str());
339 if (convergence_criteria) {
340 convergence_criteria->setConvergenceState(
342 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
343 }
344 return;
345 }
346
347 previous_transform = transform_centered;
348 ++this->nr_iterations_;
349
350 if (convergence_criteria && static_cast<bool>(*convergence_criteria)) {
351 converged = true;
352 break;
353 }
354 }
355
356 if (!use_welsch || (std::abs(nu_current - nu_limit) < same_threshold_)) {
357 outer_done = true;
358 }
359 else {
360 nu_current = std::max(nu_current * nu_decay_ratio_, nu_limit);
361 last_energy = std::numeric_limits<double>::max();
362 if (use_anderson_) {
363 const Matrix4d log_state = matrixLog(transform_centered);
364 anderson_.reset(log_state.data());
365 }
366 }
367 }
368
369 this->converged_ = converged;
370 if (!converged && convergence_criteria &&
371 convergence_criteria->getConvergenceState() ==
373 Scalar>::CONVERGENCE_CRITERIA_NOT_CONVERGED) {
374 convergence_criteria->setConvergenceState(
376 Scalar>::CONVERGENCE_CRITERIA_ITERATIONS);
377 }
378
379 const Matrix4d final_transform =
380 convertCenteredToActual(transform_centered, source_mean, target_mean);
381 this->final_transformation_ = final_transform.template cast<Scalar>();
382 this->transformation_ = this->final_transformation_;
383 this->previous_transformation_ = this->final_transformation_;
384
385 pcl::transformPointCloud(*this->input_, output, this->final_transformation_);
386}
387
388template <typename PointSource, typename PointTarget, typename Scalar>
389typename FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::Matrix4d
391 convertGuessToCentered(const Matrix4& guess,
392 const Vector3d& source_mean,
393 const Vector3d& target_mean) const
394{
395 Matrix4d centered = Matrix4d::Identity();
396 centered.block<3, 3>(0, 0) = guess.template block<3, 3>(0, 0).template cast<double>();
397 Vector3d translation = guess.template block<3, 1>(0, 3).template cast<double>();
398 centered.block<3, 1>(0, 3) =
399 centered.block<3, 3>(0, 0) * source_mean + translation - target_mean;
400 return centered;
401}
402
403template <typename PointSource, typename PointTarget, typename Scalar>
404typename FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::Matrix4d
405FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::
406 convertCenteredToActual(const Matrix4d& transform,
407 const Vector3d& source_mean,
408 const Vector3d& target_mean) const
409{
410 Matrix4d actual = transform;
411 actual.block<3, 1>(0, 3) = transform.block<3, 1>(0, 3) -
412 transform.block<3, 3>(0, 0) * source_mean + target_mean;
413 return actual;
414}
415
416template <typename PointSource, typename PointTarget, typename Scalar>
417bool
418FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::
419 updateCorrespondences(const Matrix4d& transform,
420 const Matrix3Xd& source,
421 const Matrix3Xd& target,
423 Matrix3Xd& matched_targets,
424 VectorXd& residuals,
425 pcl::Correspondences* correspondences) const
426{
427 const Eigen::Matrix3d R = transform.block<3, 3>(0, 0);
428 const Eigen::Vector3d t = transform.block<3, 1>(0, 3);
429 pcl::PointXYZ query;
430 pcl::Indices nn_indices(1);
431 std::vector<float> nn_sqr_dists(1);
432
433 if (correspondences) {
434 correspondences->clear();
435 correspondences->reserve(static_cast<std::size_t>(source.cols()));
436 }
437
438 for (Eigen::Index i = 0; i < source.cols(); ++i) {
439 const Eigen::Vector3d current = R * source.col(i) + t;
440 query.x = static_cast<float>(current.x());
441 query.y = static_cast<float>(current.y());
442 query.z = static_cast<float>(current.z());
443 if (tree.nearestKSearch(query, 1, nn_indices, nn_sqr_dists) != 1)
444 return false;
445 const auto idx = nn_indices[0];
446 matched_targets.col(i) = target.col(static_cast<int>(idx));
447 // Store squared distance reported by the search (avoid recomputing
448 // the difference). Promote to double for internal computations.
449 residuals(i) = static_cast<double>(nn_sqr_dists[0]);
450 if (correspondences) {
452 corr.index_query = static_cast<int>(i);
453 corr.index_match = idx;
454 corr.distance = nn_sqr_dists[0];
455 correspondences->push_back(corr);
456 }
457 }
458 return true;
459}
460
461template <typename PointSource, typename PointTarget, typename Scalar>
462double
463FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::computeEnergy(
464 const VectorXd& residuals, double nu) const
465{
466 if (nu < same_threshold_)
467 nu = same_threshold_;
468 const double denom = 2.0 * nu * nu;
469 // "residuals" contains squared distances already
470 const Eigen::ArrayXd dist2 = residuals.array();
471 return (1.0 - (-dist2 / denom).exp()).sum();
472}
473
474template <typename PointSource, typename PointTarget, typename Scalar>
475typename FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::VectorXd
476FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::computeWeights(
477 const VectorXd& residuals, double nu) const
478{
479 if (nu < same_threshold_)
480 nu = same_threshold_;
481 const double denom = 2.0 * nu * nu;
482 // residuals already squared
483 return (-residuals.array() / denom).exp().matrix();
484}
485
486template <typename PointSource, typename PointTarget, typename Scalar>
487typename FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::Matrix4d
488FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::
489 computeWeightedRigidTransform(const Matrix3Xd& source,
490 const Matrix3Xd& target,
491 const VectorXd& weights) const
492{
493 Matrix4d transform = Matrix4d::Identity();
494 VectorXd normalized = weights;
495 const double sum = normalized.sum();
496 if (sum <= same_threshold_) {
497 normalized.setOnes();
498 normalized /= static_cast<double>(normalized.size());
499 }
500 else {
501 normalized /= sum;
502 }
503
504 const Vector3d source_mean = source * normalized;
505 const Vector3d target_mean = target * normalized;
506 const Matrix3Xd source_centered = source.colwise() - source_mean;
507 const Matrix3Xd target_centered = target.colwise() - target_mean;
508 Eigen::Matrix3d sigma =
509 source_centered * normalized.asDiagonal() * target_centered.transpose();
510 Eigen::JacobiSVD<Eigen::Matrix3d> svd(sigma,
511 Eigen::ComputeFullU | Eigen::ComputeFullV);
512 Eigen::Matrix3d R = svd.matrixV() * svd.matrixU().transpose();
513 if (R.determinant() < 0.0) {
514 Eigen::Matrix3d V = svd.matrixV();
515 V.col(2) *= -1.0;
516 R = V * svd.matrixU().transpose();
517 }
518 transform.block<3, 3>(0, 0) = R;
519 transform.block<3, 1>(0, 3) = target_mean - R * source_mean;
520 return transform;
521}
522
523template <typename PointSource, typename PointTarget, typename Scalar>
524double
525FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::findKNearestMedian(
528 int neighbors) const
529{
530 if (cloud.empty() || neighbors < 2)
531 return 0.0;
532
533 const int k = std::min<int>(neighbors, static_cast<int>(cloud.size()));
534 if (k < 2)
535 return 0.0;
536
537 std::vector<double> local_medians;
538 pcl::Indices nn_indices(k);
539 std::vector<float> nn_sqr_dists(k);
540 std::vector<double> dists;
541 dists.reserve(k - 1);
542
543 for (const auto& point : cloud) {
544 if (tree.nearestKSearch(point, k, nn_indices, nn_sqr_dists) != k)
545 continue;
546 dists.clear();
547 for (int j = 1; j < k; ++j)
548 dists.push_back(std::sqrt(nn_sqr_dists[j]));
549 if (!dists.empty()) {
550 local_medians.push_back(pcl::computeMedian(dists.begin(), dists.end()));
551 }
552 }
553
554 if (local_medians.empty())
555 return 0.0;
556 return pcl::computeMedian(local_medians.begin(), local_medians.end());
557}
558
559template <typename PointSource, typename PointTarget, typename Scalar>
560typename FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::Matrix4d
561FastRobustIterativeClosestPoint<PointSource, PointTarget, Scalar>::matrixLog(
562 const Matrix4d& transform) const
563{
564 Eigen::RealSchur<Matrix4d> schur(transform);
565 const Matrix4d U = schur.matrixU();
566 const Matrix4d R = schur.matrixT();
567 std::array<bool, 3> selected{{true, true, true}};
568 Eigen::Matrix3d B = Eigen::Matrix3d::Zero();
569 Eigen::Matrix3d V = Eigen::Matrix3d::Identity();
570
571 for (int i = 0; i < 3; ++i) {
572 if (!selected[i])
573 continue;
574 if (std::abs(R(i, i) - 1.0) <= same_threshold_)
575 continue;
576 int partner = -1;
577 for (int j = i + 1; j < 3; ++j) {
578 if (std::abs(R(j, j) - R(i, i)) < same_threshold_) {
579 partner = j;
580 selected[j] = false;
581 break;
582 }
583 }
584 if (partner > 0) {
585 selected[i] = false;
586 const double diag = std::max(-1.0, std::min(1.0, static_cast<double>(R(i, i))));
587 double theta = std::acos(diag);
588 if (R(i, partner) < 0.0)
589 theta = -theta;
590 B(i, partner) += theta;
591 B(partner, i) -= theta;
592 V(i, partner) -= theta / 2.0;
593 V(partner, i) += theta / 2.0;
594 const double denom = 2.0 * (1.0 - R(i, i));
595 if (std::abs(denom) > same_threshold_) {
596 const double coeff = 1.0 - (theta * R(i, partner)) / denom;
597 V(i, i) -= coeff;
598 V(partner, partner) -= coeff;
599 }
600 }
601 }
602
603 Matrix4d trimmed = Matrix4d::Zero();
604 trimmed.block<3, 3>(0, 0) = B;
605 trimmed.block<3, 1>(0, 3) = V * R.block<3, 1>(0, 3);
606 return U * trimmed * U.transpose();
607}
608
609} // namespace pcl
FastRobustIterativeClosestPoint implements the FRICP variant described in "Fast and Robust Iterative ...
Definition fricp.h:61
void computeTransformation(PointCloudSource &output, const Matrix4 &guess) override
Abstract transformation computation method with initial guess.
Definition fricp.hpp:119
void setDynamicWelschEndRatio(double ratio)
Set the final Welsch scale ratio used in dynamic robust weighting.
Definition fricp.hpp:101
void setRobustFunction(RobustFunction f)
Definition fricp.hpp:42
void setDynamicWelschDecay(double ratio)
Set the multiplicative decay applied to the dynamic Welsch scale.
Definition fricp.hpp:109
void setUseAndersonAcceleration(bool enabled)
Enable or disable Anderson acceleration in the FRICP optimization loop.
Definition fricp.hpp:61
void setAndersonHistorySize(std::size_t history)
Set the history size used by Anderson acceleration.
Definition fricp.hpp:77
std::size_t getAndersonHistorySize() const
Definition fricp.hpp:85
RobustFunction getRobustFunction() const
Definition fricp.hpp:52
void setDynamicWelschBeginRatio(double ratio)
Set the initial Welsch scale ratio used in dynamic robust weighting.
Definition fricp.hpp:93
typename Registration< PointSource, PointTarget, Scalar >::Matrix4 Matrix4
Definition icp.h:143
typename Registration< PointSource, PointTarget, Scalar >::PointCloudSource PointCloudSource
Definition icp.h:101
PointCloud represents the base class in PCL for storing collections of 3D points.
bool empty() const
void resize(std::size_t count)
Resizes the container to contain count elements.
std::size_t size() const
shared_ptr< PointCloud< PointT > > Ptr
bool initComputeReciprocal()
Internal computation when reciprocal lookup is needed.
DefaultConvergenceCriteria represents an instantiation of ConvergenceCriteria, and implements the fol...
Generic search class.
Definition search.h:78
shared_ptr< pcl::search::Search< PointT > > Ptr
Definition search.h:84
virtual int nearestKSearch(const PointT &point, int k, Indices &k_indices, std::vector< float > &k_sqr_distances) const =0
Search for the k-nearest neighbors for the given query point.
Define standard C methods and C++ classes that are common to all methods.
auto computeMedian(IteratorT begin, IteratorT end, Functor f) noexcept -> std::result_of_t< Functor(decltype(*begin))>
Compute the median of a list of values (fast).
Definition common.h:285
void transformPointCloud(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const Eigen::Matrix< Scalar, 4, 4 > &transform, bool copy_all_fields)
Apply a rigid transform defined by a 4x4 matrix.
@ B
Definition norms.h:54
int dist2(const cv::Vec4b &lhs, const cv::Vec4b &rhs)
__device__ __forceinline__ float3 normalized(const float3 &v)
Definition utils.hpp:101
void transform(const T t[12], const T p[3], T out[3])
The first 9 elements of 't' are treated as a 3x3 matrix (row major order) and the last 3 as a transla...
Definition auxiliary.h:304
bool isFinite(const PointT &pt)
Tests if the 3D components of a point are all finite param[in] pt point to be tested return true if f...
Definition point_tests.h:56
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
Correspondence represents a match between two entities (e.g., points, descriptors,...
index_t index_query
Index of the query (source) point.
index_t index_match
Index of the matching (target) point.
A point structure representing Euclidean xyz coordinates.
Defines basic non-point types used by PCL.