Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ppf_registration.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2011, Alexandru-Eugen Ichim
5  * Willow Garage, Inc
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage, Inc. nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * $Id: ppf_registration.hpp 5121 2012-03-16 03:03:47Z rusu $
36  */
37 
38 
39 #ifndef PCL_REGISTRATION_IMPL_PPF_REGISTRATION_H_
40 #define PCL_REGISTRATION_IMPL_PPF_REGISTRATION_H_
41 
42 //#include <pcl/registration/ppf_registration.h>
43 #include <pcl/features/ppf.h>
44 #include <pcl/common/transforms.h>
45 
46 #include <pcl/features/pfh.h>
48 void
50 {
51  // Discretize the feature cloud and insert it in the hash map
52  feature_hash_map_->clear ();
53  unsigned int n = static_cast<unsigned int> (sqrt (static_cast<float> (feature_cloud->points.size ())));
54  int d1, d2, d3, d4;
55  max_dist_ = -1.0;
56  alpha_m_.resize (n);
57  for (size_t i = 0; i < n; ++i)
58  {
59  std::vector <float> alpha_m_row (n);
60  for (size_t j = 0; j < n; ++j)
61  {
62  d1 = static_cast<int> (floor (feature_cloud->points[i*n+j].f1 / angle_discretization_step_));
63  d2 = static_cast<int> (floor (feature_cloud->points[i*n+j].f2 / angle_discretization_step_));
64  d3 = static_cast<int> (floor (feature_cloud->points[i*n+j].f3 / angle_discretization_step_));
65  d4 = static_cast<int> (floor (feature_cloud->points[i*n+j].f4 / distance_discretization_step_));
66  feature_hash_map_->insert (std::pair<HashKeyStruct, std::pair<size_t, size_t> > (HashKeyStruct (d1, d2, d3, d4), std::pair<size_t, size_t> (i, j)));
67  alpha_m_row [j] = feature_cloud->points[i*n + j].alpha_m;
68 
69  if (max_dist_ < feature_cloud->points[i*n + j].f4)
70  max_dist_ = feature_cloud->points[i*n + j].f4;
71  }
72  alpha_m_[i] = alpha_m_row;
73  }
74 
75  internals_initialized_ = true;
76 }
77 
78 
80 void
81 pcl::PPFHashMapSearch::nearestNeighborSearch (float &f1, float &f2, float &f3, float &f4,
82  std::vector<std::pair<size_t, size_t> > &indices)
83 {
84  if (!internals_initialized_)
85  {
86  PCL_ERROR("[pcl::PPFRegistration::nearestNeighborSearch]: input feature cloud has not been set - skipping search!\n");
87  return;
88  }
89 
90  int d1 = static_cast<int> (floor (f1 / angle_discretization_step_)),
91  d2 = static_cast<int> (floor (f2 / angle_discretization_step_)),
92  d3 = static_cast<int> (floor (f3 / angle_discretization_step_)),
93  d4 = static_cast<int> (floor (f4 / distance_discretization_step_));
94 
95  indices.clear ();
96  HashKeyStruct key = HashKeyStruct (d1, d2, d3, d4);
97  std::pair <FeatureHashMapType::iterator, FeatureHashMapType::iterator> map_iterator_pair = feature_hash_map_->equal_range (key);
98  for (; map_iterator_pair.first != map_iterator_pair.second; ++ map_iterator_pair.first)
99  indices.push_back (std::pair<size_t, size_t> (map_iterator_pair.first->second.first,
100  map_iterator_pair.first->second.second));
101 }
102 
103 
105 template <typename PointSource, typename PointTarget> void
107 {
109 
110  scene_search_tree_ = typename pcl::KdTreeFLANN<PointTarget>::Ptr (new pcl::KdTreeFLANN<PointTarget>);
111  scene_search_tree_->setInputCloud (target_);
112 }
113 
114 
116 template <typename PointSource, typename PointTarget> void
117 pcl::PPFRegistration<PointSource, PointTarget>::computeTransformation (PointCloudSource &output, const Eigen::Matrix4f& guess)
118 {
119  if (!search_method_)
120  {
121  PCL_ERROR("[pcl::PPFRegistration::computeTransformation] Search method not set - skipping computeTransformation!\n");
122  return;
123  }
124 
125  if (guess != Eigen::Matrix4f::Identity ())
126  {
127  PCL_ERROR("[pcl::PPFRegistration::computeTransformation] setting initial transform (guess) not implemented!\n");
128  }
129 
130  PoseWithVotesList voted_poses;
131  std::vector <std::vector <unsigned int> > accumulator_array;
132  accumulator_array.resize (input_->points.size ());
133  for (size_t i = 0; i < input_->points.size (); ++i)
134  {
135  std::vector <unsigned int> aux (static_cast<size_t> (floor(2*M_PI / search_method_->getAngleDiscretizationStep ()), 0));
136  accumulator_array[i] = aux;
137  }
138  PCL_INFO ("Accumulator array size: %u x %u.\n", accumulator_array.size (), accumulator_array.back ().size ());
139 
140  // Consider every <scene_reference_point_sampling_rate>-th point as the reference point => fix s_r
141  float f1, f2, f3, f4;
142  for (size_t scene_reference_index = 0; scene_reference_index < target_->points.size (); scene_reference_index += scene_reference_point_sampling_rate_)
143  {
144  Eigen::Vector3f scene_reference_point = target_->points[scene_reference_index].getVector3fMap (),
145  scene_reference_normal = target_->points[scene_reference_index].getNormalVector3fMap ();
146 
147  Eigen::AngleAxisf rotation_sg (acosf (scene_reference_normal.dot (Eigen::Vector3f::UnitX ())),
148  scene_reference_normal.cross (Eigen::Vector3f::UnitX ()). normalized());
149  Eigen::Affine3f transform_sg = Eigen::Translation3f ( rotation_sg* ((-1)*scene_reference_point)) * rotation_sg;
150 
151  // For every other point in the scene => now have pair (s_r, s_i) fixed
152  std::vector<int> indices;
153  std::vector<float> distances;
154  scene_search_tree_->radiusSearch (target_->points[scene_reference_index],
155  search_method_->getModelDiameter () /2,
156  indices,
157  distances);
158  for(size_t i = 0; i < indices.size (); ++i)
159 // for(size_t i = 0; i < target_->points.size (); ++i)
160  {
161  //size_t scene_point_index = i;
162  size_t scene_point_index = indices[i];
163  if (scene_reference_index != scene_point_index)
164  {
165  if (/*pcl::computePPFPairFeature*/pcl::computePairFeatures (target_->points[scene_reference_index].getVector4fMap (),
166  target_->points[scene_reference_index].getNormalVector4fMap (),
167  target_->points[scene_point_index].getVector4fMap (),
168  target_->points[scene_point_index].getNormalVector4fMap (),
169  f1, f2, f3, f4))
170  {
171  std::vector<std::pair<size_t, size_t> > nearest_indices;
172  search_method_->nearestNeighborSearch (f1, f2, f3, f4, nearest_indices);
173 
174  // Compute alpha_s angle
175  Eigen::Vector3f scene_point = target_->points[scene_point_index].getVector3fMap ();
176  Eigen::AngleAxisf rotation_sg (acosf (scene_reference_normal.dot (Eigen::Vector3f::UnitX ())),
177  scene_reference_normal.cross (Eigen::Vector3f::UnitX ()).normalized ());
178  Eigen::Affine3f transform_sg = Eigen::Translation3f ( rotation_sg * ((-1) * scene_reference_point)) * rotation_sg;
179 // float alpha_s = acos (Eigen::Vector3f::UnitY ().dot ((transform_sg * scene_point).normalized ()));
180 
181  Eigen::Vector3f scene_point_transformed = transform_sg * scene_point;
182  float alpha_s = atan2f ( -scene_point_transformed(2), scene_point_transformed(1));
183  if ( alpha_s != alpha_s)
184  {
185  PCL_ERROR ("alpha_s is nan\n");
186  continue;
187  }
188  if (sin (alpha_s) * scene_point_transformed(2) < 0.0f)
189  alpha_s *= (-1);
190  alpha_s *= (-1);
191 
192  // Go through point pairs in the model with the same discretized feature
193  for (std::vector<std::pair<size_t, size_t> >::iterator v_it = nearest_indices.begin (); v_it != nearest_indices.end (); ++ v_it)
194  {
195  size_t model_reference_index = v_it->first,
196  model_point_index = v_it->second;
197  // Calculate angle alpha = alpha_m - alpha_s
198  float alpha = search_method_->alpha_m_[model_reference_index][model_point_index] - alpha_s;
199  unsigned int alpha_discretized = static_cast<unsigned int> (floor (alpha) + floor (M_PI / search_method_->getAngleDiscretizationStep ()));
200  accumulator_array[model_reference_index][alpha_discretized] ++;
201  }
202  }
203  else PCL_ERROR ("[pcl::PPFRegistration::computeTransformation] Computing pair feature vector between points %zu and %zu went wrong.\n", scene_reference_index, scene_point_index);
204  }
205  }
206 
207  size_t max_votes_i = 0, max_votes_j = 0;
208  unsigned int max_votes = 0;
209 
210  for (size_t i = 0; i < accumulator_array.size (); ++i)
211  for (size_t j = 0; j < accumulator_array.back ().size (); ++j)
212  {
213  if (accumulator_array[i][j] > max_votes)
214  {
215  max_votes = accumulator_array[i][j];
216  max_votes_i = i;
217  max_votes_j = j;
218  }
219  // Reset accumulator_array for the next set of iterations with a new scene reference point
220  accumulator_array[i][j] = 0;
221  }
222 
223  Eigen::Vector3f model_reference_point = input_->points[max_votes_i].getVector3fMap (),
224  model_reference_normal = input_->points[max_votes_i].getNormalVector3fMap ();
225  Eigen::AngleAxisf rotation_mg (acosf (model_reference_normal.dot (Eigen::Vector3f::UnitX ())), model_reference_normal.cross (Eigen::Vector3f::UnitX ()).normalized ());
226  Eigen::Affine3f transform_mg = Eigen::Translation3f ( rotation_mg * ((-1) * model_reference_point)) * rotation_mg;
227  Eigen::Affine3f max_transform =
228  transform_sg.inverse () *
229  Eigen::AngleAxisf ((static_cast<float> (max_votes_j) - floorf (static_cast<float> (M_PI) / search_method_->getAngleDiscretizationStep ())) * search_method_->getAngleDiscretizationStep (), Eigen::Vector3f::UnitX ()) *
230  transform_mg;
231 
232  voted_poses.push_back (PoseWithVotes (max_transform, max_votes));
233  }
234  PCL_DEBUG ("Done with the Hough Transform ...\n");
235 
236  // Cluster poses for filtering out outliers and obtaining more precise results
237  PoseWithVotesList results;
238  clusterPoses (voted_poses, results);
239 
240  pcl::transformPointCloud (*input_, output, results.front ().pose);
241 
242  transformation_ = final_transformation_ = results.front ().pose.matrix ();
243  converged_ = true;
244 }
245 
246 
248 template <typename PointSource, typename PointTarget> void
251 {
252  PCL_INFO ("Clustering poses ...\n");
253  // Start off by sorting the poses by the number of votes
254  sort(poses.begin (), poses.end (), poseWithVotesCompareFunction);
255 
256  std::vector<PoseWithVotesList> clusters;
257  std::vector<std::pair<size_t, unsigned int> > cluster_votes;
258  for (size_t poses_i = 0; poses_i < poses.size(); ++ poses_i)
259  {
260  bool found_cluster = false;
261  for (size_t clusters_i = 0; clusters_i < clusters.size(); ++ clusters_i)
262  {
263  if (posesWithinErrorBounds (poses[poses_i].pose, clusters[clusters_i].front ().pose))
264  {
265  found_cluster = true;
266  clusters[clusters_i].push_back (poses[poses_i]);
267  cluster_votes[clusters_i].second += poses[poses_i].votes;
268  break;
269  }
270  }
271 
272  if (found_cluster == false)
273  {
274  // Create a new cluster with the current pose
275  PoseWithVotesList new_cluster;
276  new_cluster.push_back (poses[poses_i]);
277  clusters.push_back (new_cluster);
278  cluster_votes.push_back (std::pair<size_t, unsigned int> (clusters.size () - 1, poses[poses_i].votes));
279  }
280  }
281 
282  // Sort clusters by total number of votes
283  std::sort (cluster_votes.begin (), cluster_votes.end (), clusterVotesCompareFunction);
284  // Compute pose average and put them in result vector
287  result.clear ();
288  size_t max_clusters = (clusters.size () < 3) ? clusters.size () : 3;
289  for (size_t cluster_i = 0; cluster_i < max_clusters; ++ cluster_i)
290  {
291  PCL_INFO ("Winning cluster has #votes: %d and #poses voted: %d.\n", cluster_votes[cluster_i].second, clusters[cluster_votes[cluster_i].first].size ());
292  Eigen::Vector3f translation_average (0.0, 0.0, 0.0);
293  Eigen::Vector4f rotation_average (0.0, 0.0, 0.0, 0.0);
294  for (typename PoseWithVotesList::iterator v_it = clusters[cluster_votes[cluster_i].first].begin (); v_it != clusters[cluster_votes[cluster_i].first].end (); ++ v_it)
295  {
296  translation_average += v_it->pose.translation ();
298  rotation_average += Eigen::Quaternionf (v_it->pose.rotation ()).coeffs ();
299  }
300 
301  translation_average /= static_cast<float> (clusters[cluster_votes[cluster_i].first].size ());
302  rotation_average /= static_cast<float> (clusters[cluster_votes[cluster_i].first].size ());
303 
304  Eigen::Affine3f transform_average;
305  transform_average.translation () = translation_average;
306  transform_average.linear () = Eigen::Quaternionf (rotation_average).normalized().toRotationMatrix ();
307 
308  result.push_back (PoseWithVotes (transform_average, cluster_votes[cluster_i].second));
309  }
310 }
311 
312 
314 template <typename PointSource, typename PointTarget> bool
316  Eigen::Affine3f &pose2)
317 {
318  float position_diff = (pose1.translation () - pose2.translation ()).norm ();
319  Eigen::AngleAxisf rotation_diff_mat (pose1.rotation ().inverse () * pose2.rotation ());
320 
321  float rotation_diff_angle = fabsf (rotation_diff_mat.angle ());
322 
323  if (position_diff < clustering_position_diff_threshold_ && rotation_diff_angle < clustering_rotation_diff_threshold_)
324  return true;
325  else return false;
326 }
327 
328 
330 template <typename PointSource, typename PointTarget> bool
333 {
334  return (a.votes > b.votes);
335 }
336 
337 
339 template <typename PointSource, typename PointTarget> bool
341  const std::pair<size_t, unsigned int> &b)
342 {
343  return (a.second > b.second);
344 }
345 
346 //#define PCL_INSTANTIATE_PPFRegistration(PointSource,PointTarget) template class PCL_EXPORTS pcl::PPFRegistration<PointSource, PointTarget>;
347 
348 #endif // PCL_REGISTRATION_IMPL_PPF_REGISTRATION_H_