Point Cloud Library (PCL)  1.15.1-dev
ia_fpcs.hpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2014-, Open Perception, Inc.
6  * Copyright (C) 2008 Ben Gurion University of the Negev, Beer Sheva, Israel.
7  *
8  * All rights reserved
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the copyright holder(s) nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  */
37 
38 #ifndef PCL_REGISTRATION_IMPL_IA_FPCS_H_
39 #define PCL_REGISTRATION_IMPL_IA_FPCS_H_
40 
41 #include <pcl/common/distances.h>
42 #include <pcl/common/time.h>
43 #include <pcl/common/utils.h>
44 #include <pcl/filters/random_sample.h>
45 #include <pcl/registration/ia_fpcs.h>
46 #include <pcl/registration/transformation_estimation_3point.h>
47 #include <pcl/sample_consensus/sac_model_plane.h>
48 #include <pcl/search/auto.h>
49 
50 #include <limits>
51 
52 ///////////////////////////////////////////////////////////////////////////////////////////
53 template <typename PointT>
54 inline float
56  float max_dist,
57  int nr_threads)
58 {
59  const float max_dist_sqr = max_dist * max_dist;
60  const std::size_t s = cloud->size();
61 
62  typename pcl::search::Search<PointT>::Ptr tree(pcl::search::autoSelectMethod<PointT>(
64 
65  float mean_dist = 0.f;
66  int num = 0;
67  pcl::Indices ids(2);
68  std::vector<float> dists_sqr(2);
69 
70  pcl::utils::ignore(nr_threads);
71 #pragma omp parallel for default(none) shared(tree, cloud) \
72  firstprivate(ids, dists_sqr) reduction(+ : mean_dist, num) \
73  firstprivate(s, max_dist_sqr) num_threads(nr_threads)
74  for (int i = 0; i < 1000; i++) {
75  tree->nearestKSearch((*cloud)[rand() % s], 2, ids, dists_sqr);
76  if (dists_sqr[1] < max_dist_sqr) {
77  mean_dist += std::sqrt(dists_sqr[1]);
78  num++;
79  }
80  }
81 
82  return (mean_dist / num);
83 };
84 
85 ///////////////////////////////////////////////////////////////////////////////////////////
86 template <typename PointT>
87 inline float
89  const pcl::Indices& indices,
90  float max_dist,
91  int nr_threads)
92 {
93  const float max_dist_sqr = max_dist * max_dist;
94  const std::size_t s = indices.size();
95 
96  typename pcl::search::Search<PointT>::Ptr tree(pcl::search::autoSelectMethod<PointT>(
98 
99  float mean_dist = 0.f;
100  int num = 0;
101  pcl::Indices ids(2);
102  std::vector<float> dists_sqr(2);
103 
104  pcl::utils::ignore(nr_threads);
105 #if OPENMP_LEGACY_CONST_DATA_SHARING_RULE
106 #pragma omp parallel for default(none) shared(tree, cloud, indices) \
107  firstprivate(ids, dists_sqr) reduction(+ : mean_dist, num) num_threads(nr_threads)
108 #else
109 #pragma omp parallel for default(none) shared(tree, cloud, indices, s, max_dist_sqr) \
110  firstprivate(ids, dists_sqr) reduction(+ : mean_dist, num) num_threads(nr_threads)
111 #endif
112  for (int i = 0; i < 1000; i++) {
113  tree->nearestKSearch((*cloud)[indices[rand() % s]], 2, ids, dists_sqr);
114  if (dists_sqr[1] < max_dist_sqr) {
115  mean_dist += std::sqrt(dists_sqr[1]);
116  num++;
117  }
118  }
119 
120  return (mean_dist / num);
121 };
122 
123 ///////////////////////////////////////////////////////////////////////////////////////////
124 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
127 : source_normals_()
128 , target_normals_()
129 , score_threshold_(std::numeric_limits<float>::max())
130 , fitness_score_(std::numeric_limits<float>::max())
131 {
132  reg_name_ = "pcl::registration::FPCSInitialAlignment";
133  max_iterations_ = 0;
134  ransac_iterations_ = 1000;
137 }
138 
139 ///////////////////////////////////////////////////////////////////////////////////////////
140 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
141 void
143  computeTransformation(PointCloudSource& output, const Eigen::Matrix4f& guess)
144 {
145  if (!initCompute())
146  return;
147 
148  final_transformation_ = guess;
149  bool abort = false;
150  std::vector<MatchingCandidates> all_candidates(max_iterations_);
151  pcl::StopWatch timer;
152 
153 #pragma omp parallel default(none) shared(abort, all_candidates, timer) \
154  num_threads(nr_threads_)
155  {
156 #ifdef _OPENMP
157  const unsigned int seed =
158  static_cast<unsigned int>(std::time(nullptr)) ^ omp_get_thread_num();
159  std::srand(seed);
160  PCL_DEBUG("[%s::computeTransformation] Using seed=%u\n", reg_name_.c_str(), seed);
161 #pragma omp for schedule(dynamic)
162 #endif
163  for (int i = 0; i < max_iterations_; i++) {
164 #pragma omp flush(abort)
165 
166  MatchingCandidates candidates(1);
167  pcl::Indices base_indices(4);
168  all_candidates[i] = candidates;
169 
170  if (!abort) {
171  float ratio[2];
172  // select four coplanar point base
173  if (selectBase(base_indices, ratio) == 0) {
174  // calculate candidate pair correspondences using diagonal lengths of base
175  pcl::Correspondences pairs_a, pairs_b;
176  if (bruteForceCorrespondences(base_indices[0], base_indices[1], pairs_a) ==
177  0 &&
178  bruteForceCorrespondences(base_indices[2], base_indices[3], pairs_b) ==
179  0) {
180  // determine candidate matches by combining pair correspondences based on
181  // segment distances
182  std::vector<pcl::Indices> matches;
183  if (determineBaseMatches(base_indices, matches, pairs_a, pairs_b, ratio) ==
184  0) {
185  // check and evaluate candidate matches and store them
186  handleMatches(base_indices, matches, candidates);
187  if (!candidates.empty())
188  all_candidates[i] = candidates;
189  }
190  }
191  }
192 
193  // check terminate early (time or fitness_score threshold reached)
194  if (!candidates.empty() && candidates[0].fitness_score < score_threshold_) {
195  PCL_DEBUG("[%s::computeTransformation] Terminating because fitness score "
196  "(%g) is below threshold (%g).\n",
197  reg_name_.c_str(),
198  candidates[0].fitness_score,
199  score_threshold_);
200  abort = true;
201  }
202  else if (timer.getTimeSeconds() > max_runtime_) {
203  PCL_DEBUG("[%s::computeTransformation] Terminating because time exceeded.\n",
204  reg_name_.c_str());
205  abort = true;
206  }
207 
208 #pragma omp flush(abort)
209  }
210  }
211  }
212 
213  // determine best match over all tries
214  finalCompute(all_candidates);
215 
216  // apply the final transformation
217  pcl::transformPointCloud(*input_, output, final_transformation_);
218 
219  deinitCompute();
220 }
221 
222 ///////////////////////////////////////////////////////////////////////////////////////////
223 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
224 bool
226  initCompute()
227 {
228  const unsigned int seed = std::time(nullptr);
229  std::srand(seed);
230  PCL_DEBUG("[%s::initCompute] Using seed=%u\n", reg_name_.c_str(), seed);
231 
232  // basic pcl initialization
234  return (false);
235 
236  // check if source and target are given
237  if (!input_ || !target_) {
238  PCL_ERROR("[%s::initCompute] Source or target dataset not given!\n",
239  reg_name_.c_str());
240  return (false);
241  }
242 
243  if (!target_indices_ || target_indices_->empty()) {
244  target_indices_.reset(new pcl::Indices(target_->size()));
245  int index = 0;
246  for (auto& target_index : *target_indices_)
247  target_index = index++;
248  target_cloud_updated_ = true;
249  }
250 
251  // if a sample size for the point clouds is given; preferably no sampling of target
252  // cloud
253  if (nr_samples_ > 0 && static_cast<std::size_t>(nr_samples_) < indices_->size()) {
254  source_indices_ = pcl::IndicesPtr(new pcl::Indices);
255  pcl::RandomSample<PointSource> random_sampling;
256  random_sampling.setInputCloud(input_);
257  random_sampling.setIndices(indices_);
258  random_sampling.setSample(nr_samples_);
259  random_sampling.setSeed(seed);
260  random_sampling.filter(*source_indices_);
261  }
262  else
263  source_indices_ = indices_;
264 
265  // check usage of normals
266  if (source_normals_ && target_normals_ && source_normals_->size() == input_->size() &&
267  target_normals_->size() == target_->size())
268  use_normals_ = true;
269 
270  // set up tree structures
271  if (target_cloud_updated_) {
272  tree_->setInputCloud(target_, target_indices_);
273  target_cloud_updated_ = false;
274  }
275 
276  // set predefined variables
277  constexpr int min_iterations = 4;
278  constexpr float diameter_fraction = 0.3f;
279 
280  // get diameter of input cloud (distance between farthest points)
281  Eigen::Vector4f pt_min, pt_max;
282  pcl::getMinMax3D(*target_, *target_indices_, pt_min, pt_max);
283  diameter_ = (pt_max - pt_min).norm();
284 
285  // derive the limits for the random base selection
286  float max_base_diameter = diameter_ * approx_overlap_ * 2.f;
287  max_base_diameter_sqr_ = max_base_diameter * max_base_diameter;
288 
289 #ifdef _OPENMP
290  if (nr_threads_ < 1) {
291  nr_threads_ = omp_get_num_procs();
292  PCL_DEBUG("[%s::initCompute] Setting number of threads to %i.\n",
293  reg_name_.c_str(),
294  nr_threads_);
295  }
296 #endif // _OPENMP
297 
298  // normalize the delta
299  if (normalize_delta_) {
300  float mean_dist = getMeanPointDensity<PointTarget>(
301  target_, *target_indices_, 0.05f * diameter_, nr_threads_);
302  delta_ *= mean_dist;
303  }
304 
305  // heuristic determination of number of trials to have high probability of finding a
306  // good solution
307  if (max_iterations_ == 0) {
308  float first_est = std::log(small_error_) /
309  std::log(1.0 - std::pow(static_cast<double>(approx_overlap_),
310  static_cast<double>(min_iterations)));
311  max_iterations_ =
312  static_cast<int>(first_est / (diameter_fraction * approx_overlap_ * 2.f));
313  PCL_DEBUG("[%s::initCompute] Estimated max iterations as %i\n",
314  reg_name_.c_str(),
315  max_iterations_);
316  }
317 
318  // set further parameter
319  if (score_threshold_ == std::numeric_limits<float>::max())
320  score_threshold_ = 1.f - approx_overlap_;
321 
322  if (max_iterations_ < 4)
323  max_iterations_ = 4;
324 
325  if (max_runtime_ < 1)
326  max_runtime_ = std::numeric_limits<int>::max();
327 
328  // calculate internal parameters based on the the estimated point density
329  max_pair_diff_ = delta_ * 2.f;
330  max_edge_diff_ = delta_ * 4.f;
331  coincidation_limit_ = delta_ * 2.f; // EDITED: originally std::sqrt (delta_ * 2.f)
332  max_mse_ = powf(delta_ * 2.f, 2.f);
333  max_inlier_dist_sqr_ = powf(delta_ * 2.f, 2.f);
334  PCL_DEBUG("[%s::initCompute] delta_=%g, max_inlier_dist_sqr_=%g, "
335  "coincidation_limit_=%g, max_edge_diff_=%g, max_pair_diff_=%g\n",
336  reg_name_.c_str(),
337  delta_,
338  max_inlier_dist_sqr_,
339  coincidation_limit_,
340  max_edge_diff_,
341  max_pair_diff_);
342 
343  // reset fitness_score
344  fitness_score_ = std::numeric_limits<float>::max();
345 
346  return (true);
347 }
348 
349 ///////////////////////////////////////////////////////////////////////////////////////////
350 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
351 int
353  selectBase(pcl::Indices& base_indices, float (&ratio)[2])
354 {
355  const float too_close_sqr = max_base_diameter_sqr_ * 0.01;
356 
357  Eigen::VectorXf coefficients(4);
359  plane.setIndices(target_indices_);
360  Eigen::Vector4f centre_pt;
361  float nearest_to_plane = std::numeric_limits<float>::max();
362 
363  // repeat base search until valid quadruple was found or ransac_iterations_ number of
364  // tries were unsuccessful
365  for (int i = 0; i < ransac_iterations_; i++) {
366  // random select an appropriate point triple
367  if (selectBaseTriangle(base_indices) < 0)
368  continue;
369 
370  pcl::Indices base_triple(base_indices.begin(), base_indices.end() - 1);
371  plane.computeModelCoefficients(base_triple, coefficients);
372  pcl::compute3DCentroid(*target_, base_triple, centre_pt);
373 
374  // loop over all points in source cloud to find most suitable fourth point
375  const PointTarget* pt1 = &((*target_)[base_indices[0]]);
376  const PointTarget* pt2 = &((*target_)[base_indices[1]]);
377  const PointTarget* pt3 = &((*target_)[base_indices[2]]);
378 
379  for (const auto& target_index : *target_indices_) {
380  const PointTarget* pt4 = &((*target_)[target_index]);
381 
382  float d1 = pcl::squaredEuclideanDistance(*pt4, *pt1);
383  float d2 = pcl::squaredEuclideanDistance(*pt4, *pt2);
384  float d3 = pcl::squaredEuclideanDistance(*pt4, *pt3);
385  float d4 = (pt4->getVector3fMap() - centre_pt.head<3>()).squaredNorm();
386 
387  // check distance between points w.r.t minimum sampling distance; EDITED -> 4th
388  // point now also limited by max base line
389  if (d1 < too_close_sqr || d2 < too_close_sqr || d3 < too_close_sqr ||
390  d4 < too_close_sqr || d1 > max_base_diameter_sqr_ ||
391  d2 > max_base_diameter_sqr_ || d3 > max_base_diameter_sqr_)
392  continue;
393 
394  // check distance to plane to get point closest to plane
395  float dist_to_plane = pcl::pointToPlaneDistance(*pt4, coefficients);
396  if (dist_to_plane < nearest_to_plane) {
397  base_indices[3] = target_index;
398  nearest_to_plane = dist_to_plane;
399  }
400  }
401 
402  // check if at least one point fulfilled the conditions
403  if (nearest_to_plane != std::numeric_limits<float>::max()) {
404  // order points to build largest quadrangle and calculate intersection ratios of
405  // diagonals
406  setupBase(base_indices, ratio);
407  return (0);
408  }
409  }
410 
411  // return unsuccessful if no quadruple was selected
412  return (-1);
413 }
414 
415 ///////////////////////////////////////////////////////////////////////////////////////////
416 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
417 int
419  selectBaseTriangle(pcl::Indices& base_indices)
420 {
421  const auto nr_points = target_indices_->size();
422  float best_t = 0.f;
423 
424  // choose random first point
425  base_indices[0] = (*target_indices_)[rand() % nr_points];
426  auto* index1 = base_indices.data();
427 
428  // random search for 2 other points (as far away as overlap allows)
429  for (int i = 0; i < ransac_iterations_; i++) {
430  auto* index2 = &(*target_indices_)[rand() % nr_points];
431  auto* index3 = &(*target_indices_)[rand() % nr_points];
432 
433  Eigen::Vector3f u =
434  (*target_)[*index2].getVector3fMap() - (*target_)[*index1].getVector3fMap();
435  Eigen::Vector3f v =
436  (*target_)[*index3].getVector3fMap() - (*target_)[*index1].getVector3fMap();
437  float t =
438  u.cross(v).squaredNorm(); // triangle area (0.5 * sqrt(t)) should be maximal
439 
440  // check for most suitable point triple
441  if (t > best_t && u.squaredNorm() < max_base_diameter_sqr_ &&
442  v.squaredNorm() < max_base_diameter_sqr_) {
443  best_t = t;
444  base_indices[1] = *index2;
445  base_indices[2] = *index3;
446  }
447  }
448 
449  // return if a triplet could be selected
450  return (best_t == 0.f ? -1 : 0);
451 }
452 
453 ///////////////////////////////////////////////////////////////////////////////////////////
454 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
455 void
457  setupBase(pcl::Indices& base_indices, float (&ratio)[2])
458 {
459  float best_t = std::numeric_limits<float>::max();
460  const pcl::Indices copy(base_indices.begin(), base_indices.end());
461  pcl::Indices temp(base_indices.begin(), base_indices.end());
462 
463  // loop over all combinations of base points
464  for (auto i = copy.begin(), i_e = copy.end(); i != i_e; ++i)
465  for (auto j = copy.begin(), j_e = copy.end(); j != j_e; ++j) {
466  if (i == j)
467  continue;
468 
469  for (auto k = copy.begin(), k_e = copy.end(); k != k_e; ++k) {
470  if (k == j || k == i)
471  continue;
472 
473  auto l = copy.begin();
474  while (l == i || l == j || l == k)
475  ++l;
476 
477  temp[0] = *i;
478  temp[1] = *j;
479  temp[2] = *k;
480  temp[3] = *l;
481 
482  // calculate diagonal intersection ratios and check for suitable segment to
483  // segment distances
484  float ratio_temp[2];
485  float t = segmentToSegmentDist(temp, ratio_temp);
486  if (t < best_t) {
487  best_t = t;
488  ratio[0] = ratio_temp[0];
489  ratio[1] = ratio_temp[1];
490  base_indices = temp;
491  }
492  }
493  }
494 }
495 
496 ///////////////////////////////////////////////////////////////////////////////////////////
497 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
498 float
500  segmentToSegmentDist(const pcl::Indices& base_indices, float (&ratio)[2])
501 {
502  // get point vectors
503  Eigen::Vector3f u = (*target_)[base_indices[1]].getVector3fMap() -
504  (*target_)[base_indices[0]].getVector3fMap();
505  Eigen::Vector3f v = (*target_)[base_indices[3]].getVector3fMap() -
506  (*target_)[base_indices[2]].getVector3fMap();
507  Eigen::Vector3f w = (*target_)[base_indices[0]].getVector3fMap() -
508  (*target_)[base_indices[2]].getVector3fMap();
509 
510  // calculate segment distances
511  float a = u.dot(u);
512  float b = u.dot(v);
513  float c = v.dot(v);
514  float d = u.dot(w);
515  float e = v.dot(w);
516  float D = a * c - b * b;
517  float sN = 0.f, sD = D;
518  float tN = 0.f, tD = D;
519 
520  // check segments
521  if (D < small_error_) {
522  sN = 0.f;
523  sD = 1.f;
524  tN = e;
525  tD = c;
526  }
527  else {
528  sN = (b * e - c * d);
529  tN = (a * e - b * d);
530 
531  if (sN < 0.f) {
532  sN = 0.f;
533  tN = e;
534  tD = c;
535  }
536  else if (sN > sD) {
537  sN = sD;
538  tN = e + b;
539  tD = c;
540  }
541  }
542 
543  if (tN < 0.f) {
544  tN = 0.f;
545 
546  if (-d < 0.f)
547  sN = 0.f;
548 
549  else if (-d > a)
550  sN = sD;
551 
552  else {
553  sN = -d;
554  sD = a;
555  }
556  }
557 
558  else if (tN > tD) {
559  tN = tD;
560 
561  if ((-d + b) < 0.f)
562  sN = 0.f;
563 
564  else if ((-d + b) > a)
565  sN = sD;
566 
567  else {
568  sN = (-d + b);
569  sD = a;
570  }
571  }
572 
573  // set intersection ratios
574  ratio[0] = (std::abs(sN) < small_error_) ? 0.f : sN / sD;
575  ratio[1] = (std::abs(tN) < small_error_) ? 0.f : tN / tD;
576 
577  Eigen::Vector3f x = w + (ratio[0] * u) - (ratio[1] * v);
578  return (x.norm());
579 }
580 
581 ///////////////////////////////////////////////////////////////////////////////////////////
582 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
583 int
585  bruteForceCorrespondences(int idx1, int idx2, pcl::Correspondences& pairs)
586 {
587  const float max_norm_diff = 0.5f * max_norm_diff_ * M_PI / 180.f;
588 
589  // calculate reference segment distance and normal angle
590  float ref_dist = pcl::euclideanDistance((*target_)[idx1], (*target_)[idx2]);
591  float ref_norm_angle =
592  (use_normals_ ? ((*target_normals_)[idx1].getNormalVector3fMap() -
593  (*target_normals_)[idx2].getNormalVector3fMap())
594  .norm()
595  : 0.f);
596 
597  // loop over all pairs of points in source point cloud
598  auto it_out = source_indices_->begin(), it_out_e = source_indices_->end() - 1;
599  auto it_in_e = source_indices_->end();
600  for (; it_out != it_out_e; it_out++) {
601  auto it_in = it_out + 1;
602  const PointSource* pt1 = &(*input_)[*it_out];
603  for (; it_in != it_in_e; it_in++) {
604  const PointSource* pt2 = &(*input_)[*it_in];
605 
606  // check point distance compared to reference dist (from base)
607  float dist = pcl::euclideanDistance(*pt1, *pt2);
608  if (std::abs(dist - ref_dist) < max_pair_diff_) {
609  // add here normal evaluation if normals are given
610  if (use_normals_) {
611  const NormalT* pt1_n = &((*source_normals_)[*it_out]);
612  const NormalT* pt2_n = &((*source_normals_)[*it_in]);
613 
614  float norm_angle_1 =
615  (pt1_n->getNormalVector3fMap() - pt2_n->getNormalVector3fMap()).norm();
616  float norm_angle_2 =
617  (pt1_n->getNormalVector3fMap() + pt2_n->getNormalVector3fMap()).norm();
618 
619  float norm_diff = std::min<float>(std::abs(norm_angle_1 - ref_norm_angle),
620  std::abs(norm_angle_2 - ref_norm_angle));
621  if (norm_diff > max_norm_diff)
622  continue;
623  }
624 
625  pairs.emplace_back(*it_in, *it_out, dist);
626  pairs.emplace_back(*it_out, *it_in, dist);
627  }
628  }
629  }
630 
631  // return success if at least one correspondence was found
632  return (pairs.empty() ? -1 : 0);
633 }
634 
635 ///////////////////////////////////////////////////////////////////////////////////////////
636 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
637 int
639  determineBaseMatches(const pcl::Indices& base_indices,
640  std::vector<pcl::Indices>& matches,
641  const pcl::Correspondences& pairs_a,
642  const pcl::Correspondences& pairs_b,
643  const float (&ratio)[2])
644 {
645  // calculate edge lengths of base
646  float dist_base[4];
647  dist_base[0] =
648  pcl::euclideanDistance((*target_)[base_indices[0]], (*target_)[base_indices[2]]);
649  dist_base[1] =
650  pcl::euclideanDistance((*target_)[base_indices[0]], (*target_)[base_indices[3]]);
651  dist_base[2] =
652  pcl::euclideanDistance((*target_)[base_indices[1]], (*target_)[base_indices[2]]);
653  dist_base[3] =
654  pcl::euclideanDistance((*target_)[base_indices[1]], (*target_)[base_indices[3]]);
655 
656  // loop over first point pair correspondences and store intermediate points 'e' in new
657  // point cloud
658  PointCloudSourcePtr cloud_e(new PointCloudSource);
659  cloud_e->resize(pairs_a.size() * 2);
660  auto it_pt = cloud_e->begin();
661  for (const auto& pair : pairs_a) {
662  const PointSource* pt1 = &((*input_)[pair.index_match]);
663  const PointSource* pt2 = &((*input_)[pair.index_query]);
664 
665  // calculate intermediate points using both ratios from base (r1,r2)
666  for (int i = 0; i < 2; i++, it_pt++) {
667  it_pt->x = pt1->x + ratio[i] * (pt2->x - pt1->x);
668  it_pt->y = pt1->y + ratio[i] * (pt2->y - pt1->y);
669  it_pt->z = pt1->z + ratio[i] * (pt2->z - pt1->z);
670  }
671  }
672 
673  // initialize new kd tree of intermediate points from first point pair correspondences
675  tree_e->setInputCloud(cloud_e);
676 
677  pcl::Indices ids;
678  std::vector<float> dists_sqr;
679 
680  // loop over second point pair correspondences
681  for (const auto& pair : pairs_b) {
682  const PointTarget* pt1 = &((*input_)[pair.index_match]);
683  const PointTarget* pt2 = &((*input_)[pair.index_query]);
684 
685  // calculate intermediate points using both ratios from base (r1,r2)
686  for (const float& r : ratio) {
687  PointTarget pt_e;
688  pt_e.x = pt1->x + r * (pt2->x - pt1->x);
689  pt_e.y = pt1->y + r * (pt2->y - pt1->y);
690  pt_e.z = pt1->z + r * (pt2->z - pt1->z);
691 
692  // search for corresponding intermediate points
693  tree_e->radiusSearch(pt_e, coincidation_limit_, ids, dists_sqr);
694  for (const auto& id : ids) {
695  pcl::Indices match_indices(4);
696 
697  match_indices[0] =
698  pairs_a[static_cast<int>(std::floor((id / 2.f)))].index_match;
699  match_indices[1] =
700  pairs_a[static_cast<int>(std::floor((id / 2.f)))].index_query;
701  match_indices[2] = pair.index_match;
702  match_indices[3] = pair.index_query;
703  if (match_indices[0] == match_indices[2] ||
704  match_indices[0] == match_indices[3] ||
705  match_indices[1] == match_indices[2] ||
706  match_indices[1] == match_indices[3])
707  continue;
708 
709  // EDITED: added coarse check of match based on edge length (due to rigid-body )
710  if (checkBaseMatch(match_indices, dist_base) < 0)
711  continue;
712 
713  matches.push_back(match_indices);
714  }
715  }
716  }
717 
718  // return unsuccessful if no match was found
719  if (matches.empty()) {
720  PCL_DEBUG("[%s::determineBaseMatches] no matches found\n", reg_name_.c_str());
721  return -1;
722  }
723  else {
724  PCL_DEBUG("[%s::determineBaseMatches] found %zu matches\n",
725  reg_name_.c_str(),
726  matches.size());
727  return 0;
728  }
729 }
730 
731 ///////////////////////////////////////////////////////////////////////////////////////////
732 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
733 int
735  checkBaseMatch(const pcl::Indices& match_indices, const float (&dist_ref)[4])
736 {
737  float d0 =
738  pcl::euclideanDistance((*input_)[match_indices[0]], (*input_)[match_indices[2]]);
739  float d1 =
740  pcl::euclideanDistance((*input_)[match_indices[0]], (*input_)[match_indices[3]]);
741  float d2 =
742  pcl::euclideanDistance((*input_)[match_indices[1]], (*input_)[match_indices[2]]);
743  float d3 =
744  pcl::euclideanDistance((*input_)[match_indices[1]], (*input_)[match_indices[3]]);
745 
746  // check edge distances of match w.r.t the base
747  return (std::abs(d0 - dist_ref[0]) < max_edge_diff_ &&
748  std::abs(d1 - dist_ref[1]) < max_edge_diff_ &&
749  std::abs(d2 - dist_ref[2]) < max_edge_diff_ &&
750  std::abs(d3 - dist_ref[3]) < max_edge_diff_)
751  ? 0
752  : -1;
753 }
754 
755 ///////////////////////////////////////////////////////////////////////////////////////////
756 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
757 void
759  handleMatches(const pcl::Indices& base_indices,
760  std::vector<pcl::Indices>& matches,
761  MatchingCandidates& candidates)
762 {
763  candidates.resize(1);
764  float fitness_score = std::numeric_limits<float>::max();
765 
766  // loop over all Candidate matches
767  for (auto& match : matches) {
768  Eigen::Matrix4f transformation_temp;
769  pcl::Correspondences correspondences_temp;
770  correspondences_temp.emplace_back(match[0], base_indices[0], 0.0);
771  correspondences_temp.emplace_back(match[1], base_indices[1], 0.0);
772  correspondences_temp.emplace_back(match[2], base_indices[2], 0.0);
773  correspondences_temp.emplace_back(match[3], base_indices[3], 0.0);
774 
775  // check match based on residuals of the corresponding points after
776  if (validateMatch(base_indices, match, correspondences_temp, transformation_temp) <
777  0)
778  continue;
779 
780  // check resulting using a sub sample of the source point cloud and compare to
781  // previous matches
782  if (validateTransformation(transformation_temp, fitness_score) < 0)
783  continue;
784 
785  // store best match as well as associated fitness_score and transformation
786  candidates[0].fitness_score = fitness_score;
787  candidates[0].transformation = transformation_temp;
788  correspondences_temp.erase(correspondences_temp.end() - 1);
789  candidates[0].correspondences = correspondences_temp;
790  }
791 }
792 
793 ///////////////////////////////////////////////////////////////////////////////////////////
794 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
795 void
797  linkMatchWithBase(const pcl::Indices& base_indices,
798  pcl::Indices& match_indices,
799  pcl::Correspondences& correspondences)
800 {
801  // calculate centroid of base and target
802  Eigen::Vector4f centre_base{0, 0, 0, 0}, centre_match{0, 0, 0, 0};
803  pcl::compute3DCentroid(*target_, base_indices, centre_base);
804  pcl::compute3DCentroid(*input_, match_indices, centre_match);
805 
806  PointTarget centre_pt_base;
807  centre_pt_base.x = centre_base[0];
808  centre_pt_base.y = centre_base[1];
809  centre_pt_base.z = centre_base[2];
810 
811  PointSource centre_pt_match;
812  centre_pt_match.x = centre_match[0];
813  centre_pt_match.y = centre_match[1];
814  centre_pt_match.z = centre_match[2];
815 
816  // find corresponding points according to their distance to the centroid
817  pcl::Indices copy = match_indices;
818 
819  auto it_match_orig = match_indices.begin();
820  for (auto it_base = base_indices.cbegin(), it_base_e = base_indices.cend();
821  it_base != it_base_e;
822  it_base++, it_match_orig++) {
823  float dist_sqr_1 =
824  pcl::squaredEuclideanDistance((*target_)[*it_base], centre_pt_base);
825  float best_diff_sqr = std::numeric_limits<float>::max();
826  int best_index = -1;
827 
828  for (const auto& match_index : copy) {
829  // calculate difference of distances to centre point
830  float dist_sqr_2 =
831  pcl::squaredEuclideanDistance((*input_)[match_index], centre_pt_match);
832  float diff_sqr = std::abs(dist_sqr_1 - dist_sqr_2);
833 
834  if (diff_sqr < best_diff_sqr) {
835  best_diff_sqr = diff_sqr;
836  best_index = match_index;
837  }
838  }
839 
840  // assign new correspondence and update indices of matched targets
841  correspondences.emplace_back(best_index, *it_base, best_diff_sqr);
842  *it_match_orig = best_index;
843  }
844 }
845 
846 ///////////////////////////////////////////////////////////////////////////////////////////
847 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
848 int
850  validateMatch(const pcl::Indices& base_indices,
851  const pcl::Indices& match_indices,
852  const pcl::Correspondences& correspondences,
853  Eigen::Matrix4f& transformation)
854 {
855  // only use triplet of points to simplify process (possible due to planar case)
856  pcl::Correspondences correspondences_temp = correspondences;
857  correspondences_temp.erase(correspondences_temp.end() - 1);
858 
859  // estimate transformation between correspondence set
860  transformation_estimation_->estimateRigidTransformation(
861  *input_, *target_, correspondences_temp, transformation);
862 
863  // transform base points
864  PointCloudSource match_transformed;
865  pcl::transformPointCloud(*input_, match_indices, match_transformed, transformation);
866 
867  // calculate residuals of transformation and check against maximum threshold
868  std::size_t nr_points = correspondences_temp.size();
869  float mse = 0.f;
870  for (std::size_t i = 0; i < nr_points; i++)
871  mse += pcl::squaredEuclideanDistance(match_transformed.points[i],
872  target_->points[base_indices[i]]);
873 
874  mse /= nr_points;
875  return (mse < max_mse_ ? 0 : -1);
876 }
877 
878 ///////////////////////////////////////////////////////////////////////////////////////////
879 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
880 int
882  validateTransformation(Eigen::Matrix4f& transformation, float& fitness_score)
883 {
884  // transform source point cloud
885  PointCloudSource source_transformed;
887  *input_, *source_indices_, source_transformed, transformation);
888 
889  std::size_t nr_points = source_transformed.size();
890  std::size_t terminate_value =
891  fitness_score > 1 ? 0
892  : static_cast<std::size_t>((1.f - fitness_score) * nr_points);
893 
894  float inlier_score_temp = 0;
895  pcl::Indices ids(1);
896  std::vector<float> dists_sqr(1);
897  auto it = source_transformed.begin();
898 
899  for (std::size_t i = 0; i < nr_points; it++, i++) {
900  // search for nearest point using kd tree search
901  tree_->nearestKSearch(*it, 1, ids, dists_sqr);
902  inlier_score_temp += (dists_sqr[0] < max_inlier_dist_sqr_ ? 1 : 0);
903 
904  // early terminating
905  if (nr_points - i + inlier_score_temp < terminate_value)
906  break;
907  }
908 
909  // check current costs and return unsuccessful if larger than previous ones
910  inlier_score_temp /= static_cast<float>(nr_points);
911  float fitness_score_temp = 1.f - inlier_score_temp;
912 
913  if (fitness_score_temp > fitness_score)
914  return (-1);
915 
916  fitness_score = fitness_score_temp;
917  return (0);
918 }
919 
920 ///////////////////////////////////////////////////////////////////////////////////////////
921 template <typename PointSource, typename PointTarget, typename NormalT, typename Scalar>
922 void
924  finalCompute(const std::vector<MatchingCandidates>& candidates)
925 {
926  // get best fitness_score over all tries
927  int nr_candidates = static_cast<int>(candidates.size());
928  int best_index = -1;
929  float best_score = std::numeric_limits<float>::max();
930  for (int i = 0; i < nr_candidates; i++) {
931  const float& fitness_score = candidates[i][0].fitness_score;
932  if (fitness_score < best_score) {
933  best_score = fitness_score;
934  best_index = i;
935  }
936  }
937  PCL_DEBUG(
938  "[%s::finalCompute] best score is %g (iteration %i), out of %zu iterations.\n",
939  reg_name_.c_str(),
940  best_score,
941  best_index,
942  candidates.size());
943 
944  // check if a valid candidate was available
945  if (!(best_index < 0)) {
946  fitness_score_ = candidates[best_index][0].fitness_score;
947  final_transformation_ = candidates[best_index][0].transformation;
948  *correspondences_ = candidates[best_index][0].correspondences;
949 
950  // here we define convergence if resulting fitness_score is below 1-threshold
951  converged_ = fitness_score_ < score_threshold_;
952  }
953 }
954 
955 ///////////////////////////////////////////////////////////////////////////////////////////
956 
957 #endif // PCL_REGISTRATION_IMPL_IA_4PCS_H_
void filter(Indices &indices)
Calls the filtering method and returns the filtered point cloud indices.
PCL base class.
Definition: pcl_base.h:70
virtual void setInputCloud(const PointCloudConstPtr &cloud)
Provide a pointer to the input dataset.
Definition: pcl_base.hpp:65
virtual void setIndices(const IndicesPtr &indices)
Provide a pointer to the vector of indices that represents the input data.
Definition: pcl_base.hpp:72
std::size_t size() const
Definition: point_cloud.h:444
shared_ptr< const PointCloud< PointT > > ConstPtr
Definition: point_cloud.h:415
RandomSample applies a random sampling with uniform probability.
Definition: random_sample.h:56
void setSeed(unsigned int seed)
Set seed of random function.
void setSample(unsigned int sample)
Set number of indices to be sampled.
Definition: random_sample.h:89
typename KdTreeReciprocal::Ptr KdTreeReciprocalPtr
Definition: registration.h:74
std::string reg_name_
The registration method name.
Definition: registration.h:548
typename PointCloudSource::Ptr PointCloudSourcePtr
Definition: registration.h:77
int ransac_iterations_
The number of iterations RANSAC should run for.
Definition: registration.h:566
TransformationEstimationPtr transformation_estimation_
A TransformationEstimation object, used to calculate the 4x4 rigid transformation.
Definition: registration.h:625
int max_iterations_
The maximum number of iterations the internal optimization should run for.
Definition: registration.h:563
void setIndices(const IndicesPtr &indices)
Provide a pointer to the vector of indices that represents the input data.
Definition: sac_model.h:312
SampleConsensusModelPlane defines a model for 3D plane segmentation.
bool computeModelCoefficients(const Indices &samples, Eigen::VectorXf &model_coefficients) const override
Check whether the given index samples can form a valid plane model, compute the model coefficients fr...
Simple stopwatch.
Definition: time.h:59
double getTimeSeconds() const
Retrieve the time in seconds spent since the last call to reset().
Definition: time.h:74
virtual void finalCompute(const std::vector< MatchingCandidates > &candidates)
Final computation of best match out of vector of best matches.
Definition: ia_fpcs.hpp:924
void setupBase(pcl::Indices &base_indices, float(&ratio)[2])
Setup the base (four coplanar points) by ordering the points and computing intersection ratios and se...
Definition: ia_fpcs.hpp:457
int selectBaseTriangle(pcl::Indices &base_indices)
Select randomly a triplet of points with large point-to-point distances.
Definition: ia_fpcs.hpp:419
virtual int bruteForceCorrespondences(int idx1, int idx2, pcl::Correspondences &pairs)
Search for corresponding point pairs given the distance between two base points.
Definition: ia_fpcs.hpp:585
int selectBase(pcl::Indices &base_indices, float(&ratio)[2])
Select an approximately coplanar set of four points from the source cloud.
Definition: ia_fpcs.hpp:353
virtual int validateTransformation(Eigen::Matrix4f &transformation, float &fitness_score)
Validate the transformation by calculating the number of inliers after transforming the source cloud.
Definition: ia_fpcs.hpp:882
virtual int determineBaseMatches(const pcl::Indices &base_indices, std::vector< pcl::Indices > &matches, const pcl::Correspondences &pairs_a, const pcl::Correspondences &pairs_b, const float(&ratio)[2])
Determine base matches by combining the point pair candidate and search for coinciding intersection p...
Definition: ia_fpcs.hpp:639
virtual void linkMatchWithBase(const pcl::Indices &base_indices, pcl::Indices &match_indices, pcl::Correspondences &correspondences)
Sets the correspondences between the base B and the match M by using the distance of each point to th...
Definition: ia_fpcs.hpp:797
virtual void handleMatches(const pcl::Indices &base_indices, std::vector< pcl::Indices > &matches, MatchingCandidates &candidates)
Method to handle current candidate matches.
Definition: ia_fpcs.hpp:759
int checkBaseMatch(const pcl::Indices &match_indices, const float(&ds)[4])
Check if outer rectangle distance of matched points fit with the base rectangle.
Definition: ia_fpcs.hpp:735
float segmentToSegmentDist(const pcl::Indices &base_indices, float(&ratio)[2])
Calculate intersection ratios and segment to segment distances of base diagonals.
Definition: ia_fpcs.hpp:500
virtual int validateMatch(const pcl::Indices &base_indices, const pcl::Indices &match_indices, const pcl::Correspondences &correspondences, Eigen::Matrix4f &transformation)
Validate the matching by computing the transformation between the source and target based on the four...
Definition: ia_fpcs.hpp:850
void computeTransformation(PointCloudSource &output, const Eigen::Matrix4f &guess) override
Rigid transformation computation method.
Definition: ia_fpcs.hpp:143
virtual bool initCompute()
Internal computation initialization.
Definition: ia_fpcs.hpp:226
TransformationEstimation3Points represents the class for transformation estimation based on:
search::KdTree is a wrapper class which inherits the pcl::KdTree class for performing search function...
Definition: kdtree.h:62
shared_ptr< pcl::search::Search< PointT > > Ptr
Definition: search.h:81
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 to do distance calculations.
Define methods for measuring time spent in code blocks.
void getMinMax3D(const pcl::PointCloud< PointT > &cloud, PointT &min_pt, PointT &max_pt)
Get the minimum and maximum values on each of the 3 (x-y-z) dimensions in a given pointcloud.
Definition: common.hpp:295
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.
Definition: transforms.hpp:221
unsigned int compute3DCentroid(ConstCloudIterator< PointT > &cloud_iterator, Eigen::Matrix< Scalar, 4, 1 > &centroid)
Compute the 3D (X-Y-Z) centroid of a set of points and return it as a 3D vector.
Definition: centroid.hpp:57
double pointToPlaneDistance(const Point &p, double a, double b, double c, double d)
Get the distance from a point to a plane (unsigned) defined by ax+by+cz+d=0.
std::vector< MatchingCandidate, Eigen::aligned_allocator< MatchingCandidate > > MatchingCandidates
@ many_knn_search
The search method will mainly be used for nearestKSearch where k is larger than 1.
void ignore(const T &...)
Utility function to eliminate unused variable warnings.
Definition: utils.h:62
float squaredEuclideanDistance(const PointType1 &p1, const PointType2 &p2)
Calculate the squared euclidean distance between the two given points.
Definition: distances.h:182
float getMeanPointDensity(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, float max_dist, int nr_threads=1)
Compute the mean point density of a given point cloud.
Definition: ia_fpcs.hpp:55
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
float euclideanDistance(const PointType1 &p1, const PointType2 &p2)
Calculate the euclidean distance between the two given points.
Definition: distances.h:204
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition: types.h:133
shared_ptr< Indices > IndicesPtr
Definition: pcl_base.h:58
#define M_PI
Definition: pcl_macros.h:203
A point structure representing normal coordinates and the surface curvature estimate.