Point Cloud Library (PCL)  1.8.1-dev
convex_hull.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  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of Willow Garage, Inc. nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * $Id$
37  *
38  */
39 
40 #include <pcl/pcl_config.h>
41 #ifdef HAVE_QHULL
42 
43 #ifndef PCL_SURFACE_IMPL_CONVEX_HULL_H_
44 #define PCL_SURFACE_IMPL_CONVEX_HULL_H_
45 
46 #include <pcl/surface/convex_hull.h>
47 #include <pcl/common/common.h>
48 #include <pcl/common/eigen.h>
49 #include <pcl/common/transforms.h>
50 #include <pcl/common/io.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <pcl/surface/qhull.h>
54 
55 //////////////////////////////////////////////////////////////////////////
56 template <typename PointInT> void
58 {
59  PCL_DEBUG ("[pcl::%s::calculateInputDimension] WARNING: Input dimension not specified. Automatically determining input dimension.\n", getClassName ().c_str ());
60  Eigen::Vector4d xyz_centroid;
61  compute3DCentroid (*input_, *indices_, xyz_centroid);
62  EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix;
63  computeCovarianceMatrixNormalized (*input_, *indices_, xyz_centroid, covariance_matrix);
64 
65  EIGEN_ALIGN16 Eigen::Vector3d eigen_values;
66  pcl::eigen33 (covariance_matrix, eigen_values);
67 
68  if (std::abs (eigen_values[0]) < std::numeric_limits<double>::epsilon () || std::abs (eigen_values[0] / eigen_values[2]) < 1.0e-3)
69  dimension_ = 2;
70  else
71  dimension_ = 3;
72 }
73 
74 //////////////////////////////////////////////////////////////////////////
75 template <typename PointInT> void
76 pcl::ConvexHull<PointInT>::performReconstruction2D (PointCloud &hull, std::vector<pcl::Vertices> &polygons,
77  bool)
78 {
79  int dimension = 2;
80  bool xy_proj_safe = true;
81  bool yz_proj_safe = true;
82  bool xz_proj_safe = true;
83 
84  // Check the input's normal to see which projection to use
85  PointInT p0 = input_->points[(*indices_)[0]];
86  PointInT p1 = input_->points[(*indices_)[indices_->size () - 1]];
87  PointInT p2 = input_->points[(*indices_)[indices_->size () / 2]];
88  Eigen::Array4f dy1dy2 = (p1.getArray4fMap () - p0.getArray4fMap ()) / (p2.getArray4fMap () - p0.getArray4fMap ());
89  while (!( (dy1dy2[0] != dy1dy2[1]) || (dy1dy2[2] != dy1dy2[1]) ) )
90  {
91  p0 = input_->points[(*indices_)[rand () % indices_->size ()]];
92  p1 = input_->points[(*indices_)[rand () % indices_->size ()]];
93  p2 = input_->points[(*indices_)[rand () % indices_->size ()]];
94  dy1dy2 = (p1.getArray4fMap () - p0.getArray4fMap ()) / (p2.getArray4fMap () - p0.getArray4fMap ());
95  }
96 
97  pcl::PointCloud<PointInT> normal_calc_cloud;
98  normal_calc_cloud.points.resize (3);
99  normal_calc_cloud.points[0] = p0;
100  normal_calc_cloud.points[1] = p1;
101  normal_calc_cloud.points[2] = p2;
102 
103  Eigen::Vector4d normal_calc_centroid;
104  Eigen::Matrix3d normal_calc_covariance;
105  pcl::compute3DCentroid (normal_calc_cloud, normal_calc_centroid);
106  pcl::computeCovarianceMatrixNormalized (normal_calc_cloud, normal_calc_centroid, normal_calc_covariance);
107 
108  // Need to set -1 here. See eigen33 for explanations.
109  Eigen::Vector3d::Scalar eigen_value;
110  Eigen::Vector3d plane_params;
111  pcl::eigen33 (normal_calc_covariance, eigen_value, plane_params);
112  float theta_x = fabsf (static_cast<float> (plane_params.dot (x_axis_)));
113  float theta_y = fabsf (static_cast<float> (plane_params.dot (y_axis_)));
114  float theta_z = fabsf (static_cast<float> (plane_params.dot (z_axis_)));
115 
116  // Check for degenerate cases of each projection
117  // We must avoid projections in which the plane projects as a line
118  if (theta_z > projection_angle_thresh_)
119  {
120  xz_proj_safe = false;
121  yz_proj_safe = false;
122  }
123  if (theta_x > projection_angle_thresh_)
124  {
125  xz_proj_safe = false;
126  xy_proj_safe = false;
127  }
128  if (theta_y > projection_angle_thresh_)
129  {
130  xy_proj_safe = false;
131  yz_proj_safe = false;
132  }
133 
134  // True if qhull should free points in qh_freeqhull() or reallocation
135  boolT ismalloc = True;
136  // output from qh_produce_output(), use NULL to skip qh_produce_output()
137  FILE *outfile = NULL;
138 
139 #ifndef HAVE_QHULL_2011
140  if (compute_area_)
141  outfile = stderr;
142 #endif
143 
144  // option flags for qhull, see qh_opt.htm
145  const char* flags = qhull_flags.c_str ();
146  // error messages from qhull code
147  FILE *errfile = stderr;
148 
149  // Array of coordinates for each point
150  coordT *points = reinterpret_cast<coordT*> (calloc (indices_->size () * dimension, sizeof (coordT)));
151 
152  // Build input data, using appropriate projection
153  int j = 0;
154  if (xy_proj_safe)
155  {
156  for (size_t i = 0; i < indices_->size (); ++i, j+=dimension)
157  {
158  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].x);
159  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].y);
160  }
161  }
162  else if (yz_proj_safe)
163  {
164  for (size_t i = 0; i < indices_->size (); ++i, j+=dimension)
165  {
166  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].y);
167  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].z);
168  }
169  }
170  else if (xz_proj_safe)
171  {
172  for (size_t i = 0; i < indices_->size (); ++i, j+=dimension)
173  {
174  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].x);
175  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].z);
176  }
177  }
178  else
179  {
180  // This should only happen if we had invalid input
181  PCL_ERROR ("[pcl::%s::performReconstruction2D] Invalid input!\n", getClassName ().c_str ());
182  }
183 
184  // Compute convex hull
185  int exitcode = qh_new_qhull (dimension, static_cast<int> (indices_->size ()), points, ismalloc, const_cast<char*> (flags), outfile, errfile);
186 #ifdef HAVE_QHULL_2011
187  if (compute_area_)
188  {
189  qh_prepare_output();
190  }
191 #endif
192 
193  // 0 if no error from qhull or it doesn't find any vertices
194  if (exitcode != 0 || qh num_vertices == 0)
195  {
196  PCL_ERROR ("[pcl::%s::performReconstrution2D] ERROR: qhull was unable to compute a convex hull for the given point cloud (%lu)!\n", getClassName ().c_str (), indices_->size ());
197 
198  hull.points.resize (0);
199  hull.width = hull.height = 0;
200  polygons.resize (0);
201 
202  qh_freeqhull (!qh_ALL);
203  int curlong, totlong;
204  qh_memfreeshort (&curlong, &totlong);
205 
206  return;
207  }
208 
209  // Qhull returns the area in volume for 2D
210  if (compute_area_)
211  {
212  total_area_ = qh totvol;
213  total_volume_ = 0.0;
214  }
215 
216  int num_vertices = qh num_vertices;
217  hull.points.resize (num_vertices);
218  memset (&hull.points[0], static_cast<int> (hull.points.size ()), sizeof (PointInT));
219 
220  vertexT * vertex;
221  int i = 0;
222 
223  std::vector<std::pair<int, Eigen::Vector4f>, Eigen::aligned_allocator<std::pair<int, Eigen::Vector4f> > > idx_points (num_vertices);
224  idx_points.resize (hull.points.size ());
225  memset (&idx_points[0], static_cast<int> (hull.points.size ()), sizeof (std::pair<int, Eigen::Vector4f>));
226 
227  FORALLvertices
228  {
229  hull.points[i] = input_->points[(*indices_)[qh_pointid (vertex->point)]];
230  idx_points[i].first = qh_pointid (vertex->point);
231  ++i;
232  }
233 
234  // Sort
235  Eigen::Vector4f centroid;
236  pcl::compute3DCentroid (hull, centroid);
237  if (xy_proj_safe)
238  {
239  for (size_t j = 0; j < hull.points.size (); j++)
240  {
241  idx_points[j].second[0] = hull.points[j].x - centroid[0];
242  idx_points[j].second[1] = hull.points[j].y - centroid[1];
243  }
244  }
245  else if (yz_proj_safe)
246  {
247  for (size_t j = 0; j < hull.points.size (); j++)
248  {
249  idx_points[j].second[0] = hull.points[j].y - centroid[1];
250  idx_points[j].second[1] = hull.points[j].z - centroid[2];
251  }
252  }
253  else if (xz_proj_safe)
254  {
255  for (size_t j = 0; j < hull.points.size (); j++)
256  {
257  idx_points[j].second[0] = hull.points[j].x - centroid[0];
258  idx_points[j].second[1] = hull.points[j].z - centroid[2];
259  }
260  }
261  std::sort (idx_points.begin (), idx_points.end (), comparePoints2D);
262 
263  polygons.resize (1);
264  polygons[0].vertices.resize (hull.points.size ());
265 
266  hull_indices_.header = input_->header;
267  hull_indices_.indices.clear ();
268  hull_indices_.indices.reserve (hull.points.size ());
269 
270  for (int j = 0; j < static_cast<int> (hull.points.size ()); j++)
271  {
272  hull_indices_.indices.push_back ((*indices_)[idx_points[j].first]);
273  hull.points[j] = input_->points[(*indices_)[idx_points[j].first]];
274  polygons[0].vertices[j] = static_cast<unsigned int> (j);
275  }
276 
277  qh_freeqhull (!qh_ALL);
278  int curlong, totlong;
279  qh_memfreeshort (&curlong, &totlong);
280 
281  hull.width = static_cast<uint32_t> (hull.points.size ());
282  hull.height = 1;
283  hull.is_dense = false;
284  return;
285 }
286 
287 #ifdef __GNUC__
288 #pragma GCC diagnostic ignored "-Wold-style-cast"
289 #endif
290 //////////////////////////////////////////////////////////////////////////
291 template <typename PointInT> void
293  PointCloud &hull, std::vector<pcl::Vertices> &polygons, bool fill_polygon_data)
294 {
295  int dimension = 3;
296 
297  // True if qhull should free points in qh_freeqhull() or reallocation
298  boolT ismalloc = True;
299  // output from qh_produce_output(), use NULL to skip qh_produce_output()
300  FILE *outfile = NULL;
301 
302 #ifndef HAVE_QHULL_2011
303  if (compute_area_)
304  outfile = stderr;
305 #endif
306 
307  // option flags for qhull, see qh_opt.htm
308  const char *flags = qhull_flags.c_str ();
309  // error messages from qhull code
310  FILE *errfile = stderr;
311 
312  // Array of coordinates for each point
313  coordT *points = reinterpret_cast<coordT*> (calloc (indices_->size () * dimension, sizeof (coordT)));
314 
315  int j = 0;
316  for (size_t i = 0; i < indices_->size (); ++i, j+=dimension)
317  {
318  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].x);
319  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].y);
320  points[j + 2] = static_cast<coordT> (input_->points[(*indices_)[i]].z);
321  }
322 
323  // Compute convex hull
324  int exitcode = qh_new_qhull (dimension, static_cast<int> (indices_->size ()), points, ismalloc, const_cast<char*> (flags), outfile, errfile);
325 #ifdef HAVE_QHULL_2011
326  if (compute_area_)
327  {
328  qh_prepare_output();
329  }
330 #endif
331 
332  // 0 if no error from qhull
333  if (exitcode != 0)
334  {
335  PCL_ERROR ("[pcl::%s::performReconstrution3D] ERROR: qhull was unable to compute a convex hull for the given point cloud (%lu)!\n", getClassName ().c_str (), input_->points.size ());
336 
337  hull.points.resize (0);
338  hull.width = hull.height = 0;
339  polygons.resize (0);
340 
341  qh_freeqhull (!qh_ALL);
342  int curlong, totlong;
343  qh_memfreeshort (&curlong, &totlong);
344 
345  return;
346  }
347 
348  qh_triangulate ();
349 
350  int num_facets = qh num_facets;
351 
352  int num_vertices = qh num_vertices;
353  hull.points.resize (num_vertices);
354 
355  vertexT * vertex;
356  int i = 0;
357  // Max vertex id
358  unsigned int max_vertex_id = 0;
359  FORALLvertices
360  {
361  if (vertex->id + 1 > max_vertex_id)
362  max_vertex_id = vertex->id + 1;
363  }
364 
365  ++max_vertex_id;
366  std::vector<int> qhid_to_pcidx (max_vertex_id);
367 
368  hull_indices_.header = input_->header;
369  hull_indices_.indices.clear ();
370  hull_indices_.indices.reserve (num_vertices);
371 
372  FORALLvertices
373  {
374  // Add vertices to hull point_cloud and store index
375  hull_indices_.indices.push_back ((*indices_)[qh_pointid (vertex->point)]);
376  hull.points[i] = input_->points[(*indices_)[hull_indices_.indices.back ()]];
377 
378  qhid_to_pcidx[vertex->id] = i; // map the vertex id of qhull to the point cloud index
379  ++i;
380  }
381 
382  if (compute_area_)
383  {
384  total_area_ = qh totarea;
385  total_volume_ = qh totvol;
386  }
387 
388  if (fill_polygon_data)
389  {
390  polygons.resize (num_facets);
391  int dd = 0;
392 
393  facetT * facet;
394  FORALLfacets
395  {
396  polygons[dd].vertices.resize (3);
397 
398  // Needed by FOREACHvertex_i_
399  int vertex_n, vertex_i;
400  FOREACHvertex_i_ ((*facet).vertices)
401  //facet_vertices.vertices.push_back (qhid_to_pcidx[vertex->id]);
402  polygons[dd].vertices[vertex_i] = qhid_to_pcidx[vertex->id];
403  ++dd;
404  }
405  }
406  // Deallocates memory (also the points)
407  qh_freeqhull (!qh_ALL);
408  int curlong, totlong;
409  qh_memfreeshort (&curlong, &totlong);
410 
411  hull.width = static_cast<uint32_t> (hull.points.size ());
412  hull.height = 1;
413  hull.is_dense = false;
414 }
415 #ifdef __GNUC__
416 #pragma GCC diagnostic warning "-Wold-style-cast"
417 #endif
418 
419 //////////////////////////////////////////////////////////////////////////
420 template <typename PointInT> void
421 pcl::ConvexHull<PointInT>::performReconstruction (PointCloud &hull, std::vector<pcl::Vertices> &polygons,
422  bool fill_polygon_data)
423 {
424  if (dimension_ == 0)
425  calculateInputDimension ();
426  if (dimension_ == 2)
427  performReconstruction2D (hull, polygons, fill_polygon_data);
428  else if (dimension_ == 3)
429  performReconstruction3D (hull, polygons, fill_polygon_data);
430  else
431  PCL_ERROR ("[pcl::%s::performReconstruction] Error: invalid input dimension requested: %d\n",getClassName ().c_str (),dimension_);
432 }
433 
434 //////////////////////////////////////////////////////////////////////////
435 template <typename PointInT> void
437 {
438  points.header = input_->header;
439  if (!initCompute () || input_->points.empty () || indices_->empty ())
440  {
441  points.points.clear ();
442  return;
443  }
444 
445  // Perform the actual surface reconstruction
446  std::vector<pcl::Vertices> polygons;
447  performReconstruction (points, polygons, false);
448 
449  points.width = static_cast<uint32_t> (points.points.size ());
450  points.height = 1;
451  points.is_dense = true;
452 
453  deinitCompute ();
454 }
455 
456 
457 //////////////////////////////////////////////////////////////////////////
458 template <typename PointInT> void
460 {
461  // Perform reconstruction
462  pcl::PointCloud<PointInT> hull_points;
463  performReconstruction (hull_points, output.polygons, true);
464 
465  // Convert the PointCloud into a PCLPointCloud2
466  pcl::toPCLPointCloud2 (hull_points, output.cloud);
467 }
468 
469 //////////////////////////////////////////////////////////////////////////
470 template <typename PointInT> void
471 pcl::ConvexHull<PointInT>::performReconstruction (std::vector<pcl::Vertices> &polygons)
472 {
473  pcl::PointCloud<PointInT> hull_points;
474  performReconstruction (hull_points, polygons, true);
475 }
476 
477 //////////////////////////////////////////////////////////////////////////
478 template <typename PointInT> void
479 pcl::ConvexHull<PointInT>::reconstruct (PointCloud &points, std::vector<pcl::Vertices> &polygons)
480 {
481  points.header = input_->header;
482  if (!initCompute () || input_->points.empty () || indices_->empty ())
483  {
484  points.points.clear ();
485  return;
486  }
487 
488  // Perform the actual surface reconstruction
489  performReconstruction (points, polygons, true);
490 
491  points.width = static_cast<uint32_t> (points.points.size ());
492  points.height = 1;
493  points.is_dense = true;
494 
495  deinitCompute ();
496 }
497 //////////////////////////////////////////////////////////////////////////
498 template <typename PointInT> void
500 {
501  hull_point_indices = hull_indices_;
502 }
503 
504 #define PCL_INSTANTIATE_ConvexHull(T) template class PCL_EXPORTS pcl::ConvexHull<T>;
505 
506 #endif // PCL_SURFACE_IMPL_CONVEX_HULL_H_
507 #endif
void performReconstruction(PointCloud &points, std::vector< pcl::Vertices > &polygons, bool fill_polygon_data=false)
The actual reconstruction method.
struct pcl::PointXYZIEdge EIGEN_ALIGN16
uint32_t width
The point cloud width (if organized as an image-structure).
Definition: point_cloud.h:413
pcl::PCLHeader header
The point cloud header.
Definition: point_cloud.h:407
void calculateInputDimension()
Automatically determines the dimension of input data - 2D or 3D.
Definition: convex_hull.hpp:57
std::vector< PointT, Eigen::aligned_allocator< PointT > > points
The point data.
Definition: point_cloud.h:410
void performReconstruction3D(PointCloud &points, std::vector< pcl::Vertices > &polygons, bool fill_polygon_data=false)
The reconstruction method for 3D data.
void performReconstruction2D(PointCloud &points, std::vector< pcl::Vertices > &polygons, bool fill_polygon_data=false)
The reconstruction method for 2D data.
Definition: convex_hull.hpp:76
void getHullPointIndices(pcl::PointIndices &hull_point_indices) const
Retrieve the indices of the input point cloud that for the convex hull.
std::vector< ::pcl::Vertices > polygons
Definition: PolygonMesh.h:24
void eigen33(const Matrix &mat, typename Matrix::Scalar &eigenvalue, Vector &eigenvector)
determines the eigenvector and eigenvalue of the smallest eigenvalue of the symmetric positive semi d...
Definition: eigen.hpp:251
void reconstruct(PointCloud &points, std::vector< pcl::Vertices > &polygons)
Compute a convex hull for all points given.
::pcl::PCLPointCloud2 cloud
Definition: PolygonMesh.h:22
void toPCLPointCloud2(const pcl::PointCloud< PointT > &cloud, pcl::PCLPointCloud2 &msg)
Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
Definition: conversions.h:242
unsigned int computeCovarianceMatrixNormalized(const pcl::PointCloud< PointT > &cloud, const Eigen::Matrix< Scalar, 4, 1 > &centroid, Eigen::Matrix< Scalar, 3, 3 > &covariance_matrix)
Compute normalized the 3x3 covariance matrix of a given set of points.
Definition: centroid.hpp:251
unsigned int compute3DCentroid(ConstCloudIterator< PointT > &cloud_iterator, Eigen::Matrix< Scalar, 4, 1 > &centroid)
Compute the 3D (X-Y-Z) centroid of a set of points and return it as a 3D vector.
Definition: centroid.hpp:50
bool is_dense
True if no points are invalid (e.g., have NaN or Inf values).
Definition: point_cloud.h:418
bool comparePoints2D(const std::pair< int, Eigen::Vector4f > &p1, const std::pair< int, Eigen::Vector4f > &p2)
Sort 2D points in a vector structure.
Definition: convex_hull.h:59
uint32_t height
The point cloud height (if organized as an image-structure).
Definition: point_cloud.h:415