Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
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/correspondence.h>
44
45namespace pcl {
46
47template <typename PointSource, typename PointTarget, typename Scalar>
48void
50 PointCloudSource& output, const Matrix4& guess)
51{
52 // Point clouds containing the correspondences of each point in <input, indices>
53 if (sources_.size() != targets_.size() || sources_.empty() || targets_.empty()) {
54 PCL_ERROR("[pcl::%s::computeTransformation] Must set InputSources and InputTargets "
55 "to the same, nonzero size!\n",
56 getClassName().c_str());
57 return;
58 }
59 bool manual_correspondence_estimations_set = true;
60 if (correspondence_estimations_.empty()) {
61 manual_correspondence_estimations_set = false;
62 correspondence_estimations_.resize(sources_.size());
63 for (std::size_t i = 0; i < sources_.size(); i++) {
64 correspondence_estimations_[i] = correspondence_estimation_->clone();
65 }
66 }
67 if (correspondence_estimations_.size() != sources_.size()) {
68 PCL_ERROR("[pcl::%s::computeTransform] Must set CorrespondenceEstimations to be "
69 "the same size as the joint\n",
70 getClassName().c_str());
71 return;
72 }
73 std::vector<PointCloudSourcePtr> inputs_transformed(sources_.size());
74 for (std::size_t i = 0; i < sources_.size(); i++) {
75 inputs_transformed[i].reset(new PointCloudSource);
76 }
77
78 nr_iterations_ = 0;
79 converged_ = false;
80
81 // Initialise final transformation to the guessed one
82 final_transformation_ = guess;
83
84 // Make a combined transformed input and output
85 std::vector<std::size_t> input_offsets(sources_.size());
86 std::vector<std::size_t> target_offsets(targets_.size());
87 PointCloudSourcePtr sources_combined(new PointCloudSource);
88 PointCloudSourcePtr inputs_transformed_combined(new PointCloudSource);
89 PointCloudTargetPtr targets_combined(new PointCloudTarget);
90 std::size_t input_offset = 0;
91 std::size_t target_offset = 0;
92 for (std::size_t i = 0; i < sources_.size(); i++) {
93 // If the guessed transformation is non identity
94 if (guess != Matrix4::Identity()) {
95 // Apply guessed transformation prior to search for neighbours
96 this->transformCloud(*sources_[i], *inputs_transformed[i], guess);
97 }
98 else {
99 *inputs_transformed[i] = *sources_[i];
100 }
101 *sources_combined += *sources_[i];
102 *inputs_transformed_combined += *inputs_transformed[i];
103 *targets_combined += *targets_[i];
104 input_offsets[i] = input_offset;
105 target_offsets[i] = target_offset;
106 input_offset += inputs_transformed[i]->size();
107 target_offset += targets_[i]->size();
108 }
109
110 transformation_ = Matrix4::Identity();
111 // Make blobs if necessary
112 determineRequiredBlobData();
113 // Pass in the default target for the Correspondence Estimation/Rejection code
114 for (std::size_t i = 0; i < sources_.size(); i++) {
115 correspondence_estimations_[i]->setInputTarget(targets_[i]);
116 if (correspondence_estimations_[i]->requiresTargetNormals()) {
117 PCLPointCloud2::Ptr target_blob(new PCLPointCloud2);
118 pcl::toPCLPointCloud2(*targets_[i], *target_blob);
119 correspondence_estimations_[i]->setTargetNormals(target_blob);
120 }
121 }
122
123 PCLPointCloud2::Ptr targets_combined_blob(new PCLPointCloud2);
124 if (!correspondence_rejectors_.empty() && need_target_blob_)
125 pcl::toPCLPointCloud2(*targets_combined, *targets_combined_blob);
126
127 for (std::size_t i = 0; i < correspondence_rejectors_.size(); ++i) {
128 registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i];
129 if (rej->requiresTargetPoints())
130 rej->setTargetPoints(targets_combined_blob);
131 if (rej->requiresTargetNormals() && target_has_normals_)
132 rej->setTargetNormals(targets_combined_blob);
133 }
134
135 convergence_criteria_->setMaximumIterations(max_iterations_);
136 convergence_criteria_->setRelativeMSE(euclidean_fitness_epsilon_);
137 convergence_criteria_->setTranslationThreshold(transformation_epsilon_);
138 convergence_criteria_->setRotationThreshold(1.0 - transformation_epsilon_);
139
140 // Repeat until convergence
141 std::vector<CorrespondencesPtr> partial_correspondences_(sources_.size());
142 for (std::size_t i = 0; i < sources_.size(); i++) {
143 partial_correspondences_[i].reset(new pcl::Correspondences);
144 }
145
146 do {
147 // Save the previously estimated transformation
148 previous_transformation_ = transformation_;
149
150 // Set the source each iteration, to ensure the dirty flag is updated
151 correspondences_->clear();
152 for (std::size_t i = 0; i < correspondence_estimations_.size(); i++) {
153 correspondence_estimations_[i]->setInputSource(inputs_transformed[i]);
154 // Get blob data if needed
155 if (correspondence_estimations_[i]->requiresSourceNormals()) {
156 PCLPointCloud2::Ptr input_transformed_blob(new PCLPointCloud2);
157 toPCLPointCloud2(*inputs_transformed[i], *input_transformed_blob);
158 correspondence_estimations_[i]->setSourceNormals(input_transformed_blob);
159 }
160
161 // Estimate correspondences on each cloud pair separately
162 if (use_reciprocal_correspondence_) {
163 correspondence_estimations_[i]->determineReciprocalCorrespondences(
164 *partial_correspondences_[i], corr_dist_threshold_);
165 }
166 else {
167 correspondence_estimations_[i]->determineCorrespondences(
168 *partial_correspondences_[i], corr_dist_threshold_);
169 }
170 PCL_DEBUG("[pcl::%s::computeTransformation] Found %d partial correspondences for "
171 "cloud [%d]\n",
172 getClassName().c_str(),
173 partial_correspondences_[i]->size(),
174 i);
175 for (std::size_t j = 0; j < partial_correspondences_[i]->size(); j++) {
176 pcl::Correspondence corr = partial_correspondences_[i]->at(j);
177 // Update the offsets to be for the combined clouds
178 corr.index_query += input_offsets[i];
179 corr.index_match += target_offsets[i];
180 correspondences_->push_back(corr);
181 }
182 }
183 PCL_DEBUG("[pcl::%s::computeTransformation] Total correspondences: %d\n",
184 getClassName().c_str(),
185 correspondences_->size());
186
187 PCLPointCloud2::Ptr inputs_transformed_combined_blob;
188 if (need_source_blob_) {
189 inputs_transformed_combined_blob.reset(new PCLPointCloud2);
190 toPCLPointCloud2(*inputs_transformed_combined, *inputs_transformed_combined_blob);
191 }
192 CorrespondencesPtr temp_correspondences(new Correspondences(*correspondences_));
193 for (std::size_t i = 0; i < correspondence_rejectors_.size(); ++i) {
194 PCL_DEBUG("Applying a correspondence rejector method: %s.\n",
195 correspondence_rejectors_[i]->getClassName().c_str());
196 registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i];
197 PCL_DEBUG("Applying a correspondence rejector method: %s.\n",
198 rej->getClassName().c_str());
199 if (rej->requiresSourcePoints())
200 rej->setSourcePoints(inputs_transformed_combined_blob);
201 if (rej->requiresSourceNormals() && source_has_normals_)
202 rej->setSourceNormals(inputs_transformed_combined_blob);
203 rej->setInputCorrespondences(temp_correspondences);
204 rej->getCorrespondences(*correspondences_);
205 // Modify input for the next iteration
206 if (i < correspondence_rejectors_.size() - 1)
207 *temp_correspondences = *correspondences_;
208 }
209
210 // Check whether we have enough correspondences
211 if (correspondences_->size() < min_number_correspondences_) {
212 PCL_ERROR("[pcl::%s::computeTransformation] Not enough correspondences found. "
213 "Relax your threshold parameters.\n",
214 getClassName().c_str());
215 convergence_criteria_->setConvergenceState(
217 Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);
218 converged_ = false;
219 break;
220 }
221
222 // Estimate the transform jointly, on a combined correspondence set
223 transformation_estimation_->estimateRigidTransformation(
224 *inputs_transformed_combined,
225 *targets_combined,
226 *correspondences_,
227 transformation_);
228
229 // Transform the combined data
230 this->transformCloud(
231 *inputs_transformed_combined, *inputs_transformed_combined, transformation_);
232 // And all its components
233 for (std::size_t i = 0; i < sources_.size(); i++) {
234 this->transformCloud(
235 *inputs_transformed[i], *inputs_transformed[i], transformation_);
236 }
237
238 // Obtain the final transformation
239 final_transformation_ = transformation_ * final_transformation_;
240
241 ++nr_iterations_;
242
243 // Update the visualization of icp convergence
244 // if (update_visualizer_ != 0)
245 // update_visualizer_(output, source_indices_good, *target_, target_indices_good );
246
247 converged_ = static_cast<bool>((*convergence_criteria_));
248 } while (!converged_);
249
250 PCL_DEBUG("Transformation "
251 "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%"
252 "5f\t%5f\t%5f\t%5f\n",
253 final_transformation_(0, 0),
254 final_transformation_(0, 1),
255 final_transformation_(0, 2),
256 final_transformation_(0, 3),
257 final_transformation_(1, 0),
258 final_transformation_(1, 1),
259 final_transformation_(1, 2),
260 final_transformation_(1, 3),
261 final_transformation_(2, 0),
262 final_transformation_(2, 1),
263 final_transformation_(2, 2),
264 final_transformation_(2, 3),
265 final_transformation_(3, 0),
266 final_transformation_(3, 1),
267 final_transformation_(3, 2),
268 final_transformation_(3, 3));
269
270 // For fitness checks, etc, we'll use an aggregated cloud for now (should be
271 // evaluating independently for correctness, but this requires propagating a few
272 // virtual methods from Registration)
274 sources_combined);
276 targets_combined);
277
278 // If we automatically set the correspondence estimators, we should clear them now
279 if (!manual_correspondence_estimations_set) {
280 correspondence_estimations_.clear();
281 }
282
283 // By definition, this method will return an empty cloud (for compliance with the ICP
284 // API). We can figure out a better solution, if necessary.
285 output = PointCloudSource();
286}
287
288template <typename PointSource, typename PointTarget, typename Scalar>
289void
292{
293 need_source_blob_ = false;
294 need_target_blob_ = false;
295 // Check estimators
296 for (std::size_t i = 0; i < correspondence_estimations_.size(); i++) {
297 CorrespondenceEstimationPtr& ce = correspondence_estimations_[i];
298
299 need_source_blob_ |= ce->requiresSourceNormals();
300 need_target_blob_ |= ce->requiresTargetNormals();
301 // Add warnings if necessary
302 if (ce->requiresSourceNormals() && !source_has_normals_) {
303 PCL_WARN("[pcl::%s::determineRequiredBlobData] Estimator expects source normals, "
304 "but we can't provide them.\n",
305 getClassName().c_str());
306 }
307 if (ce->requiresTargetNormals() && !target_has_normals_) {
308 PCL_WARN("[pcl::%s::determineRequiredBlobData] Estimator expects target normals, "
309 "but we can't provide them.\n",
310 getClassName().c_str());
311 }
312 }
313 // Check rejectors
314 for (std::size_t i = 0; i < correspondence_rejectors_.size(); i++) {
315 registration::CorrespondenceRejector::Ptr& rej = correspondence_rejectors_[i];
316 need_source_blob_ |= rej->requiresSourcePoints();
317 need_source_blob_ |= rej->requiresSourceNormals();
318 need_target_blob_ |= rej->requiresTargetPoints();
319 need_target_blob_ |= rej->requiresTargetNormals();
320 if (rej->requiresSourceNormals() && !source_has_normals_) {
321 PCL_WARN("[pcl::%s::determineRequiredBlobData] Rejector %s expects source "
322 "normals, but we can't provide them.\n",
323 getClassName().c_str(),
324 rej->getClassName().c_str());
325 }
326 if (rej->requiresTargetNormals() && !target_has_normals_) {
327 PCL_WARN("[pcl::%s::determineRequiredBlobData] Rejector %s expects target "
328 "normals, but we can't provide them.\n",
329 getClassName().c_str(),
330 rej->getClassName().c_str());
331 }
332 }
333}
334
335} // namespace pcl
336
337#endif /* PCL_REGISTRATION_IMPL_JOINT_ICP_HPP_ */
void setInputTarget(const PointCloudTargetConstPtr &cloud) override
Provide a pointer to the input target (e.g., the point cloud that we want to align the input source t...
Definition icp.h:232
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:199
typename PointCloudSource::Ptr PointCloudSourcePtr
Definition joint_icp.h:59
void determineRequiredBlobData() override
Looks at the Estimators and Rejectors and determines whether their blob-setter methods need to be cal...
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::PointCloudTarget PointCloudTarget
Definition joint_icp.h:64
void computeTransformation(PointCloudSource &output, const Matrix4 &guess) override
Rigid transformation computation method with initial guess.
Definition joint_icp.hpp:49
typename PointCloudTarget::Ptr PointCloudTargetPtr
Definition joint_icp.h:65
typename CorrespondenceEstimation::Ptr CorrespondenceEstimationPtr
Definition joint_icp.h:83
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::PointCloudSource PointCloudSource
Definition joint_icp.h:58
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::Matrix4 Matrix4
Definition joint_icp.h:126
shared_ptr< CorrespondenceRejector > Ptr
DefaultConvergenceCriteria represents an instantiation of ConvergenceCriteria, and implements the fol...
shared_ptr< Correspondences > CorrespondencesPtr
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
void toPCLPointCloud2(const pcl::PointCloud< PointT > &cloud, pcl::PCLPointCloud2 &msg, bool padding)
Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
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.
shared_ptr< ::pcl::PCLPointCloud2 > Ptr