Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
sample_consensus_prerejective.hpp
1/*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2010-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 * $Id$
38 *
39 */
40
41#ifndef PCL_REGISTRATION_SAMPLE_CONSENSUS_PREREJECTIVE_HPP_
42#define PCL_REGISTRATION_SAMPLE_CONSENSUS_PREREJECTIVE_HPP_
43
44#include <pcl/search/auto.h> // for autoSelectMethod
45
46namespace pcl {
47
48template <typename PointSource, typename PointTarget, typename FeatureT>
49void
51 const FeatureCloudConstPtr& features)
52{
53 if (features == nullptr || features->empty()) {
54 PCL_ERROR(
55 "[pcl::%s::setSourceFeatures] Invalid or empty point cloud dataset given!\n",
56 getClassName().c_str());
57 return;
58 }
59 input_features_ = features;
60}
61
62template <typename PointSource, typename PointTarget, typename FeatureT>
63void
65 const FeatureCloudConstPtr& features)
66{
67 if (features == nullptr || features->empty()) {
68 PCL_ERROR(
69 "[pcl::%s::setTargetFeatures] Invalid or empty point cloud dataset given!\n",
70 getClassName().c_str());
71 return;
72 }
73 target_features_ = features;
74 feature_tree_.reset(pcl::search::autoSelectMethod<FeatureT>(
75 target_features_, false, pcl::search::Purpose::many_knn_search));
76}
77
78template <typename PointSource, typename PointTarget, typename FeatureT>
79void
81 const PointCloudSource& cloud, int nr_samples, pcl::Indices& sample_indices)
82{
83 if (nr_samples > static_cast<int>(cloud.size())) {
84 PCL_ERROR("[pcl::%s::selectSamples] ", getClassName().c_str());
85 PCL_ERROR("The number of samples (%d) must not be greater than the number of "
86 "points (%zu)!\n",
87 nr_samples,
88 static_cast<std::size_t>(cloud.size()));
89 return;
90 }
91
92 sample_indices.resize(nr_samples);
93 int temp_sample;
94
95 // Draw random samples until n samples is reached
96 for (int i = 0; i < nr_samples; i++) {
97 // Select a random number
98 sample_indices[i] = getRandomIndex(static_cast<int>(cloud.size()) - i);
99
100 // Run through list of numbers, starting at the lowest, to avoid duplicates
101 for (int j = 0; j < i; j++) {
102 // Move value up if it is higher than previous selections to ensure true
103 // randomness
104 if (sample_indices[i] >= sample_indices[j]) {
105 sample_indices[i]++;
106 }
107 else {
108 // The new number is lower, place it at the correct point and break for a sorted
109 // list
110 temp_sample = sample_indices[i];
111 for (int k = i; k > j; k--)
112 sample_indices[k] = sample_indices[k - 1];
113
114 sample_indices[j] = temp_sample;
115 break;
116 }
117 }
118 }
119}
120
121template <typename PointSource, typename PointTarget, typename FeatureT>
122void
124 const pcl::Indices& sample_indices,
125 std::vector<pcl::Indices>& similar_features,
126 pcl::Indices& corresponding_indices)
127{
128 // Allocate results
129 corresponding_indices.resize(sample_indices.size());
130 std::vector<float> nn_distances(k_correspondences_);
131
132 // Loop over the sampled features
133 for (std::size_t i = 0; i < sample_indices.size(); ++i) {
134 // Current feature index
135 const auto& idx = sample_indices[i];
136
137 // Find the k nearest feature neighbors to the sampled input feature if they are not
138 // in the cache already
139 if (similar_features[idx].empty())
140 feature_tree_->nearestKSearch(*input_features_,
141 idx,
142 k_correspondences_,
143 similar_features[idx],
144 nn_distances);
145
146 // Select one at random and add it to corresponding_indices
147 if (k_correspondences_ == 1)
148 corresponding_indices[i] = similar_features[idx][0];
149 else
150 corresponding_indices[i] =
151 similar_features[idx][getRandomIndex(k_correspondences_)];
152 }
153}
154
155template <typename PointSource, typename PointTarget, typename FeatureT>
156void
158 PointCloudSource& output, const Eigen::Matrix4f& guess)
159{
160 // Some sanity checks first
161 if (!input_features_) {
162 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
163 PCL_ERROR(
164 "No source features were given! Call setSourceFeatures before aligning.\n");
165 return;
166 }
167 if (!target_features_) {
168 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
169 PCL_ERROR(
170 "No target features were given! Call setTargetFeatures before aligning.\n");
171 return;
172 }
173
174 if (input_->size() != input_features_->size()) {
175 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
176 PCL_ERROR("The source points and source feature points need to be in a one-to-one "
177 "relationship! Current input cloud sizes: %ld vs %ld.\n",
178 input_->size(),
179 input_features_->size());
180 return;
181 }
182
183 if (target_->size() != target_features_->size()) {
184 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
185 PCL_ERROR("The target points and target feature points need to be in a one-to-one "
186 "relationship! Current input cloud sizes: %ld vs %ld.\n",
187 target_->size(),
188 target_features_->size());
189 return;
190 }
191
192 if (inlier_fraction_ < 0.0f || inlier_fraction_ > 1.0f) {
193 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
194 PCL_ERROR("Illegal inlier fraction %f, must be in [0,1]!\n", inlier_fraction_);
195 return;
196 }
197
198 const float similarity_threshold =
199 correspondence_rejector_poly_->getSimilarityThreshold();
200 if (similarity_threshold < 0.0f || similarity_threshold >= 1.0f) {
201 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
202 PCL_ERROR("Illegal prerejection similarity threshold %f, must be in [0,1[!\n",
203 similarity_threshold);
204 return;
205 }
206
207 if (k_correspondences_ <= 0) {
208 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
209 PCL_ERROR("Illegal correspondence randomness %d, must be > 0!\n",
210 k_correspondences_);
211 return;
212 }
213
214 // Initialize prerejector (similarity threshold already set to default value in
215 // constructor)
216 correspondence_rejector_poly_->setInputSource(input_);
217 correspondence_rejector_poly_->setInputTarget(target_);
218 correspondence_rejector_poly_->setCardinality(nr_samples_);
219 int num_rejections = 0; // For debugging
220
221 // Initialize results
222 final_transformation_ = guess;
223 inliers_.clear();
224 float lowest_error = std::numeric_limits<float>::max();
225 converged_ = false;
226
227 // Temporaries
228 pcl::Indices inliers;
229 float inlier_fraction;
230 float error;
231
232 // If guess is not the Identity matrix we check it
233 if (!guess.isApprox(Eigen::Matrix4f::Identity(), 0.01f)) {
234 getFitness(inliers, error);
235 inlier_fraction =
236 static_cast<float>(inliers.size()) / static_cast<float>(input_->size());
237
238 if (inlier_fraction >= inlier_fraction_ && error < lowest_error) {
239 inliers_ = inliers;
240 lowest_error = error;
241 converged_ = true;
242 }
243 }
244
245 // Feature correspondence cache
246 std::vector<pcl::Indices> similar_features(input_->size());
247
248 // Start
249 for (int i = 0; i < max_iterations_; ++i) {
250 // Temporary containers
251 pcl::Indices sample_indices;
252 pcl::Indices corresponding_indices;
253
254 // Draw nr_samples_ random samples
255 selectSamples(*input_, nr_samples_, sample_indices);
256
257 // Find corresponding features in the target cloud
258 findSimilarFeatures(sample_indices, similar_features, corresponding_indices);
259
260 // Apply prerejection
261 if (!correspondence_rejector_poly_->thresholdPolygon(sample_indices,
262 corresponding_indices)) {
263 ++num_rejections;
264 continue;
265 }
266
267 // Estimate the transform from the correspondences, write to transformation_
268 transformation_estimation_->estimateRigidTransformation(
269 *input_, sample_indices, *target_, corresponding_indices, transformation_);
270
271 // Take a backup of previous result
272 const Matrix4 final_transformation_prev = final_transformation_;
273
274 // Set final result to current transformation
275 final_transformation_ = transformation_;
276
277 // Transform the input and compute the error (uses input_ and final_transformation_)
278 getFitness(inliers, error);
279
280 // Restore previous result
281 final_transformation_ = final_transformation_prev;
282
283 // If the new fit is better, update results
284 inlier_fraction =
285 static_cast<float>(inliers.size()) / static_cast<float>(input_->size());
286
287 // Update result if pose hypothesis is better
288 if (inlier_fraction >= inlier_fraction_ && error < lowest_error) {
289 inliers_ = inliers;
290 lowest_error = error;
291 converged_ = true;
292 final_transformation_ = transformation_;
293 }
294 }
295
296 // Apply the final transformation
297 if (converged_)
298 transformPointCloud(*input_, output, final_transformation_);
299
300 // Debug output
301 PCL_DEBUG("[pcl::%s::computeTransformation] Rejected %i out of %i generated pose "
302 "hypotheses.\n",
303 getClassName().c_str(),
304 num_rejections,
305 max_iterations_);
306}
307
308template <typename PointSource, typename PointTarget, typename FeatureT>
309void
311 pcl::Indices& inliers, float& fitness_score)
312{
313 // Initialize variables
314 inliers.clear();
315 inliers.reserve(input_->size());
316 fitness_score = 0.0f;
317
318 // Use squared distance for comparison with NN search results
319 const float max_range = corr_dist_threshold_ * corr_dist_threshold_;
320
321 // Transform the input dataset using the final transformation
322 PointCloudSource input_transformed;
323 input_transformed.resize(input_->size());
324 transformPointCloud(*input_, input_transformed, final_transformation_);
325
326 // For each point in the source dataset
327 for (std::size_t i = 0; i < input_transformed.size(); ++i) {
328 // Find its nearest neighbor in the target
329 pcl::Indices nn_indices(1);
330 std::vector<float> nn_dists(1);
331 tree_->nearestKSearch(input_transformed[i], 1, nn_indices, nn_dists);
332
333 // Check if point is an inlier
334 if (nn_dists[0] < max_range) {
335 // Update inliers
336 inliers.push_back(i);
337
338 // Update fitness score
339 fitness_score += nn_dists[0];
340 }
341 }
342
343 // Calculate MSE
344 if (!inliers.empty())
345 fitness_score /= static_cast<float>(inliers.size());
346 else
347 fitness_score = std::numeric_limits<float>::max();
348}
349
350} // namespace pcl
351
352#endif
void setTargetFeatures(const FeatureCloudConstPtr &features)
Provide a boost shared pointer to the target point cloud's feature descriptors.
void computeTransformation(PointCloudSource &output, const Eigen::Matrix4f &guess) override
Rigid transformation computation method.
typename Registration< PointSource, PointTarget >::PointCloudSource PointCloudSource
typename FeatureCloud::ConstPtr FeatureCloudConstPtr
void findSimilarFeatures(const pcl::Indices &sample_indices, std::vector< pcl::Indices > &similar_features, pcl::Indices &corresponding_indices)
For each of the sample points, find a list of points in the target cloud whose features are similar t...
void setSourceFeatures(const FeatureCloudConstPtr &features)
Provide a boost shared pointer to the source point cloud's feature descriptors.
void selectSamples(const PointCloudSource &cloud, int nr_samples, pcl::Indices &sample_indices)
Select nr_samples sample points from cloud while making sure that their pairwise distances are greate...
typename Registration< PointSource, PointTarget >::Matrix4 Matrix4
void getFitness(pcl::Indices &inliers, float &fitness_score)
Obtain the fitness of a transformation The following metrics are calculated, based on final_transform...
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.
@ many_knn_search
The search method will mainly be used for nearestKSearch where k is larger than 1.
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133