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