Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cvfh.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2010-2011, 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: cvfh.hpp 6144 2012-07-04 22:06:28Z rusu $
38  *
39  */
40 
41 #ifndef PCL_FEATURES_IMPL_CVFH_H_
42 #define PCL_FEATURES_IMPL_CVFH_H_
43 
44 #include <pcl/features/cvfh.h>
45 #include <pcl/features/pfh.h>
46 
48 template<typename PointInT, typename PointNT, typename PointOutT> void
50 {
52  {
53  output.width = output.height = 0;
54  output.points.clear ();
55  return;
56  }
57  // Resize the output dataset
58  // Important! We should only allocate precisely how many elements we will need, otherwise
59  // we risk at pre-allocating too much memory which could lead to bad_alloc
60  // (see http://dev.pointclouds.org/issues/657)
61  output.width = output.height = 1;
62  output.points.resize (1);
63 
64  // Perform the actual feature computation
65  computeFeature (output);
66 
68 }
69 
71 template<typename PointInT, typename PointNT, typename PointOutT> void
74  const pcl::PointCloud<pcl::PointNormal> &normals,
75  float tolerance,
77  std::vector<pcl::PointIndices> &clusters,
78  double eps_angle,
79  unsigned int min_pts_per_cluster,
80  unsigned int max_pts_per_cluster)
81 {
82  if (tree->getInputCloud ()->points.size () != cloud.points.size ())
83  {
84  PCL_ERROR ("[pcl::extractEuclideanClusters] Tree built for a different point cloud dataset (%zu) than the input cloud (%zu)!\n", tree->getInputCloud ()->points.size (), cloud.points.size ());
85  return;
86  }
87  if (cloud.points.size () != normals.points.size ())
88  {
89  PCL_ERROR ("[pcl::extractEuclideanClusters] Number of points in the input point cloud (%zu) different than normals (%zu)!\n", cloud.points.size (), normals.points.size ());
90  return;
91  }
92 
93  // Create a bool vector of processed point indices, and initialize it to false
94  std::vector<bool> processed (cloud.points.size (), false);
95 
96  std::vector<int> nn_indices;
97  std::vector<float> nn_distances;
98  // Process all points in the indices vector
99  for (int i = 0; i < static_cast<int> (cloud.points.size ()); ++i)
100  {
101  if (processed[i])
102  continue;
103 
104  std::vector<unsigned int> seed_queue;
105  int sq_idx = 0;
106  seed_queue.push_back (i);
107 
108  processed[i] = true;
109 
110  while (sq_idx < static_cast<int> (seed_queue.size ()))
111  {
112  // Search for sq_idx
113  if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances))
114  {
115  sq_idx++;
116  continue;
117  }
118 
119  for (size_t j = 1; j < nn_indices.size (); ++j) // nn_indices[0] should be sq_idx
120  {
121  if (processed[nn_indices[j]]) // Has this point been processed before ?
122  continue;
123 
124  //processed[nn_indices[j]] = true;
125  // [-1;1]
126 
127  double dot_p = normals.points[seed_queue[sq_idx]].normal[0] * normals.points[nn_indices[j]].normal[0]
128  + normals.points[seed_queue[sq_idx]].normal[1] * normals.points[nn_indices[j]].normal[1]
129  + normals.points[seed_queue[sq_idx]].normal[2] * normals.points[nn_indices[j]].normal[2];
130 
131  if (fabs (acos (dot_p)) < eps_angle)
132  {
133  processed[nn_indices[j]] = true;
134  seed_queue.push_back (nn_indices[j]);
135  }
136  }
137 
138  sq_idx++;
139  }
140 
141  // If this queue is satisfactory, add to the clusters
142  if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster)
143  {
145  r.indices.resize (seed_queue.size ());
146  for (size_t j = 0; j < seed_queue.size (); ++j)
147  r.indices[j] = seed_queue[j];
148 
149  std::sort (r.indices.begin (), r.indices.end ());
150  r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ());
151 
152  r.header = cloud.header;
153  clusters.push_back (r); // We could avoid a copy by working directly in the vector
154  }
155  }
156 }
157 
159 template<typename PointInT, typename PointNT, typename PointOutT> void
161  const pcl::PointCloud<PointNT> & cloud,
162  std::vector<int> &indices_to_use,
163  std::vector<int> &indices_out,
164  std::vector<int> &indices_in,
165  float threshold)
166 {
167  indices_out.resize (cloud.points.size ());
168  indices_in.resize (cloud.points.size ());
169 
170  size_t in, out;
171  in = out = 0;
172 
173  for (int i = 0; i < static_cast<int> (indices_to_use.size ()); i++)
174  {
175  if (cloud.points[indices_to_use[i]].curvature > threshold)
176  {
177  indices_out[out] = indices_to_use[i];
178  out++;
179  }
180  else
181  {
182  indices_in[in] = indices_to_use[i];
183  in++;
184  }
185  }
186 
187  indices_out.resize (out);
188  indices_in.resize (in);
189 }
190 
192 template<typename PointInT, typename PointNT, typename PointOutT> void
194 {
195  // Check if input was set
196  if (!normals_)
197  {
198  PCL_ERROR ("[pcl::%s::computeFeature] No input dataset containing normals was given!\n", getClassName ().c_str ());
199  output.width = output.height = 0;
200  output.points.clear ();
201  return;
202  }
203  if (normals_->points.size () != surface_->points.size ())
204  {
205  PCL_ERROR ("[pcl::%s::computeFeature] The number of points in the input dataset differs from the number of points in the dataset containing the normals!\n", getClassName ().c_str ());
206  output.width = output.height = 0;
207  output.points.clear ();
208  return;
209  }
210 
211  centroids_dominant_orientations_.clear ();
212 
213  // ---[ Step 0: remove normals with high curvature
214  std::vector<int> indices_out;
215  std::vector<int> indices_in;
216  filterNormalsWithHighCurvature (*normals_, *indices_, indices_out, indices_in, curv_threshold_);
217 
219  normals_filtered_cloud->width = static_cast<uint32_t> (indices_in.size ());
220  normals_filtered_cloud->height = 1;
221  normals_filtered_cloud->points.resize (normals_filtered_cloud->width);
222 
223  for (size_t i = 0; i < indices_in.size (); ++i)
224  {
225  normals_filtered_cloud->points[i].x = surface_->points[indices_in[i]].x;
226  normals_filtered_cloud->points[i].y = surface_->points[indices_in[i]].y;
227  normals_filtered_cloud->points[i].z = surface_->points[indices_in[i]].z;
228  }
229 
230  std::vector<pcl::PointIndices> clusters;
231 
232  if(normals_filtered_cloud->points.size() >= min_points_)
233  {
234  //recompute normals and use them for clustering
235  KdTreePtr normals_tree_filtered (new pcl::search::KdTree<pcl::PointNormal> (false));
236  normals_tree_filtered->setInputCloud (normals_filtered_cloud);
237 
238  NormalEstimator n3d;
239  n3d.setRadiusSearch (radius_normals_);
240  n3d.setSearchMethod (normals_tree_filtered);
241  n3d.setInputCloud (normals_filtered_cloud);
242  n3d.compute (*normals_filtered_cloud);
243 
244  KdTreePtr normals_tree (new pcl::search::KdTree<pcl::PointNormal> (false));
245  normals_tree->setInputCloud (normals_filtered_cloud);
246 
247  extractEuclideanClustersSmooth (*normals_filtered_cloud,
248  *normals_filtered_cloud,
249  cluster_tolerance_,
250  normals_tree,
251  clusters,
252  eps_angle_threshold_,
253  static_cast<unsigned int> (min_points_));
254 
255  }
256 
257  VFHEstimator vfh;
258  vfh.setInputCloud (surface_);
259  vfh.setInputNormals (normals_);
260  vfh.setIndices(indices_);
261  vfh.setSearchMethod (this->tree_);
262  vfh.setUseGivenNormal (true);
263  vfh.setUseGivenCentroid (true);
264  vfh.setNormalizeBins (normalize_bins_);
265  vfh.setNormalizeDistance (true);
266  vfh.setFillSizeComponent (true);
267  output.height = 1;
268 
269  // ---[ Step 1b : check if any dominant cluster was found
270  if (clusters.size () > 0)
271  { // ---[ Step 1b.1 : If yes, compute CVFH using the cluster information
272 
273  for (size_t i = 0; i < clusters.size (); ++i) //for each cluster
274  {
275  Eigen::Vector4f avg_normal = Eigen::Vector4f::Zero ();
276  Eigen::Vector4f avg_centroid = Eigen::Vector4f::Zero ();
277 
278  for (size_t j = 0; j < clusters[i].indices.size (); j++)
279  {
280  avg_normal += normals_filtered_cloud->points[clusters[i].indices[j]].getNormalVector4fMap ();
281  avg_centroid += normals_filtered_cloud->points[clusters[i].indices[j]].getVector4fMap ();
282  }
283 
284  avg_normal /= static_cast<float> (clusters[i].indices.size ());
285  avg_centroid /= static_cast<float> (clusters[i].indices.size ());
286 
287  Eigen::Vector4f centroid_test;
288  pcl::compute3DCentroid (*normals_filtered_cloud, centroid_test);
289  avg_normal.normalize ();
290 
291  Eigen::Vector3f avg_norm (avg_normal[0], avg_normal[1], avg_normal[2]);
292  Eigen::Vector3f avg_dominant_centroid (avg_centroid[0], avg_centroid[1], avg_centroid[2]);
293 
294  //append normal and centroid for the clusters
295  dominant_normals_.push_back (avg_norm);
296  centroids_dominant_orientations_.push_back (avg_dominant_centroid);
297  }
298 
299  //compute modified VFH for all dominant clusters and add them to the list!
300  output.points.resize (dominant_normals_.size ());
301  output.width = static_cast<uint32_t> (dominant_normals_.size ());
302 
303  for (size_t i = 0; i < dominant_normals_.size (); ++i)
304  {
305  //configure VFH computation for CVFH
306  vfh.setNormalToUse (dominant_normals_[i]);
307  vfh.setCentroidToUse (centroids_dominant_orientations_[i]);
309  vfh.compute (vfh_signature);
310  output.points[i] = vfh_signature.points[0];
311  }
312  }
313  else
314  { // ---[ Step 1b.1 : If no, compute CVFH using all the object points
315  Eigen::Vector4f avg_centroid;
316  pcl::compute3DCentroid (*surface_, avg_centroid);
317  Eigen::Vector3f cloud_centroid (avg_centroid[0], avg_centroid[1], avg_centroid[2]);
318  centroids_dominant_orientations_.push_back (cloud_centroid);
319 
320  //configure VFH computation for CVFH using all object points
321  vfh.setCentroidToUse (cloud_centroid);
322  vfh.setUseGivenNormal (false);
323 
325  vfh.compute (vfh_signature);
326 
327  output.points.resize (1);
328  output.width = 1;
329 
330  output.points[0] = vfh_signature.points[0];
331  }
332 }
333 
334 #define PCL_INSTANTIATE_CVFHEstimation(T,NT,OutT) template class PCL_EXPORTS pcl::CVFHEstimation<T,NT,OutT>;
335 
336 #endif // PCL_FEATURES_IMPL_VFH_H_