Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
convex_hull.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-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: convex_hull.hpp 6214 2012-07-06 19:31:29Z rusu $
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 
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 
56 template <typename PointInT> void
57 pcl::ConvexHull<PointInT>::calculateInputDimension ()
58 {
59  PCL_WARN ("[pcl::%s::calculateInputDimension] WARNING: Input dimension not specified. Automatically determining input dimension.\n", getClassName ().c_str ());
60  EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
61  Eigen::Vector4f xyz_centroid;
62  computeMeanAndCovarianceMatrix (*input_, *indices_, covariance_matrix, xyz_centroid);
63 
64  EIGEN_ALIGN16 Eigen::Vector3f eigen_values;
65  pcl::eigen33 (covariance_matrix, eigen_values);
66 
67  if (eigen_values[0] / eigen_values[2] < 1.0e-3)
68  dimension_ = 2;
69  else
70  dimension_ = 3;
71 }
72 
74 template <typename PointInT> void
75 pcl::ConvexHull<PointInT>::performReconstruction2D (PointCloud &hull, std::vector<pcl::Vertices> &polygons,
76  bool)
77 {
78  int dimension = 2;
79  bool xy_proj_safe = true;
80  bool yz_proj_safe = true;
81  bool xz_proj_safe = true;
82 
83  // Check the input's normal to see which projection to use
84  PointInT p0 = input_->points[0];
85  PointInT p1 = input_->points[indices_->size () - 1];
86  PointInT p2 = input_->points[indices_->size () / 2];
87  Eigen::Array4f dy1dy2 = (p1.getArray4fMap () - p0.getArray4fMap ()) / (p2.getArray4fMap () - p0.getArray4fMap ());
88  while (!( (dy1dy2[0] != dy1dy2[1]) || (dy1dy2[2] != dy1dy2[1]) ) )
89  {
90  p0 = input_->points[rand () % indices_->size ()];
91  p1 = input_->points[rand () % indices_->size ()];
92  p2 = input_->points[rand () % indices_->size ()];
93  dy1dy2 = (p1.getArray4fMap () - p0.getArray4fMap ()) / (p2.getArray4fMap () - p0.getArray4fMap ());
94  }
95 
96  pcl::PointCloud<PointInT> normal_calc_cloud;
97  normal_calc_cloud.points.resize (3);
98  normal_calc_cloud.points[0] = p0;
99  normal_calc_cloud.points[1] = p1;
100  normal_calc_cloud.points[2] = p2;
101 
102  Eigen::Vector4f normal_calc_centroid;
103  Eigen::Matrix3f normal_calc_covariance;
104  pcl::computeMeanAndCovarianceMatrix (normal_calc_cloud, normal_calc_covariance, normal_calc_centroid);
105  // Need to set -1 here. See eigen33 for explanations.
106  Eigen::Vector3f::Scalar eigen_value;
107  Eigen::Vector3f plane_params;
108  pcl::eigen33 (normal_calc_covariance, eigen_value, plane_params);
109  float theta_x = fabsf (plane_params.dot (x_axis_));
110  float theta_y = fabsf (plane_params.dot (y_axis_));
111  float theta_z = fabsf (plane_params.dot (z_axis_));
112 
113  // Check for degenerate cases of each projection
114  // We must avoid projections in which the plane projects as a line
115  if (theta_z > projection_angle_thresh_)
116  {
117  xz_proj_safe = false;
118  yz_proj_safe = false;
119  }
120  if (theta_x > projection_angle_thresh_)
121  {
122  xz_proj_safe = false;
123  xy_proj_safe = false;
124  }
125  if (theta_y > projection_angle_thresh_)
126  {
127  xy_proj_safe = false;
128  yz_proj_safe = false;
129  }
130 
131  // True if qhull should free points in qh_freeqhull() or reallocation
132  boolT ismalloc = True;
133  // output from qh_produce_output(), use NULL to skip qh_produce_output()
134  FILE *outfile = NULL;
135 
136  if (compute_area_)
137  outfile = stderr;
138 
139  // option flags for qhull, see qh_opt.htm
140  const char* flags = qhull_flags.c_str ();
141  // error messages from qhull code
142  FILE *errfile = stderr;
143 
144  // Array of coordinates for each point
145  coordT *points = reinterpret_cast<coordT*> (calloc (indices_->size () * dimension, sizeof (coordT)));
146 
147  // Build input data, using appropriate projection
148  int j = 0;
149  if (xy_proj_safe)
150  {
151  for (size_t i = 0; i < indices_->size (); ++i, j+=dimension)
152  {
153  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].x);
154  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].y);
155  }
156  }
157  else if (yz_proj_safe)
158  {
159  for (size_t i = 0; i < input_->points.size (); ++i, j+=dimension)
160  {
161  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].y);
162  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].z);
163  }
164  }
165  else if (xz_proj_safe)
166  {
167  for (size_t i = 0; i < input_->points.size (); ++i, j+=dimension)
168  {
169  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].x);
170  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].z);
171  }
172  }
173  else
174  {
175  // This should only happen if we had invalid input
176  PCL_ERROR ("[pcl::%s::performReconstruction2D] Invalid input!\n", getClassName ().c_str ());
177  }
178 
179  // Compute convex hull
180  int exitcode = qh_new_qhull (dimension, static_cast<int> (indices_->size ()), points, ismalloc, const_cast<char*> (flags), outfile, errfile);
181 
182  // 0 if no error from qhull
183  if (exitcode != 0)
184  {
185  PCL_ERROR ("[pcl::%s::performReconstrution2D] ERROR: qhull was unable to compute a convex hull for the given point cloud (%zu)!\n", getClassName ().c_str (), indices_->size ());
186 
187  hull.points.resize (0);
188  hull.width = hull.height = 0;
189  polygons.resize (0);
190 
191  qh_freeqhull (!qh_ALL);
192  int curlong, totlong;
193  qh_memfreeshort (&curlong, &totlong);
194 
195  return;
196  }
197 
198  // Qhull returns the area in volume for 2D
199  if (compute_area_)
200  {
201  total_area_ = qh totvol;
202  total_volume_ = 0.0;
203  }
204 
205  int num_vertices = qh num_vertices;
206  hull.points.resize (num_vertices);
207  memset (&hull.points[0], static_cast<int> (hull.points.size ()), sizeof (PointInT));
208 
209  vertexT * vertex;
210  int i = 0;
211 
212  std::vector<std::pair<int, Eigen::Vector4f>, Eigen::aligned_allocator<std::pair<int, Eigen::Vector4f> > > idx_points (num_vertices);
213  idx_points.resize (hull.points.size ());
214  memset (&idx_points[0], static_cast<int> (hull.points.size ()), sizeof (std::pair<int, Eigen::Vector4f>));
215 
216  FORALLvertices
217  {
218  hull.points[i] = input_->points[(*indices_)[qh_pointid (vertex->point)]];
219  idx_points[i].first = qh_pointid (vertex->point);
220  ++i;
221  }
222 
223  // Sort
224  Eigen::Vector4f centroid;
225  pcl::compute3DCentroid (hull, centroid);
226  if (xy_proj_safe)
227  {
228  for (size_t j = 0; j < hull.points.size (); j++)
229  {
230  idx_points[j].second[0] = hull.points[j].x - centroid[0];
231  idx_points[j].second[1] = hull.points[j].y - centroid[1];
232  }
233  }
234  else if (yz_proj_safe)
235  {
236  for (size_t j = 0; j < hull.points.size (); j++)
237  {
238  idx_points[j].second[0] = hull.points[j].y - centroid[1];
239  idx_points[j].second[1] = hull.points[j].z - centroid[2];
240  }
241  }
242  else if (xz_proj_safe)
243  {
244  for (size_t j = 0; j < hull.points.size (); j++)
245  {
246  idx_points[j].second[0] = hull.points[j].x - centroid[0];
247  idx_points[j].second[1] = hull.points[j].z - centroid[2];
248  }
249  }
250  std::sort (idx_points.begin (), idx_points.end (), comparePoints2D);
251 
252  polygons.resize (1);
253  polygons[0].vertices.resize (hull.points.size () + 1);
254 
255  for (int j = 0; j < static_cast<int> (hull.points.size ()); j++)
256  {
257  hull.points[j] = input_->points[(*indices_)[idx_points[j].first]];
258  polygons[0].vertices[j] = static_cast<unsigned int> (j);
259  }
260  polygons[0].vertices[hull.points.size ()] = 0;
261 
262  qh_freeqhull (!qh_ALL);
263  int curlong, totlong;
264  qh_memfreeshort (&curlong, &totlong);
265 
266  hull.width = static_cast<uint32_t> (hull.points.size ());
267  hull.height = 1;
268  hull.is_dense = false;
269  return;
270 }
271 
272 #ifdef __GNUC__
273 #pragma GCC diagnostic ignored "-Wold-style-cast"
274 #endif
275 template <typename PointInT> void
277 pcl::ConvexHull<PointInT>::performReconstruction3D (
278  PointCloud &hull, std::vector<pcl::Vertices> &polygons, bool fill_polygon_data)
279 {
280  int dimension = 3;
281 
282  // True if qhull should free points in qh_freeqhull() or reallocation
283  boolT ismalloc = True;
284  // output from qh_produce_output(), use NULL to skip qh_produce_output()
285  FILE *outfile = NULL;
286 
287  if (compute_area_)
288  outfile = stderr;
289 
290  // option flags for qhull, see qh_opt.htm
291  const char *flags = qhull_flags.c_str ();
292  // error messages from qhull code
293  FILE *errfile = stderr;
294 
295  // Array of coordinates for each point
296  coordT *points = reinterpret_cast<coordT*> (calloc (indices_->size () * dimension, sizeof (coordT)));
297 
298  int j = 0;
299  for (size_t i = 0; i < indices_->size (); ++i, j+=dimension)
300  {
301  points[j + 0] = static_cast<coordT> (input_->points[(*indices_)[i]].x);
302  points[j + 1] = static_cast<coordT> (input_->points[(*indices_)[i]].y);
303  points[j + 2] = static_cast<coordT> (input_->points[(*indices_)[i]].z);
304  }
305 
306  // Compute convex hull
307  int exitcode = qh_new_qhull (dimension, static_cast<int> (indices_->size ()), points, ismalloc, const_cast<char*> (flags), outfile, errfile);
308 
309  // 0 if no error from qhull
310  if (exitcode != 0)
311  {
312  PCL_ERROR ("[pcl::%s::performReconstrution3D] ERROR: qhull was unable to compute a convex hull for the given point cloud (%zu)!\n", getClassName ().c_str (), input_->points.size ());
313 
314  hull.points.resize (0);
315  hull.width = hull.height = 0;
316  polygons.resize (0);
317 
318  qh_freeqhull (!qh_ALL);
319  int curlong, totlong;
320  qh_memfreeshort (&curlong, &totlong);
321 
322  return;
323  }
324 
325  qh_triangulate ();
326 
327  int num_facets = qh num_facets;
328 
329  int num_vertices = qh num_vertices;
330  hull.points.resize (num_vertices);
331 
332  vertexT * vertex;
333  int i = 0;
334  // Max vertex id
335  int max_vertex_id = 0;
336  FORALLvertices
337  {
338  if (vertex->id + 1 > max_vertex_id)
339  max_vertex_id = vertex->id + 1;
340  }
341 
342  ++max_vertex_id;
343  std::vector<int> qhid_to_pcidx (max_vertex_id);
344 
345  FORALLvertices
346  {
347  // Add vertices to hull point_cloud
348  hull.points[i] = input_->points[(*indices_)[qh_pointid (vertex->point)]];
349 
350  qhid_to_pcidx[vertex->id] = i; // map the vertex id of qhull to the point cloud index
351  ++i;
352  }
353 
354  if (compute_area_)
355  {
356  total_area_ = qh totarea;
357  total_volume_ = qh totvol;
358  }
359 
360  if (fill_polygon_data)
361  {
362  polygons.resize (num_facets);
363  int dd = 0;
364 
365  facetT * facet;
366  FORALLfacets
367  {
368  polygons[dd].vertices.resize (3);
369 
370  // Needed by FOREACHvertex_i_
371  int vertex_n, vertex_i;
372  FOREACHvertex_i_ ((*facet).vertices)
373  //facet_vertices.vertices.push_back (qhid_to_pcidx[vertex->id]);
374  polygons[dd].vertices[vertex_i] = qhid_to_pcidx[vertex->id];
375  ++dd;
376  }
377  }
378  // Deallocates memory (also the points)
379  qh_freeqhull (!qh_ALL);
380  int curlong, totlong;
381  qh_memfreeshort (&curlong, &totlong);
382 
383  hull.width = static_cast<uint32_t> (hull.points.size ());
384  hull.height = 1;
385  hull.is_dense = false;
386 }
387 #ifdef __GNUC__
388 #pragma GCC diagnostic warning "-Wold-style-cast"
389 #endif
390 
392 template <typename PointInT> void
393 pcl::ConvexHull<PointInT>::performReconstruction (PointCloud &hull, std::vector<pcl::Vertices> &polygons,
394  bool fill_polygon_data)
395 {
396  if (dimension_ == 0)
397  calculateInputDimension ();
398  if (dimension_ == 2)
399  performReconstruction2D (hull, polygons, fill_polygon_data);
400  else if (dimension_ == 3)
401  performReconstruction3D (hull, polygons, fill_polygon_data);
402  else
403  PCL_ERROR ("[pcl::%s::performReconstruction] Error: invalid input dimension requested: %d\n",getClassName ().c_str (),dimension_);
404 }
405 
407 template <typename PointInT> void
408 pcl::ConvexHull<PointInT>::reconstruct (PointCloud &output)
409 {
410  output.header = input_->header;
411  if (!initCompute () || input_->points.empty () || indices_->empty ())
412  {
413  output.points.clear ();
414  return;
415  }
416 
417  // Perform the actual surface reconstruction
418  std::vector<pcl::Vertices> polygons;
419  performReconstruction (output, polygons, false);
420 
421  output.width = static_cast<uint32_t> (output.points.size ());
422  output.height = 1;
423  output.is_dense = true;
424 
425  deinitCompute ();
426 }
427 
428 
430 template <typename PointInT> void
431 pcl::ConvexHull<PointInT>::performReconstruction (PolygonMesh &output)
432 {
433  // Perform reconstruction
434  pcl::PointCloud<PointInT> hull_points;
435  performReconstruction (hull_points, output.polygons, true);
436 
437  // Convert the PointCloud into a PointCloud2
438  pcl::toROSMsg (hull_points, output.cloud);
439 }
440 
442 template <typename PointInT> void
443 pcl::ConvexHull<PointInT>::performReconstruction (std::vector<pcl::Vertices> &polygons)
444 {
445  pcl::PointCloud<PointInT> hull_points;
446  performReconstruction (hull_points, polygons, true);
447 }
448 
450 template <typename PointInT> void
451 pcl::ConvexHull<PointInT>::reconstruct (PointCloud &points, std::vector<pcl::Vertices> &polygons)
452 {
453  points.header = input_->header;
454  if (!initCompute () || input_->points.empty () || indices_->empty ())
455  {
456  points.points.clear ();
457  return;
458  }
459 
460  // Perform the actual surface reconstruction
461  performReconstruction (points, polygons, true);
462 
463  points.width = static_cast<uint32_t> (points.points.size ());
464  points.height = 1;
465  points.is_dense = true;
466 
467  deinitCompute ();
468 }
469 
470 #define PCL_INSTANTIATE_ConvexHull(T) template class PCL_EXPORTS pcl::ConvexHull<T>;
471 
472 #endif // PCL_SURFACE_IMPL_CONVEX_HULL_H_
473 #endif