Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
concave_hull.hpp
Go to the documentation of this file.
1 
40 #include <pcl/pcl_config.h>
41 #ifdef HAVE_QHULL
42 
43 #ifndef PCL_SURFACE_IMPL_CONCAVE_HULL_H_
44 #define PCL_SURFACE_IMPL_CONCAVE_HULL_H_
45 
46 #include <map>
48 #include <pcl/common/common.h>
49 #include <pcl/common/eigen.h>
50 #include <pcl/common/centroid.h>
51 #include <pcl/common/transforms.h>
53 #include <pcl/common/io.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <pcl/surface/qhull.h>
57 
59 template <typename PointInT> void
60 pcl::ConcaveHull<PointInT>::reconstruct (PointCloud &output)
61 {
62  output.header = input_->header;
63  if (alpha_ <= 0)
64  {
65  PCL_ERROR ("[pcl::%s::reconstruct] Alpha parameter must be set to a positive number!\n", getClassName ().c_str ());
66  output.points.clear ();
67  return;
68  }
69 
70  if (!initCompute ())
71  {
72  output.points.clear ();
73  return;
74  }
75 
76  // Perform the actual surface reconstruction
77  std::vector<pcl::Vertices> polygons;
78  performReconstruction (output, polygons);
79 
80  output.width = static_cast<uint32_t> (output.points.size ());
81  output.height = 1;
82  output.is_dense = true;
83 
84  deinitCompute ();
85 }
86 
88 template <typename PointInT> void
89 pcl::ConcaveHull<PointInT>::reconstruct (PointCloud &output, std::vector<pcl::Vertices> &polygons)
90 {
91  output.header = input_->header;
92  if (alpha_ <= 0)
93  {
94  PCL_ERROR ("[pcl::%s::reconstruct] Alpha parameter must be set to a positive number!\n", getClassName ().c_str ());
95  output.points.clear ();
96  return;
97  }
98 
99  if (!initCompute ())
100  {
101  output.points.clear ();
102  return;
103  }
104 
105  // Perform the actual surface reconstruction
106  performReconstruction (output, polygons);
107 
108  output.width = static_cast<uint32_t> (output.points.size ());
109  output.height = 1;
110  output.is_dense = true;
111 
112  deinitCompute ();
113 }
114 
115 #ifdef __GNUC__
116 #pragma GCC diagnostic ignored "-Wold-style-cast"
117 #endif
118 
119 template <typename PointInT> void
120 pcl::ConcaveHull<PointInT>::performReconstruction (PointCloud &alpha_shape, std::vector<pcl::Vertices> &polygons)
121 {
122  EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
123  Eigen::Vector4f xyz_centroid;
124  computeMeanAndCovarianceMatrix (*input_, *indices_, covariance_matrix, xyz_centroid);
125 
126  // Check if the covariance matrix is finite or not.
127  for (int i = 0; i < 3; ++i)
128  for (int j = 0; j < 3; ++j)
129  if (!pcl_isfinite (covariance_matrix.coeffRef (i, j)))
130  return;
131 
132  EIGEN_ALIGN16 Eigen::Vector3f eigen_values;
133  EIGEN_ALIGN16 Eigen::Matrix3f eigen_vectors;
134  pcl::eigen33 (covariance_matrix, eigen_vectors, eigen_values);
135 
136  Eigen::Affine3f transform1;
137  transform1.setIdentity ();
138 
139  // If no input dimension is specified, determine automatically
140  if (dim_ == 0)
141  {
142  PCL_WARN ("[pcl::%s] WARNING: Input dimension not specified. Automatically determining input dimension.\n", getClassName ().c_str ());
143  if (eigen_values[0] / eigen_values[2] < 1.0e-3)
144  dim_ = 2;
145  else
146  dim_ = 3;
147  }
148 
149  if (dim_ == 2)
150  {
151  // we have points laying on a plane, using 2d convex hull
152  // compute transformation bring eigen_vectors.col(i) to z-axis
153 
154  transform1 (2, 0) = eigen_vectors (0, 0);
155  transform1 (2, 1) = eigen_vectors (1, 0);
156  transform1 (2, 2) = eigen_vectors (2, 0);
157 
158  transform1 (1, 0) = eigen_vectors (0, 1);
159  transform1 (1, 1) = eigen_vectors (1, 1);
160  transform1 (1, 2) = eigen_vectors (2, 1);
161  transform1 (0, 0) = eigen_vectors (0, 2);
162  transform1 (0, 1) = eigen_vectors (1, 2);
163  transform1 (0, 2) = eigen_vectors (2, 2);
164  }
165  else
166  {
167  transform1.setIdentity ();
168  }
169 
170  PointCloud cloud_transformed;
171  pcl::demeanPointCloud (*input_, *indices_, xyz_centroid, cloud_transformed);
172  pcl::transformPointCloud (cloud_transformed, cloud_transformed, transform1);
173 
174  // True if qhull should free points in qh_freeqhull() or reallocation
175  boolT ismalloc = True;
176  // option flags for qhull, see qh_opt.htm
177  char flags[] = "qhull d QJ";
178  // output from qh_produce_output(), use NULL to skip qh_produce_output()
179  FILE *outfile = NULL;
180  // error messages from qhull code
181  FILE *errfile = stderr;
182  // 0 if no error from qhull
183  int exitcode;
184 
185  // Array of coordinates for each point
186  coordT *points = reinterpret_cast<coordT*> (calloc (cloud_transformed.points.size () * dim_, sizeof(coordT)));
187 
188  for (size_t i = 0; i < cloud_transformed.points.size (); ++i)
189  {
190  points[i * dim_ + 0] = static_cast<coordT> (cloud_transformed.points[i].x);
191  points[i * dim_ + 1] = static_cast<coordT> (cloud_transformed.points[i].y);
192 
193  if (dim_ > 2)
194  points[i * dim_ + 2] = static_cast<coordT> (cloud_transformed.points[i].z);
195  }
196 
197  // Compute concave hull
198  exitcode = qh_new_qhull (dim_, static_cast<int> (cloud_transformed.points.size ()), points, ismalloc, flags, outfile, errfile);
199 
200  if (exitcode != 0)
201  {
202  PCL_ERROR ("[pcl::%s::performReconstrution] ERROR: qhull was unable to compute a concave hull for the given point cloud (%zu)!\n", getClassName ().c_str (), cloud_transformed.points.size ());
203 
204  //check if it fails because of NaN values...
205  if (!cloud_transformed.is_dense)
206  {
207  bool NaNvalues = false;
208  for (size_t i = 0; i < cloud_transformed.size (); ++i)
209  {
210  if (!pcl_isfinite (cloud_transformed.points[i].x) ||
211  !pcl_isfinite (cloud_transformed.points[i].y) ||
212  !pcl_isfinite (cloud_transformed.points[i].z))
213  {
214  NaNvalues = true;
215  break;
216  }
217  }
218 
219  if (NaNvalues)
220  PCL_ERROR ("[pcl::%s::performReconstruction] ERROR: point cloud contains NaN values, consider running pcl::PassThrough filter first to remove NaNs!\n", getClassName ().c_str ());
221  }
222 
223  alpha_shape.points.resize (0);
224  alpha_shape.width = alpha_shape.height = 0;
225  polygons.resize (0);
226 
227  qh_freeqhull (!qh_ALL);
228  int curlong, totlong;
229  qh_memfreeshort (&curlong, &totlong);
230 
231  return;
232  }
233 
234  qh_setvoronoi_all ();
235 
236  int num_vertices = qh num_vertices;
237  alpha_shape.points.resize (num_vertices);
238 
239  vertexT *vertex;
240  // Max vertex id
241  int max_vertex_id = 0;
242  FORALLvertices
243  {
244  if (vertex->id + 1 > max_vertex_id)
245  max_vertex_id = vertex->id + 1;
246  }
247 
248  facetT *facet; // set by FORALLfacets
249 
250  ++max_vertex_id;
251  std::vector<int> qhid_to_pcidx (max_vertex_id);
252 
253  int num_facets = qh num_facets;
254  int dd = 0;
255 
256  if (dim_ == 3)
257  {
258  setT *triangles_set = qh_settemp (4 * num_facets);
259  if (voronoi_centers_)
260  voronoi_centers_->points.resize (num_facets);
261 
262  int non_upper = 0;
263  FORALLfacets
264  {
265  // Facets are tetrahedrons (3d)
266  if (!facet->upperdelaunay)
267  {
268  vertexT *anyVertex = static_cast<vertexT*> (facet->vertices->e[0].p);
269  double *center = facet->center;
270  double r = qh_pointdist (anyVertex->point,center,dim_);
271  facetT *neighb;
272 
273  if (voronoi_centers_)
274  {
275  voronoi_centers_->points[non_upper].x = static_cast<float> (facet->center[0]);
276  voronoi_centers_->points[non_upper].y = static_cast<float> (facet->center[1]);
277  voronoi_centers_->points[non_upper].z = static_cast<float> (facet->center[2]);
278  }
279 
280  non_upper++;
281 
282  if (r <= alpha_)
283  {
284  // all triangles in tetrahedron are good, add them all to the alpha shape (triangles_set)
285  qh_makeridges (facet);
286  facet->good = true;
287  facet->visitid = qh visit_id;
288  ridgeT *ridge, **ridgep;
289  FOREACHridge_ (facet->ridges)
290  {
291  neighb = otherfacet_ (ridge, facet);
292  if ((neighb->visitid != qh visit_id))
293  qh_setappend (&triangles_set, ridge);
294  }
295  }
296  else
297  {
298  // consider individual triangles from the tetrahedron...
299  facet->good = false;
300  facet->visitid = qh visit_id;
301  qh_makeridges (facet);
302  ridgeT *ridge, **ridgep;
303  FOREACHridge_ (facet->ridges)
304  {
305  facetT *neighb;
306  neighb = otherfacet_ (ridge, facet);
307  if ((neighb->visitid != qh visit_id))
308  {
309  // check if individual triangle is good and add it to triangles_set
310 
311  PointInT a, b, c;
312  a.x = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[0].p))->point[0]);
313  a.y = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[0].p))->point[1]);
314  a.z = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[0].p))->point[2]);
315  b.x = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[1].p))->point[0]);
316  b.y = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[1].p))->point[1]);
317  b.z = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[1].p))->point[2]);
318  c.x = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[2].p))->point[0]);
319  c.y = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[2].p))->point[1]);
320  c.z = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[2].p))->point[2]);
321 
322  double r = pcl::getCircumcircleRadius (a, b, c);
323  if (r <= alpha_)
324  qh_setappend (&triangles_set, ridge);
325  }
326  }
327  }
328  }
329  }
330 
331  if (voronoi_centers_)
332  voronoi_centers_->points.resize (non_upper);
333 
334  // filter, add points to alpha_shape and create polygon structure
335 
336  int num_good_triangles = 0;
337  ridgeT *ridge, **ridgep;
338  FOREACHridge_ (triangles_set)
339  {
340  if (ridge->bottom->upperdelaunay || ridge->top->upperdelaunay || !ridge->top->good || !ridge->bottom->good)
341  num_good_triangles++;
342  }
343 
344  polygons.resize (num_good_triangles);
345 
346  int vertices = 0;
347  std::vector<bool> added_vertices (max_vertex_id, false);
348 
349  int triangles = 0;
350  FOREACHridge_ (triangles_set)
351  {
352  if (ridge->bottom->upperdelaunay || ridge->top->upperdelaunay || !ridge->top->good || !ridge->bottom->good)
353  {
354  polygons[triangles].vertices.resize (3);
355  int vertex_n, vertex_i;
356  FOREACHvertex_i_ ((*ridge).vertices) //3 vertices per ridge!
357  {
358  if (!added_vertices[vertex->id])
359  {
360  alpha_shape.points[vertices].x = static_cast<float> (vertex->point[0]);
361  alpha_shape.points[vertices].y = static_cast<float> (vertex->point[1]);
362  alpha_shape.points[vertices].z = static_cast<float> (vertex->point[2]);
363 
364  qhid_to_pcidx[vertex->id] = vertices; //map the vertex id of qhull to the point cloud index
365  added_vertices[vertex->id] = true;
366  vertices++;
367  }
368 
369  polygons[triangles].vertices[vertex_i] = qhid_to_pcidx[vertex->id];
370 
371  }
372 
373  triangles++;
374  }
375  }
376 
377  alpha_shape.points.resize (vertices);
378  alpha_shape.width = static_cast<uint32_t> (alpha_shape.points.size ());
379  alpha_shape.height = 1;
380  }
381  else
382  {
383  // Compute the alpha complex for the set of points
384  // Filters the delaunay triangles
385  setT *edges_set = qh_settemp (3 * num_facets);
386  if (voronoi_centers_)
387  voronoi_centers_->points.resize (num_facets);
388 
389  FORALLfacets
390  {
391  // Facets are the delaunay triangles (2d)
392  if (!facet->upperdelaunay)
393  {
394  // Check if the distance from any vertex to the facet->center
395  // (center of the voronoi cell) is smaller than alpha
396  vertexT *anyVertex = static_cast<vertexT*>(facet->vertices->e[0].p);
397  double r = (sqrt ((anyVertex->point[0] - facet->center[0]) *
398  (anyVertex->point[0] - facet->center[0]) +
399  (anyVertex->point[1] - facet->center[1]) *
400  (anyVertex->point[1] - facet->center[1])));
401  if (r <= alpha_)
402  {
403  pcl::Vertices facet_vertices; //TODO: is not used!!
404  qh_makeridges (facet);
405  facet->good = true;
406 
407  ridgeT *ridge, **ridgep;
408  FOREACHridge_ (facet->ridges)
409  qh_setappend (&edges_set, ridge);
410 
411  if (voronoi_centers_)
412  {
413  voronoi_centers_->points[dd].x = static_cast<float> (facet->center[0]);
414  voronoi_centers_->points[dd].y = static_cast<float> (facet->center[1]);
415  voronoi_centers_->points[dd].z = 0.0f;
416  }
417 
418  ++dd;
419  }
420  else
421  facet->good = false;
422  }
423  }
424 
425  int vertices = 0;
426  std::vector<bool> added_vertices (max_vertex_id, false);
427  std::map<int, std::vector<int> > edges;
428 
429  ridgeT *ridge, **ridgep;
430  FOREACHridge_ (edges_set)
431  {
432  if (ridge->bottom->upperdelaunay || ridge->top->upperdelaunay || !ridge->top->good || !ridge->bottom->good)
433  {
434  int vertex_n, vertex_i;
435  int vertices_in_ridge=0;
436  std::vector<int> pcd_indices;
437  pcd_indices.resize (2);
438 
439  FOREACHvertex_i_ ((*ridge).vertices) //in 2-dim, 2 vertices per ridge!
440  {
441  if (!added_vertices[vertex->id])
442  {
443  alpha_shape.points[vertices].x = static_cast<float> (vertex->point[0]);
444  alpha_shape.points[vertices].y = static_cast<float> (vertex->point[1]);
445 
446  if (dim_ > 2)
447  alpha_shape.points[vertices].z = static_cast<float> (vertex->point[2]);
448  else
449  alpha_shape.points[vertices].z = 0;
450 
451  qhid_to_pcidx[vertex->id] = vertices; //map the vertex id of qhull to the point cloud index
452  added_vertices[vertex->id] = true;
453  pcd_indices[vertices_in_ridge] = vertices;
454  vertices++;
455  }
456  else
457  {
458  pcd_indices[vertices_in_ridge] = qhid_to_pcidx[vertex->id];
459  }
460 
461  vertices_in_ridge++;
462  }
463 
464  // make edges bidirectional and pointing to alpha_shape pointcloud...
465  edges[pcd_indices[0]].push_back (pcd_indices[1]);
466  edges[pcd_indices[1]].push_back (pcd_indices[0]);
467  }
468  }
469 
470  alpha_shape.points.resize (vertices);
471 
472  std::vector<std::vector<int> > connected;
473  PointCloud alpha_shape_sorted;
474  alpha_shape_sorted.points.resize (vertices);
475 
476  // iterate over edges until they are empty!
477  std::map<int, std::vector<int> >::iterator curr = edges.begin ();
478  int next = - 1;
479  std::vector<bool> used (vertices, false); // used to decide which direction should we take!
480  std::vector<int> pcd_idx_start_polygons;
481  pcd_idx_start_polygons.push_back (0);
482 
483  // start following edges and removing elements
484  int sorted_idx = 0;
485  while (!edges.empty ())
486  {
487  alpha_shape_sorted.points[sorted_idx] = alpha_shape.points[(*curr).first];
488  // check where we can go from (*curr).first
489  for (size_t i = 0; i < (*curr).second.size (); i++)
490  {
491  if (!used[((*curr).second)[i]])
492  {
493  // we can go there
494  next = ((*curr).second)[i];
495  break;
496  }
497  }
498 
499  used[(*curr).first] = true;
500  edges.erase (curr); // remove edges starting from curr
501 
502  sorted_idx++;
503 
504  if (edges.empty ())
505  break;
506 
507  // reassign current
508  curr = edges.find (next); // if next is not found, then we have unconnected polygons.
509  if (curr == edges.end ())
510  {
511  // set current to any of the remaining in edge!
512  curr = edges.begin ();
513  pcd_idx_start_polygons.push_back (sorted_idx);
514  }
515  }
516 
517  pcd_idx_start_polygons.push_back (sorted_idx);
518 
519  alpha_shape.points = alpha_shape_sorted.points;
520 
521  polygons.resize (pcd_idx_start_polygons.size () - 1);
522 
523  for (size_t poly_id = 0; poly_id < polygons.size (); poly_id++)
524  {
525  polygons[poly_id].vertices.resize (pcd_idx_start_polygons[poly_id + 1] - pcd_idx_start_polygons[poly_id] + 1);
526  // populate points in the corresponding polygon
527  for (int j = pcd_idx_start_polygons[poly_id]; j < pcd_idx_start_polygons[poly_id + 1]; ++j)
528  polygons[poly_id].vertices[j - pcd_idx_start_polygons[poly_id]] = static_cast<uint32_t> (j);
529 
530  polygons[poly_id].vertices[polygons[poly_id].vertices.size () - 1] = pcd_idx_start_polygons[poly_id];
531  }
532 
533  if (voronoi_centers_)
534  voronoi_centers_->points.resize (dd);
535  }
536 
537  qh_freeqhull (!qh_ALL);
538  int curlong, totlong;
539  qh_memfreeshort (&curlong, &totlong);
540 
541  Eigen::Affine3f transInverse = transform1.inverse ();
542  pcl::transformPointCloud (alpha_shape, alpha_shape, transInverse);
543  xyz_centroid[0] = - xyz_centroid[0];
544  xyz_centroid[1] = - xyz_centroid[1];
545  xyz_centroid[2] = - xyz_centroid[2];
546  pcl::demeanPointCloud (alpha_shape, xyz_centroid, alpha_shape);
547 
548  // also transform voronoi_centers_...
549  if (voronoi_centers_)
550  {
551  pcl::transformPointCloud (*voronoi_centers_, *voronoi_centers_, transInverse);
552  pcl::demeanPointCloud (*voronoi_centers_, xyz_centroid, *voronoi_centers_);
553  }
554 
555  if (keep_information_)
556  {
557  // build a tree with the original points
558  pcl::KdTreeFLANN<PointInT> tree (true);
559  tree.setInputCloud (input_, indices_);
560 
561  std::vector<int> neighbor;
562  std::vector<float> distances;
563  neighbor.resize (1);
564  distances.resize (1);
565 
566  // for each point in the concave hull, search for the nearest neighbor in the original point cloud
567 
568  std::vector<int> indices;
569  indices.resize (alpha_shape.points.size ());
570 
571  for (size_t i = 0; i < alpha_shape.points.size (); i++)
572  {
573  tree.nearestKSearch (alpha_shape.points[i], 1, neighbor, distances);
574  indices[i] = neighbor[0];
575  }
576 
577  // replace point with the closest neighbor in the original point cloud
578  pcl::copyPointCloud (*input_, indices, alpha_shape);
579  }
580 }
581 #ifdef __GNUC__
582 #pragma GCC diagnostic warning "-Wold-style-cast"
583 #endif
584 
586 template <typename PointInT> void
587 pcl::ConcaveHull<PointInT>::performReconstruction (PolygonMesh &output)
588 {
589  // Perform reconstruction
590  pcl::PointCloud<PointInT> hull_points;
591  performReconstruction (hull_points, output.polygons);
592 
593  // Convert the PointCloud into a PointCloud2
594  pcl::toROSMsg (hull_points, output.cloud);
595 }
596 
598 template <typename PointInT> void
599 pcl::ConcaveHull<PointInT>::performReconstruction (std::vector<pcl::Vertices> &polygons)
600 {
601  pcl::PointCloud<PointInT> hull_points;
602  performReconstruction (hull_points, polygons);
603 }
604 
605 
606 #define PCL_INSTANTIATE_ConcaveHull(T) template class PCL_EXPORTS pcl::ConcaveHull<T>;
607 
608 #endif // PCL_SURFACE_IMPL_CONCAVE_HULL_H_
609 #endif