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/types.h>
15
16#include <Eigen/Eigenvalues>
17#include <Eigen/SVD>
18#include <unsupported/Eigen/MatrixFunctions>
19
20#include <algorithm>
21#include <array>
22#include <cmath>
23#include <limits>
24#include <numeric>
25#include <utility>
26#include <vector>
27namespace pcl {
28
29template <typename PointSource, typename PointTarget, typename Scalar>
32{
33 this->reg_name_ = "FastRobustIterativeClosestPoint";
34 this->max_iterations_ = 50;
35 this->transformation_epsilon_ = static_cast<Scalar>(1e-6);
36 this->min_number_correspondences_ = 4;
37}
38
39template <typename PointSource, typename PointTarget, typename Scalar>
40void
46
47template <typename PointSource, typename PointTarget, typename Scalar>
48typename FastRobustIterativeClosestPoint<PointSource,
49 PointTarget,
50 Scalar>::RobustFunction
56
57template <typename PointSource, typename PointTarget, typename Scalar>
58void
64
65template <typename PointSource, typename PointTarget, typename Scalar>
66bool
72
73template <typename PointSource, typename PointTarget, typename Scalar>
74void
76 setAndersonHistorySize(std::size_t history)
77{
78 anderson_history_ = std::max<std::size_t>(1, history);
79}
80
81template <typename PointSource, typename PointTarget, typename Scalar>
82std::size_t
88
89template <typename PointSource, typename PointTarget, typename Scalar>
90void
93{
94 nu_begin_ratio_ = std::max(ratio, same_threshold_);
95}
96
97template <typename PointSource, typename PointTarget, typename Scalar>
98void
100 setDynamicWelschEndRatio(double ratio)
101{
102 nu_end_ratio_ = std::max(ratio, same_threshold_);
103}
104
105template <typename PointSource, typename PointTarget, typename Scalar>
106void
108 setDynamicWelschDecay(double ratio)
109{
110 nu_decay_ratio_ = std::max(0.0, std::min(1.0, ratio));
111 if (nu_decay_ratio_ < same_threshold_)
112 nu_decay_ratio_ = 0.5;
113}
114
115template <typename PointSource, typename PointTarget, typename Scalar>
116void
119{
121
122 auto convergence_criteria = this->getConvergeCriteria();
123 if (convergence_criteria) {
124 convergence_criteria->setMaximumIterations(this->max_iterations_);
125 convergence_criteria->setRelativeMSE(this->euclidean_fitness_epsilon_);
126 convergence_criteria->setTranslationThreshold(this->transformation_epsilon_);
127 if (this->transformation_rotation_epsilon_ > 0) {
128 convergence_criteria->setRotationThreshold(
129 this->transformation_rotation_epsilon_);
130 }
131 convergence_criteria->setConvergenceState(
133 Scalar>::CONVERGENCE_CRITERIA_NOT_CONVERGED);
134 }
135
136 if (!this->input_ || !this->target_) {
137 if (convergence_criteria) {
138 convergence_criteria->setConvergenceState(
140 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
141 }
142 PCL_ERROR("[pcl::%s::computeTransformation] Invalid input clouds.\n",
143 this->getClassName().c_str());
144 return;
145 }
146
147 if (this->input_->empty() || this->target_->empty()) {
148 if (convergence_criteria) {
149 convergence_criteria->setConvergenceState(
151 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
152 }
153 PCL_ERROR("[pcl::%s::computeTransformation] Empty input point clouds.\n",
154 this->getClassName().c_str());
155 return;
156 }
157
158 std::vector<pcl::uindex_t> source_indices;
159 if (this->indices_ && !this->indices_->empty())
160 source_indices.assign(this->indices_->begin(), this->indices_->end());
161 else {
162 source_indices.resize(this->input_->size());
163 std::iota(source_indices.begin(), source_indices.end(), 0);
164 }
165
166 if (source_indices.size() <
167 static_cast<std::size_t>(this->min_number_correspondences_)) {
168 PCL_ERROR("[pcl::%s::computeTransformation] Not enough source points (%zu).\n",
169 this->getClassName().c_str(),
170 source_indices.size());
171 if (convergence_criteria) {
172 convergence_criteria->setConvergenceState(
174 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
175 }
176 return;
177 }
178
179 const std::size_t source_size = source_indices.size();
180 const std::size_t target_size = this->target_->size();
181
182 if (target_size < static_cast<std::size_t>(this->min_number_correspondences_)) {
183 PCL_ERROR("[pcl::%s::computeTransformation] Not enough target points (%zu).\n",
184 this->getClassName().c_str(),
185 target_size);
186 if (convergence_criteria) {
187 convergence_criteria->setConvergenceState(
189 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
190 }
191 return;
192 }
193
194 Matrix3Xd source_mat(3, source_size);
195 for (std::size_t i = 0; i < source_size; ++i) {
196 const auto& pt = (*this->input_)[source_indices[i]];
197 assert(pcl::isFinite(pt) && "FRICP requires finite source points (no NaN/Inf)");
198 source_mat.col(i) = pt.getVector3fMap().template cast<double>();
199 }
200 Vector3d source_mean = source_mat.rowwise().mean();
201 source_mat.colwise() -= source_mean;
202
203 Matrix3Xd target_mat(3, target_size);
204 for (std::size_t i = 0; i < target_size; ++i) {
205 const auto& pt = (*this->target_)[i];
206 assert(pcl::isFinite(pt) && "FRICP requires finite target points (no NaN/Inf)");
207 target_mat.col(i) = pt.getVector3fMap().template cast<double>();
208 }
209 Vector3d target_mean = target_mat.rowwise().mean();
210 target_mat.colwise() -= target_mean;
211
214 target_centered->resize(target_size);
215 for (std::size_t i = 0; i < target_size; ++i) {
216 (*target_centered)[i].x = static_cast<float>(target_mat(0, i));
217 (*target_centered)[i].y = static_cast<float>(target_mat(1, i));
218 (*target_centered)[i].z = static_cast<float>(target_mat(2, i));
219 }
220
222 pcl::search::Search<pcl::PointXYZ>& tree = tree_data;
223 tree.setInputCloud(target_centered);
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_data, 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:62
void computeTransformation(PointCloudSource &output, const Matrix4 &guess) override
Abstract transformation computation method with initial guess.
Definition fricp.hpp:118
void setDynamicWelschEndRatio(double ratio)
Set the final Welsch scale ratio used in dynamic robust weighting.
Definition fricp.hpp:100
void setRobustFunction(RobustFunction f)
Definition fricp.hpp:41
void setDynamicWelschDecay(double ratio)
Set the multiplicative decay applied to the dynamic Welsch scale.
Definition fricp.hpp:108
void setUseAndersonAcceleration(bool enabled)
Enable or disable Anderson acceleration in the FRICP optimization loop.
Definition fricp.hpp:60
void setAndersonHistorySize(std::size_t history)
Set the history size used by Anderson acceleration.
Definition fricp.hpp:76
std::size_t getAndersonHistorySize() const
Definition fricp.hpp:84
RobustFunction getRobustFunction() const
Definition fricp.hpp:51
void setDynamicWelschBeginRatio(double ratio)
Set the initial Welsch scale ratio used in dynamic robust weighting.
Definition fricp.hpp:92
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...
search::KdTree is a wrapper class which inherits the pcl::KdTree class for performing search function...
Definition kdtree.h:62
Generic search class.
Definition search.h:75
virtual bool setInputCloud(const PointCloudConstPtr &cloud, const IndicesConstPtr &indices=IndicesConstPtr())
Pass the input dataset that the search will be performed on.
Definition search.hpp:75
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.