Point Cloud Library (PCL)  1.11.1-dev
joint_icp.hpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2009-2012, Willow Garage, Inc.
6  * Copyright (c) 2012-, Open Perception, Inc.
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
12  * are met:
13  *
14  * * Redistributions of source code must retain the above copyright
15  * notice, this list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above
17  * copyright notice, this list of conditions and the following
18  * disclaimer in the documentation and/or other materials provided
19  * with the distribution.
20  * * Neither the name of the copyright holder(s) nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  */
38 
39 #ifndef PCL_REGISTRATION_IMPL_JOINT_ICP_HPP_
40 #define PCL_REGISTRATION_IMPL_JOINT_ICP_HPP_
41 
42 #include <pcl/console/print.h>
43 #include <pcl/registration/boost.h>
44 #include <pcl/correspondence.h>
45 
46 namespace pcl {
47 
48 template <typename PointSource, typename PointTarget, typename Scalar>
49 void
51  PointCloudSource& output, const Matrix4& guess)
52 {
53  // Point clouds containing the correspondences of each point in <input, indices>
54  if (sources_.size() != targets_.size() || sources_.empty() || targets_.empty()) {
55  PCL_ERROR("[pcl::%s::computeTransformation] Must set InputSources and InputTargets "
56  "to the same, nonzero size!\n",
57  getClassName().c_str());
58  return;
59  }
60  bool manual_correspondence_estimations_set = true;
61  if (correspondence_estimations_.empty()) {
62  manual_correspondence_estimations_set = false;
63  correspondence_estimations_.resize(sources_.size());
64  for (std::size_t i = 0; i < sources_.size(); i++) {
65  correspondence_estimations_[i] = correspondence_estimation_->clone();
67  KdTreePtr tgt_tree(new KdTree);
68  correspondence_estimations_[i]->setSearchMethodTarget(tgt_tree);
69  correspondence_estimations_[i]->setSearchMethodSource(src_tree);
70  }
71  }
72  if (correspondence_estimations_.size() != sources_.size()) {
73  PCL_ERROR("[pcl::%s::computeTransform] Must set CorrespondenceEstimations to be "
74  "the same size as the joint\n",
75  getClassName().c_str());
76  return;
77  }
78  std::vector<PointCloudSourcePtr> inputs_transformed(sources_.size());
79  for (std::size_t i = 0; i < sources_.size(); i++) {
80  inputs_transformed[i].reset(new PointCloudSource);
81  }
82 
83  nr_iterations_ = 0;
84  converged_ = false;
85 
86  // Initialise final transformation to the guessed one
87  final_transformation_ = guess;
88 
89  // Make a combined transformed input and output
90  std::vector<std::size_t> input_offsets(sources_.size());
91  std::vector<std::size_t> target_offsets(targets_.size());
92  PointCloudSourcePtr sources_combined(new PointCloudSource);
93  PointCloudSourcePtr inputs_transformed_combined(new PointCloudSource);
94  PointCloudTargetPtr targets_combined(new PointCloudTarget);
95  std::size_t input_offset = 0;
96  std::size_t target_offset = 0;
97  for (std::size_t i = 0; i < sources_.size(); i++) {
98  // If the guessed transformation is non identity
99  if (guess != Matrix4::Identity()) {
100  // Apply guessed transformation prior to search for neighbours
101  this->transformCloud(*sources_[i], *inputs_transformed[i], guess);
102  }
103  else {
104  *inputs_transformed[i] = *sources_[i];
105  }
106  *sources_combined += *sources_[i];
107  *inputs_transformed_combined += *inputs_transformed[i];
108  *targets_combined += *targets_[i];
109  input_offsets[i] = input_offset;
110  target_offsets[i] = target_offset;
111  input_offset += inputs_transformed[i]->size();
112  target_offset += targets_[i]->size();
113  }
114 
115  transformation_ = Matrix4::Identity();
116  // Make blobs if necessary
117  determineRequiredBlobData();
118  // Pass in the default target for the Correspondence Estimation/Rejection code
119  for (std::size_t i = 0; i < sources_.size(); i++) {
120  correspondence_estimations_[i]->setInputTarget(targets_[i]);
121  if (correspondence_estimations_[i]->requiresTargetNormals()) {
122  PCLPointCloud2::Ptr target_blob(new PCLPointCloud2);
123  pcl::toPCLPointCloud2(*targets_[i], *target_blob);
124  correspondence_estimations_[i]->setTargetNormals(target_blob);
125  }
126  }
127 
128  PCLPointCloud2::Ptr targets_combined_blob(new PCLPointCloud2);
129  if (!correspondence_rejectors_.empty() && need_target_blob_)
130  pcl::toPCLPointCloud2(*targets_combined, *targets_combined_blob);
131 
132  for (std::size_t i = 0; i < correspondence_rejectors_.size(); ++i) {
133  registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i];
134  if (rej->requiresTargetPoints())
135  rej->setTargetPoints(targets_combined_blob);
136  if (rej->requiresTargetNormals() && target_has_normals_)
137  rej->setTargetNormals(targets_combined_blob);
138  }
139 
140  convergence_criteria_->setMaximumIterations(max_iterations_);
141  convergence_criteria_->setRelativeMSE(euclidean_fitness_epsilon_);
142  convergence_criteria_->setTranslationThreshold(transformation_epsilon_);
143  convergence_criteria_->setRotationThreshold(1.0 - transformation_epsilon_);
144 
145  // Repeat until convergence
146  std::vector<CorrespondencesPtr> partial_correspondences_(sources_.size());
147  for (std::size_t i = 0; i < sources_.size(); i++) {
148  partial_correspondences_[i].reset(new pcl::Correspondences);
149  }
150 
151  do {
152  // Save the previously estimated transformation
153  previous_transformation_ = transformation_;
154 
155  // Set the source each iteration, to ensure the dirty flag is updated
156  correspondences_->clear();
157  for (std::size_t i = 0; i < correspondence_estimations_.size(); i++) {
158  correspondence_estimations_[i]->setInputSource(inputs_transformed[i]);
159  // Get blob data if needed
160  if (correspondence_estimations_[i]->requiresSourceNormals()) {
161  PCLPointCloud2::Ptr input_transformed_blob(new PCLPointCloud2);
162  toPCLPointCloud2(*inputs_transformed[i], *input_transformed_blob);
163  correspondence_estimations_[i]->setSourceNormals(input_transformed_blob);
164  }
165 
166  // Estimate correspondences on each cloud pair separately
167  if (use_reciprocal_correspondence_) {
168  correspondence_estimations_[i]->determineReciprocalCorrespondences(
169  *partial_correspondences_[i], corr_dist_threshold_);
170  }
171  else {
172  correspondence_estimations_[i]->determineCorrespondences(
173  *partial_correspondences_[i], corr_dist_threshold_);
174  }
175  PCL_DEBUG("[pcl::%s::computeTransformation] Found %d partial correspondences for "
176  "cloud [%d]\n",
177  getClassName().c_str(),
178  partial_correspondences_[i]->size(),
179  i);
180  for (std::size_t j = 0; j < partial_correspondences_[i]->size(); j++) {
181  pcl::Correspondence corr = partial_correspondences_[i]->at(j);
182  // Update the offsets to be for the combined clouds
183  corr.index_query += input_offsets[i];
184  corr.index_match += target_offsets[i];
185  correspondences_->push_back(corr);
186  }
187  }
188  PCL_DEBUG("[pcl::%s::computeTransformation] Total correspondences: %d\n",
189  getClassName().c_str(),
190  correspondences_->size());
191 
192  PCLPointCloud2::Ptr inputs_transformed_combined_blob;
193  if (need_source_blob_) {
194  inputs_transformed_combined_blob.reset(new PCLPointCloud2);
195  toPCLPointCloud2(*inputs_transformed_combined, *inputs_transformed_combined_blob);
196  }
197  CorrespondencesPtr temp_correspondences(new Correspondences(*correspondences_));
198  for (std::size_t i = 0; i < correspondence_rejectors_.size(); ++i) {
199  PCL_DEBUG("Applying a correspondence rejector method: %s.\n",
200  correspondence_rejectors_[i]->getClassName().c_str());
201  registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i];
202  PCL_DEBUG("Applying a correspondence rejector method: %s.\n",
203  rej->getClassName().c_str());
204  if (rej->requiresSourcePoints())
205  rej->setSourcePoints(inputs_transformed_combined_blob);
206  if (rej->requiresSourceNormals() && source_has_normals_)
207  rej->setSourceNormals(inputs_transformed_combined_blob);
208  rej->setInputCorrespondences(temp_correspondences);
209  rej->getCorrespondences(*correspondences_);
210  // Modify input for the next iteration
211  if (i < correspondence_rejectors_.size() - 1)
212  *temp_correspondences = *correspondences_;
213  }
214 
215  int cnt = correspondences_->size();
216  // Check whether we have enough correspondences
217  if (cnt < min_number_correspondences_) {
218  PCL_ERROR("[pcl::%s::computeTransformation] Not enough correspondences found. "
219  "Relax your threshold parameters.\n",
220  getClassName().c_str());
221  convergence_criteria_->setConvergenceState(
223  Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
224  converged_ = false;
225  break;
226  }
227 
228  // Estimate the transform jointly, on a combined correspondence set
229  transformation_estimation_->estimateRigidTransformation(
230  *inputs_transformed_combined,
231  *targets_combined,
232  *correspondences_,
233  transformation_);
234 
235  // Transform the combined data
236  this->transformCloud(
237  *inputs_transformed_combined, *inputs_transformed_combined, transformation_);
238  // And all its components
239  for (std::size_t i = 0; i < sources_.size(); i++) {
240  this->transformCloud(
241  *inputs_transformed[i], *inputs_transformed[i], transformation_);
242  }
243 
244  // Obtain the final transformation
245  final_transformation_ = transformation_ * final_transformation_;
246 
247  ++nr_iterations_;
248 
249  // Update the vizualization of icp convergence
250  // if (update_visualizer_ != 0)
251  // update_visualizer_(output, source_indices_good, *target_, target_indices_good );
252 
253  converged_ = static_cast<bool>((*convergence_criteria_));
254  } while (!converged_);
255 
256  PCL_DEBUG("Transformation "
257  "is:\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n\t%"
258  "5f\t%5f\t%5f\t%5f\n",
259  final_transformation_(0, 0),
260  final_transformation_(0, 1),
261  final_transformation_(0, 2),
262  final_transformation_(0, 3),
263  final_transformation_(1, 0),
264  final_transformation_(1, 1),
265  final_transformation_(1, 2),
266  final_transformation_(1, 3),
267  final_transformation_(2, 0),
268  final_transformation_(2, 1),
269  final_transformation_(2, 2),
270  final_transformation_(2, 3),
271  final_transformation_(3, 0),
272  final_transformation_(3, 1),
273  final_transformation_(3, 2),
274  final_transformation_(3, 3));
275 
276  // For fitness checks, etc, we'll use an aggregated cloud for now (should be
277  // evaluating independently for correctness, but this requires propagating a few
278  // virtual methods from Registration)
280  sources_combined);
282  targets_combined);
283 
284  // If we automatically set the correspondence estimators, we should clear them now
285  if (!manual_correspondence_estimations_set) {
286  correspondence_estimations_.clear();
287  }
288 
289  // By definition, this method will return an empty cloud (for compliance with the ICP
290  // API). We can figure out a better solution, if necessary.
291  output = PointCloudSource();
292 }
293 
294 template <typename PointSource, typename PointTarget, typename Scalar>
295 void
298 {
299  need_source_blob_ = false;
300  need_target_blob_ = false;
301  // Check estimators
302  for (std::size_t i = 0; i < correspondence_estimations_.size(); i++) {
303  CorrespondenceEstimationPtr& ce = correspondence_estimations_[i];
304 
305  need_source_blob_ |= ce->requiresSourceNormals();
306  need_target_blob_ |= ce->requiresTargetNormals();
307  // Add warnings if necessary
308  if (ce->requiresSourceNormals() && !source_has_normals_) {
309  PCL_WARN("[pcl::%s::determineRequiredBlobData] Estimator expects source normals, "
310  "but we can't provide them.\n",
311  getClassName().c_str());
312  }
313  if (ce->requiresTargetNormals() && !target_has_normals_) {
314  PCL_WARN("[pcl::%s::determineRequiredBlobData] Estimator expects target normals, "
315  "but we can't provide them.\n",
316  getClassName().c_str());
317  }
318  }
319  // Check rejectors
320  for (std::size_t i = 0; i < correspondence_rejectors_.size(); i++) {
321  registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i];
322  need_source_blob_ |= rej->requiresSourcePoints();
323  need_source_blob_ |= rej->requiresSourceNormals();
324  need_target_blob_ |= rej->requiresTargetPoints();
325  need_target_blob_ |= rej->requiresTargetNormals();
326  if (rej->requiresSourceNormals() && !source_has_normals_) {
327  PCL_WARN("[pcl::%s::determineRequiredBlobData] Rejector %s expects source "
328  "normals, but we can't provide them.\n",
329  getClassName().c_str(),
330  rej->getClassName().c_str());
331  }
332  if (rej->requiresTargetNormals() && !target_has_normals_) {
333  PCL_WARN("[pcl::%s::determineRequiredBlobData] Rejector %s expects target "
334  "normals, but we can't provide them.\n",
335  getClassName().c_str(),
336  rej->getClassName().c_str());
337  }
338  }
339 }
340 
341 } // namespace pcl
342 
343 #endif /* PCL_REGISTRATION_IMPL_JOINT_ICP_HPP_ */
pcl::registration::CorrespondenceRejector::Ptr
shared_ptr< CorrespondenceRejector > Ptr
Definition: correspondence_rejection.h:56
pcl
Definition: convolution.h:46
pcl::registration::DefaultConvergenceCriteria
DefaultConvergenceCriteria represents an instantiation of ConvergenceCriteria, and implements the fol...
Definition: default_convergence_criteria.h:65
pcl::PCLPointCloud2::Ptr
shared_ptr< ::pcl::PCLPointCloud2 > Ptr
Definition: PCLPointCloud2.h:35
pcl::IterativeClosestPoint::setInputSource
void setInputSource(const PointCloudSourceConstPtr &cloud) override
Provide a pointer to the input source (e.g., the point cloud that we want to align to the target)
Definition: icp.h:206
pcl::JointIterativeClosestPoint::KdTreeReciprocalPtr
typename KdTree::Ptr KdTreeReciprocalPtr
Definition: joint_icp.h:72
pcl::IterativeClosestPoint::setInputTarget
void setInputTarget(const PointCloudTargetConstPtr &cloud) override
Provide a pointer to the input target (e.g., the point cloud that we want to align to the target)
Definition: icp.h:239
pcl::JointIterativeClosestPoint::PointCloudTarget
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::PointCloudTarget PointCloudTarget
Definition: joint_icp.h:64
pcl::JointIterativeClosestPoint::computeTransformation
void computeTransformation(PointCloudSource &output, const Matrix4 &guess) override
Rigid transformation computation method with initial guess.
Definition: joint_icp.hpp:50
pcl::JointIterativeClosestPoint::determineRequiredBlobData
void determineRequiredBlobData() override
Looks at the Estimators and Rejectors and determines whether their blob-setter methods need to be cal...
Definition: joint_icp.hpp:297
pcl::search::KdTree
search::KdTree is a wrapper class which inherits the pcl::KdTree class for performing search function...
Definition: kdtree.h:61
pcl::JointIterativeClosestPoint::PointCloudSource
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::PointCloudSource PointCloudSource
Definition: joint_icp.h:58
pcl::PCLPointCloud2
Definition: PCLPointCloud2.h:16
pcl::JointIterativeClosestPoint::PointCloudSourcePtr
typename PointCloudSource::Ptr PointCloudSourcePtr
Definition: joint_icp.h:59
pcl::Correspondence::index_match
index_t index_match
Index of the matching (target) point.
Definition: correspondence.h:65
pcl::JointIterativeClosestPoint::PointCloudTargetPtr
typename PointCloudTarget::Ptr PointCloudTargetPtr
Definition: joint_icp.h:65
pcl::toPCLPointCloud2
void toPCLPointCloud2(const pcl::PointCloud< PointT > &cloud, pcl::PCLPointCloud2 &msg)
Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
Definition: conversions.h:240
pcl::Correspondences
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
Definition: correspondence.h:89
pcl::Correspondence::index_query
index_t index_query
Index of the query (source) point.
Definition: correspondence.h:63
pcl::CorrespondencesPtr
shared_ptr< Correspondences > CorrespondencesPtr
Definition: correspondence.h:90
pcl::Correspondence
Correspondence represents a match between two entities (e.g., points, descriptors,...
Definition: correspondence.h:60
pcl::JointIterativeClosestPoint::KdTreePtr
typename KdTree::Ptr KdTreePtr
Definition: joint_icp.h:69
pcl::JointIterativeClosestPoint::CorrespondenceEstimationPtr
typename CorrespondenceEstimation::Ptr CorrespondenceEstimationPtr
Definition: joint_icp.h:83
pcl::JointIterativeClosestPoint::Matrix4
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::Matrix4 Matrix4
Definition: joint_icp.h:126