Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
ia_ransac.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 IA_RANSAC_HPP_
42#define IA_RANSAC_HPP_
43
45#include <pcl/search/auto.h> // for autoSelectMethod
46
47namespace pcl {
48
49template <typename PointSource, typename PointTarget, typename FeatureT>
50void
52 unsigned int nr_threads)
53{
54#ifdef _OPENMP
55 if (nr_threads == 0)
56 threads_ = omp_get_num_procs();
57 else
58 threads_ = nr_threads;
59 PCL_DEBUG("[pcl::%s::setNumberOfThreads] Setting number of threads to %u.\n",
60 getClassName().c_str(),
61 threads_);
62#else
63 threads_ = 1;
64 if (nr_threads != 1)
65 PCL_WARN("[pcl::%s::setNumberOfThreads] Parallelization is requested, but OpenMP "
66 "is not available! Continuing without parallelization.\n",
67 getClassName().c_str());
68#endif // _OPENMP
69}
70
71template <typename PointSource, typename PointTarget, typename FeatureT>
72void
74 const FeatureCloudConstPtr& features)
75{
76 if (features == nullptr || features->empty()) {
77 PCL_ERROR(
78 "[pcl::%s::setSourceFeatures] Invalid or empty point cloud dataset given!\n",
79 getClassName().c_str());
80 return;
81 }
82 input_features_ = features;
83}
84
85template <typename PointSource, typename PointTarget, typename FeatureT>
86void
88 const FeatureCloudConstPtr& features)
89{
90 if (features == nullptr || features->empty()) {
91 PCL_ERROR(
92 "[pcl::%s::setTargetFeatures] Invalid or empty point cloud dataset given!\n",
93 getClassName().c_str());
94 return;
95 }
96 target_features_ = features;
97 feature_tree_.reset(pcl::search::autoSelectMethod<FeatureT>(
98 target_features_, false, pcl::search::Purpose::many_knn_search));
99}
100
101template <typename PointSource, typename PointTarget, typename FeatureT>
102void
104 const PointCloudSource& cloud,
105 unsigned int nr_samples,
106 float min_sample_distance,
107 pcl::Indices& sample_indices)
108{
109 if (nr_samples > cloud.size()) {
110 PCL_ERROR("[pcl::%s::selectSamples] ", getClassName().c_str());
111 PCL_ERROR("The number of samples (%u) must not be greater than the number of "
112 "points (%zu)!\n",
113 nr_samples,
114 static_cast<std::size_t>(cloud.size()));
115 return;
116 }
117
118 // Iteratively draw random samples until nr_samples is reached
119 index_t iterations_without_a_sample = 0;
120 const auto max_iterations_without_a_sample = 3 * cloud.size();
121 sample_indices.clear();
122 while (sample_indices.size() < nr_samples) {
123 // Choose a sample at random
124 const auto sample_index = getRandomIndex(cloud.size());
125
126 // Check to see if the sample is 1) unique and 2) far away from the other samples
127 bool valid_sample = true;
128 for (const auto& sample_idx : sample_indices) {
129 float distance_between_samples =
130 euclideanDistance(cloud[sample_index], cloud[sample_idx]);
131
132 if (sample_index == sample_idx ||
133 distance_between_samples < min_sample_distance) {
134 valid_sample = false;
135 break;
136 }
137 }
138
139 // If the sample is valid, add it to the output
140 if (valid_sample) {
141 sample_indices.push_back(sample_index);
142 iterations_without_a_sample = 0;
143 }
144 else
145 ++iterations_without_a_sample;
146
147 // If no valid samples can be found, relax the inter-sample distance requirements
148 if (static_cast<std::size_t>(iterations_without_a_sample) >=
149 max_iterations_without_a_sample) {
150 PCL_WARN("[pcl::%s::selectSamples] ", getClassName().c_str());
151 PCL_WARN("No valid sample found after %zu iterations. Relaxing "
152 "min_sample_distance_ to %f\n",
153 static_cast<std::size_t>(iterations_without_a_sample),
154 0.5 * min_sample_distance);
155
156 min_sample_distance_ *= 0.5f;
157 min_sample_distance = min_sample_distance_;
158 iterations_without_a_sample = 0;
159 }
160 }
161}
162
163template <typename PointSource, typename PointTarget, typename FeatureT>
164void
166 findSimilarFeatures(const FeatureCloud& input_features,
167 const pcl::Indices& sample_indices,
168 pcl::Indices& corresponding_indices)
169{
170 pcl::Indices nn_indices(k_correspondences_);
171 std::vector<float> nn_distances(k_correspondences_);
172
173 corresponding_indices.resize(sample_indices.size());
174 for (std::size_t i = 0; i < sample_indices.size(); ++i) {
175 // Find the k features nearest to input_features[sample_indices[i]]
176 feature_tree_->nearestKSearch(input_features,
177 sample_indices[i],
178 k_correspondences_,
179 nn_indices,
180 nn_distances);
181
182 // Select one at random and add it to corresponding_indices
183 const auto random_correspondence = getRandomIndex(k_correspondences_);
184 corresponding_indices[i] = nn_indices[random_correspondence];
185 }
186}
187
188template <typename PointSource, typename PointTarget, typename FeatureT>
189float
191 const PointCloudSource& cloud, float)
192{
193 pcl::Indices nn_index(1);
194 std::vector<float> nn_distance(1);
195
196 const ErrorFunctor& compute_error = *error_functor_;
197 const auto& tree = tree_;
198 float error = 0;
199
200#pragma omp parallel for default(none) shared(cloud, tree, compute_error) \
201 firstprivate(nn_index, nn_distance) reduction(+ : error) num_threads(threads_)
202 for (std::ptrdiff_t i = 0; i < static_cast<std::ptrdiff_t>(cloud.size()); ++i) {
203 const auto& point = cloud[static_cast<std::size_t>(i)];
204 // Find the distance between point and its nearest neighbor in the target point
205 // cloud
206 tree->nearestKSearch(point, 1, nn_index, nn_distance);
207
208 // Compute the error
209 error += compute_error(nn_distance[0]);
210 }
211 return (error);
212}
213
214template <typename PointSource, typename PointTarget, typename FeatureT>
215void
217 computeTransformation(PointCloudSource& output, const Eigen::Matrix4f& guess)
218{
219 // Some sanity checks first
220 if (!input_features_) {
221 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
222 PCL_ERROR(
223 "No source features were given! Call setSourceFeatures before aligning.\n");
224 return;
225 }
226 if (!target_features_) {
227 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
228 PCL_ERROR(
229 "No target features were given! Call setTargetFeatures before aligning.\n");
230 return;
231 }
232
233 if (input_->size() != input_features_->size()) {
234 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
235 PCL_ERROR("The source points and source feature points need to be in a one-to-one "
236 "relationship! Current input cloud sizes: %ld vs %ld.\n",
237 input_->size(),
238 input_features_->size());
239 return;
240 }
241
242 if (target_->size() != target_features_->size()) {
243 PCL_ERROR("[pcl::%s::computeTransformation] ", getClassName().c_str());
244 PCL_ERROR("The target points and target feature points need to be in a one-to-one "
245 "relationship! Current input cloud sizes: %ld vs %ld.\n",
246 target_->size(),
247 target_features_->size());
248 return;
249 }
250
251 if (!error_functor_)
252 error_functor_.reset(new TruncatedError(static_cast<float>(corr_dist_threshold_)));
253
254 pcl::Indices sample_indices(nr_samples_);
255 pcl::Indices corresponding_indices(nr_samples_);
256 PointCloudSource input_transformed;
257 float lowest_error(0);
258
259 final_transformation_ = guess;
260 int i_iter = 0;
261 converged_ = false;
262 if (!guess.isApprox(Eigen::Matrix4f::Identity(), 0.01f)) {
263 // If guess is not the Identity matrix we check it.
264 transformPointCloud(*input_, input_transformed, final_transformation_);
265 lowest_error =
266 computeErrorMetric(input_transformed, static_cast<float>(corr_dist_threshold_));
267 i_iter = 1;
268 }
269
270 for (; i_iter < max_iterations_; ++i_iter) {
271 // Draw nr_samples_ random samples
272 selectSamples(*input_, nr_samples_, min_sample_distance_, sample_indices);
273
274 // Find corresponding features in the target cloud
275 findSimilarFeatures(*input_features_, sample_indices, corresponding_indices);
276
277 // Estimate the transform from the samples to their corresponding points
278 transformation_estimation_->estimateRigidTransformation(
279 *input_, sample_indices, *target_, corresponding_indices, transformation_);
280
281 // Transform the data and compute the error
282 transformPointCloud(*input_, input_transformed, transformation_);
283 float error =
284 computeErrorMetric(input_transformed, static_cast<float>(corr_dist_threshold_));
285
286 // If the new error is lower, update the final transformation
287 if (i_iter == 0 || error < lowest_error) {
288 lowest_error = error;
289 final_transformation_ = transformation_;
290 converged_ = true;
291 }
292 }
293
294 // Apply the final transformation
295 transformPointCloud(*input_, output, final_transformation_);
296}
297
298} // namespace pcl
299
300#endif //#ifndef IA_RANSAC_HPP_
void setNumberOfThreads(unsigned int nr_threads=0)
Initialize the scheduler and set the number of threads to use.
Definition ia_ransac.hpp:51
void computeTransformation(PointCloudSource &output, const Eigen::Matrix4f &guess) override
Rigid transformation computation method.
float computeErrorMetric(const PointCloudSource &cloud, float threshold)
An error metric for that computes the quality of the alignment between the given cloud and the target...
typename Registration< PointSource, PointTarget >::PointCloudSource PointCloudSource
Definition ia_ransac.h:71
void setTargetFeatures(const FeatureCloudConstPtr &features)
Provide a shared pointer to the target point cloud's feature descriptors.
Definition ia_ransac.hpp:87
void selectSamples(const PointCloudSource &cloud, unsigned int nr_samples, float min_sample_distance, pcl::Indices &sample_indices)
Select nr_samples sample points from cloud while making sure that their pairwise distances are greate...
void findSimilarFeatures(const FeatureCloud &input_features, const pcl::Indices &sample_indices, pcl::Indices &corresponding_indices)
For each of the sample points, find a list of points in the target cloud whose features are similar t...
typename FeatureCloud::ConstPtr FeatureCloudConstPtr
Definition ia_ransac.h:83
void setSourceFeatures(const FeatureCloudConstPtr &features)
Provide a shared pointer to the source point cloud's feature descriptors.
Definition ia_ransac.hpp:73
Define standard C methods to do distance calculations.
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.
detail::int_type_t< detail::index_type_size, detail::index_type_signed > index_t
Type used for an index in PCL.
Definition types.h:112
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