concave_hull.hpp
Go to the documentation of this file.
00001 
00040 #include <pcl/pcl_config.h>
00041 #ifdef HAVE_QHULL
00042 
00043 #ifndef PCL_SURFACE_IMPL_CONCAVE_HULL_H_
00044 #define PCL_SURFACE_IMPL_CONCAVE_HULL_H_
00045 
00046 #include <map>
00047 #include <pcl/surface/concave_hull.h>
00048 #include <pcl/common/common.h>
00049 #include <pcl/common/eigen.h>
00050 #include <pcl/common/centroid.h>
00051 #include <pcl/common/transforms.h>
00052 #include <pcl/kdtree/kdtree_flann.h>
00053 #include <pcl/common/io.h>
00054 #include <stdio.h>
00055 #include <stdlib.h>
00056 #include <pcl/surface/qhull.h>
00057 
00059 template <typename PointInT> void
00060 pcl::ConcaveHull<PointInT>::reconstruct (PointCloud &output)
00061 {
00062   output.header = input_->header;
00063   if (alpha_ <= 0)
00064   {
00065     PCL_ERROR ("[pcl::%s::reconstruct] Alpha parameter must be set to a positive number!\n", getClassName ().c_str ());
00066     output.points.clear ();
00067     return;
00068   }
00069 
00070   if (!initCompute ())
00071   {
00072     output.points.clear ();
00073     return;
00074   }
00075 
00076   // Perform the actual surface reconstruction
00077   std::vector<pcl::Vertices> polygons;
00078   performReconstruction (output, polygons);
00079 
00080   output.width = static_cast<uint32_t> (output.points.size ());
00081   output.height = 1;
00082   output.is_dense = true;
00083 
00084   deinitCompute ();
00085 }
00086 
00088 template <typename PointInT> void
00089 pcl::ConcaveHull<PointInT>::reconstruct (PointCloud &output, std::vector<pcl::Vertices> &polygons)
00090 {
00091   output.header = input_->header;
00092   if (alpha_ <= 0)
00093   {
00094     PCL_ERROR ("[pcl::%s::reconstruct] Alpha parameter must be set to a positive number!\n", getClassName ().c_str ());
00095     output.points.clear ();
00096     return;
00097   }
00098 
00099   if (!initCompute ())
00100   {
00101     output.points.clear ();
00102     return;
00103   }
00104 
00105   // Perform the actual surface reconstruction
00106   performReconstruction (output, polygons);
00107 
00108   output.width = static_cast<uint32_t> (output.points.size ());
00109   output.height = 1;
00110   output.is_dense = true;
00111 
00112   deinitCompute ();
00113 }
00114 
00115 #ifdef __GNUC__
00116 #pragma GCC diagnostic ignored "-Wold-style-cast"
00117 #endif
00118 
00119 template <typename PointInT> void
00120 pcl::ConcaveHull<PointInT>::performReconstruction (PointCloud &alpha_shape, std::vector<pcl::Vertices> &polygons)
00121 {
00122   EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
00123   Eigen::Vector4f xyz_centroid;
00124   computeMeanAndCovarianceMatrix (*input_, *indices_, covariance_matrix, xyz_centroid);
00125 
00126   // Check if the covariance matrix is finite or not.
00127   for (int i = 0; i < 3; ++i)
00128     for (int j = 0; j < 3; ++j)
00129       if (!pcl_isfinite (covariance_matrix.coeffRef (i, j)))
00130           return;
00131 
00132   EIGEN_ALIGN16 Eigen::Vector3f eigen_values;
00133   EIGEN_ALIGN16 Eigen::Matrix3f eigen_vectors;
00134   pcl::eigen33 (covariance_matrix, eigen_vectors, eigen_values);
00135 
00136   Eigen::Affine3f transform1;
00137   transform1.setIdentity ();
00138 
00139   // If no input dimension is specified, determine automatically
00140   if (dim_ == 0)
00141   {
00142     PCL_WARN ("[pcl::%s] WARNING: Input dimension not specified.  Automatically determining input dimension.\n", getClassName ().c_str ());
00143     if (eigen_values[0] / eigen_values[2] < 1.0e-3)
00144       dim_ = 2;
00145     else
00146       dim_ = 3;
00147   } 
00148 
00149   if (dim_ == 2)
00150   {
00151     // we have points laying on a plane, using 2d convex hull
00152     // compute transformation bring eigen_vectors.col(i) to z-axis
00153 
00154     transform1 (2, 0) = eigen_vectors (0, 0);
00155     transform1 (2, 1) = eigen_vectors (1, 0);
00156     transform1 (2, 2) = eigen_vectors (2, 0);
00157 
00158     transform1 (1, 0) = eigen_vectors (0, 1);
00159     transform1 (1, 1) = eigen_vectors (1, 1);
00160     transform1 (1, 2) = eigen_vectors (2, 1);
00161     transform1 (0, 0) = eigen_vectors (0, 2);
00162     transform1 (0, 1) = eigen_vectors (1, 2);
00163     transform1 (0, 2) = eigen_vectors (2, 2);
00164   }
00165   else
00166   {
00167     transform1.setIdentity ();
00168   }
00169   
00170   PointCloud cloud_transformed;
00171   pcl::demeanPointCloud (*input_, *indices_, xyz_centroid, cloud_transformed);
00172   pcl::transformPointCloud (cloud_transformed, cloud_transformed, transform1);
00173 
00174   // True if qhull should free points in qh_freeqhull() or reallocation
00175   boolT ismalloc = True;
00176   // option flags for qhull, see qh_opt.htm
00177   char flags[] = "qhull d QJ";
00178   // output from qh_produce_output(), use NULL to skip qh_produce_output()
00179   FILE *outfile = NULL;
00180   // error messages from qhull code
00181   FILE *errfile = stderr;
00182   // 0 if no error from qhull
00183   int exitcode;
00184 
00185   // Array of coordinates for each point
00186   coordT *points = reinterpret_cast<coordT*> (calloc (cloud_transformed.points.size () * dim_, sizeof(coordT)));
00187 
00188   for (size_t i = 0; i < cloud_transformed.points.size (); ++i)
00189   {
00190     points[i * dim_ + 0] = static_cast<coordT> (cloud_transformed.points[i].x);
00191     points[i * dim_ + 1] = static_cast<coordT> (cloud_transformed.points[i].y);
00192 
00193     if (dim_ > 2)
00194       points[i * dim_ + 2] = static_cast<coordT> (cloud_transformed.points[i].z);
00195   }
00196 
00197   // Compute concave hull
00198   exitcode = qh_new_qhull (dim_, static_cast<int> (cloud_transformed.points.size ()), points, ismalloc, flags, outfile, errfile);
00199 
00200   if (exitcode != 0)
00201   {
00202     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 ());
00203 
00204     //check if it fails because of NaN values...
00205     if (!cloud_transformed.is_dense)
00206     {
00207       bool NaNvalues = false;
00208       for (size_t i = 0; i < cloud_transformed.size (); ++i)
00209       {
00210         if (!pcl_isfinite (cloud_transformed.points[i].x) ||
00211             !pcl_isfinite (cloud_transformed.points[i].y) ||
00212             !pcl_isfinite (cloud_transformed.points[i].z))
00213         {
00214           NaNvalues = true;
00215           break;
00216         }
00217       }
00218 
00219       if (NaNvalues)
00220         PCL_ERROR ("[pcl::%s::performReconstruction] ERROR: point cloud contains NaN values, consider running pcl::PassThrough filter first to remove NaNs!\n", getClassName ().c_str ());
00221     }
00222 
00223     alpha_shape.points.resize (0);
00224     alpha_shape.width = alpha_shape.height = 0;
00225     polygons.resize (0);
00226 
00227     qh_freeqhull (!qh_ALL);
00228     int curlong, totlong;
00229     qh_memfreeshort (&curlong, &totlong);
00230 
00231     return;
00232   }
00233 
00234   qh_setvoronoi_all ();
00235 
00236   int num_vertices = qh num_vertices;
00237   alpha_shape.points.resize (num_vertices);
00238 
00239   vertexT *vertex;
00240   // Max vertex id
00241   int max_vertex_id = 0;
00242   FORALLvertices
00243   {
00244     if (vertex->id + 1 > max_vertex_id)
00245       max_vertex_id = vertex->id + 1;
00246   }
00247 
00248   facetT *facet;    // set by FORALLfacets
00249 
00250   ++max_vertex_id;
00251   std::vector<int> qhid_to_pcidx (max_vertex_id);
00252 
00253   int num_facets = qh num_facets;
00254   int dd = 0;
00255 
00256   if (dim_ == 3)
00257   {
00258     setT *triangles_set = qh_settemp (4 * num_facets);
00259     if (voronoi_centers_)
00260       voronoi_centers_->points.resize (num_facets);
00261 
00262     int non_upper = 0;
00263     FORALLfacets
00264     {
00265       // Facets are tetrahedrons (3d)
00266       if (!facet->upperdelaunay)
00267       {
00268         vertexT *anyVertex = static_cast<vertexT*> (facet->vertices->e[0].p);
00269         double *center = facet->center;
00270         double r = qh_pointdist (anyVertex->point,center,dim_);
00271         facetT *neighb;
00272 
00273         if (voronoi_centers_)
00274         {
00275           voronoi_centers_->points[non_upper].x = static_cast<float> (facet->center[0]);
00276           voronoi_centers_->points[non_upper].y = static_cast<float> (facet->center[1]);
00277           voronoi_centers_->points[non_upper].z = static_cast<float> (facet->center[2]);
00278         }
00279 
00280         non_upper++;
00281 
00282         if (r <= alpha_)
00283         {
00284           // all triangles in tetrahedron are good, add them all to the alpha shape (triangles_set)
00285           qh_makeridges (facet);
00286           facet->good = true;
00287           facet->visitid = qh visit_id;
00288           ridgeT *ridge, **ridgep;
00289           FOREACHridge_ (facet->ridges)
00290           {
00291             neighb = otherfacet_ (ridge, facet);
00292             if ((neighb->visitid != qh visit_id))
00293               qh_setappend (&triangles_set, ridge);
00294           }
00295         }
00296         else
00297         {
00298           // consider individual triangles from the tetrahedron...
00299           facet->good = false;
00300           facet->visitid = qh visit_id;
00301           qh_makeridges (facet);
00302           ridgeT *ridge, **ridgep;
00303           FOREACHridge_ (facet->ridges)
00304           {
00305             facetT *neighb;
00306             neighb = otherfacet_ (ridge, facet);
00307             if ((neighb->visitid != qh visit_id))
00308             {
00309               // check if individual triangle is good and add it to triangles_set
00310 
00311               PointInT a, b, c;
00312               a.x = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[0].p))->point[0]);
00313               a.y = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[0].p))->point[1]);
00314               a.z = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[0].p))->point[2]);
00315               b.x = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[1].p))->point[0]);
00316               b.y = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[1].p))->point[1]);
00317               b.z = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[1].p))->point[2]);
00318               c.x = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[2].p))->point[0]);
00319               c.y = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[2].p))->point[1]);
00320               c.z = static_cast<float> ((static_cast<vertexT*>(ridge->vertices->e[2].p))->point[2]);
00321 
00322               double r = pcl::getCircumcircleRadius (a, b, c);
00323               if (r <= alpha_)
00324                 qh_setappend (&triangles_set, ridge);
00325             }
00326           }
00327         }
00328       }
00329     }
00330 
00331     if (voronoi_centers_)
00332       voronoi_centers_->points.resize (non_upper);
00333 
00334     // filter, add points to alpha_shape and create polygon structure
00335 
00336     int num_good_triangles = 0;
00337     ridgeT *ridge, **ridgep;
00338     FOREACHridge_ (triangles_set)
00339     {
00340       if (ridge->bottom->upperdelaunay || ridge->top->upperdelaunay || !ridge->top->good || !ridge->bottom->good)
00341         num_good_triangles++;
00342     }
00343 
00344     polygons.resize (num_good_triangles);
00345 
00346     int vertices = 0;
00347     std::vector<bool> added_vertices (max_vertex_id, false);
00348 
00349     int triangles = 0;
00350     FOREACHridge_ (triangles_set)
00351     {
00352       if (ridge->bottom->upperdelaunay || ridge->top->upperdelaunay || !ridge->top->good || !ridge->bottom->good)
00353       {
00354         polygons[triangles].vertices.resize (3);
00355         int vertex_n, vertex_i;
00356         FOREACHvertex_i_ ((*ridge).vertices)  //3 vertices per ridge!
00357         {
00358           if (!added_vertices[vertex->id])
00359           {
00360             alpha_shape.points[vertices].x = static_cast<float> (vertex->point[0]);
00361             alpha_shape.points[vertices].y = static_cast<float> (vertex->point[1]);
00362             alpha_shape.points[vertices].z = static_cast<float> (vertex->point[2]);
00363 
00364             qhid_to_pcidx[vertex->id] = vertices;   //map the vertex id of qhull to the point cloud index
00365             added_vertices[vertex->id] = true;
00366             vertices++;
00367           }
00368 
00369           polygons[triangles].vertices[vertex_i] = qhid_to_pcidx[vertex->id];
00370 
00371         }
00372 
00373         triangles++;
00374       }
00375     }
00376 
00377     alpha_shape.points.resize (vertices);
00378     alpha_shape.width = static_cast<uint32_t> (alpha_shape.points.size ());
00379     alpha_shape.height = 1;
00380   }
00381   else
00382   {
00383     // Compute the alpha complex for the set of points
00384     // Filters the delaunay triangles
00385     setT *edges_set = qh_settemp (3 * num_facets);
00386     if (voronoi_centers_)
00387       voronoi_centers_->points.resize (num_facets);
00388 
00389     FORALLfacets
00390     {
00391       // Facets are the delaunay triangles (2d)
00392       if (!facet->upperdelaunay)
00393       {
00394         // Check if the distance from any vertex to the facet->center
00395         // (center of the voronoi cell) is smaller than alpha
00396         vertexT *anyVertex = static_cast<vertexT*>(facet->vertices->e[0].p);
00397         double r = (sqrt ((anyVertex->point[0] - facet->center[0]) *
00398                           (anyVertex->point[0] - facet->center[0]) +
00399                           (anyVertex->point[1] - facet->center[1]) *
00400                           (anyVertex->point[1] - facet->center[1])));
00401         if (r <= alpha_)
00402         {
00403           pcl::Vertices facet_vertices;   //TODO: is not used!!
00404           qh_makeridges (facet);
00405           facet->good = true;
00406 
00407           ridgeT *ridge, **ridgep;
00408           FOREACHridge_ (facet->ridges)
00409           qh_setappend (&edges_set, ridge);
00410 
00411           if (voronoi_centers_)
00412           {
00413             voronoi_centers_->points[dd].x = static_cast<float> (facet->center[0]);
00414             voronoi_centers_->points[dd].y = static_cast<float> (facet->center[1]);
00415             voronoi_centers_->points[dd].z = 0.0f;
00416           }
00417 
00418           ++dd;
00419         }
00420         else
00421           facet->good = false;
00422       }
00423     }
00424 
00425     int vertices = 0;
00426     std::vector<bool> added_vertices (max_vertex_id, false);
00427     std::map<int, std::vector<int> > edges;
00428 
00429     ridgeT *ridge, **ridgep;
00430     FOREACHridge_ (edges_set)
00431     {
00432       if (ridge->bottom->upperdelaunay || ridge->top->upperdelaunay || !ridge->top->good || !ridge->bottom->good)
00433       {
00434         int vertex_n, vertex_i;
00435         int vertices_in_ridge=0;
00436         std::vector<int> pcd_indices;
00437         pcd_indices.resize (2);
00438 
00439         FOREACHvertex_i_ ((*ridge).vertices)  //in 2-dim, 2 vertices per ridge!
00440         {
00441           if (!added_vertices[vertex->id])
00442           {
00443             alpha_shape.points[vertices].x = static_cast<float> (vertex->point[0]);
00444             alpha_shape.points[vertices].y = static_cast<float> (vertex->point[1]);
00445 
00446             if (dim_ > 2)
00447               alpha_shape.points[vertices].z = static_cast<float> (vertex->point[2]);
00448             else
00449               alpha_shape.points[vertices].z = 0;
00450 
00451             qhid_to_pcidx[vertex->id] = vertices;   //map the vertex id of qhull to the point cloud index
00452             added_vertices[vertex->id] = true;
00453             pcd_indices[vertices_in_ridge] = vertices;
00454             vertices++;
00455           }
00456           else
00457           {
00458             pcd_indices[vertices_in_ridge] = qhid_to_pcidx[vertex->id];
00459           }
00460 
00461           vertices_in_ridge++;
00462         }
00463 
00464         // make edges bidirectional and pointing to alpha_shape pointcloud...
00465         edges[pcd_indices[0]].push_back (pcd_indices[1]);
00466         edges[pcd_indices[1]].push_back (pcd_indices[0]);
00467       }
00468     }
00469 
00470     alpha_shape.points.resize (vertices);
00471 
00472     std::vector<std::vector<int> > connected;
00473     PointCloud alpha_shape_sorted;
00474     alpha_shape_sorted.points.resize (vertices);
00475 
00476     // iterate over edges until they are empty!
00477     std::map<int, std::vector<int> >::iterator curr = edges.begin ();
00478     int next = - 1;
00479     std::vector<bool> used (vertices, false);   // used to decide which direction should we take!
00480     std::vector<int> pcd_idx_start_polygons;
00481     pcd_idx_start_polygons.push_back (0);
00482 
00483     // start following edges and removing elements
00484     int sorted_idx = 0;
00485     while (!edges.empty ())
00486     {
00487       alpha_shape_sorted.points[sorted_idx] = alpha_shape.points[(*curr).first];
00488       // check where we can go from (*curr).first
00489       for (size_t i = 0; i < (*curr).second.size (); i++)
00490       {
00491         if (!used[((*curr).second)[i]])
00492         {
00493           // we can go there
00494           next = ((*curr).second)[i];
00495           break;
00496         }
00497       }
00498 
00499       used[(*curr).first] = true;
00500       edges.erase (curr);   // remove edges starting from curr
00501 
00502       sorted_idx++;
00503 
00504       if (edges.empty ())
00505         break;
00506 
00507       // reassign current
00508       curr = edges.find (next);   // if next is not found, then we have unconnected polygons.
00509       if (curr == edges.end ())
00510       {
00511         // set current to any of the remaining in edge!
00512         curr = edges.begin ();
00513         pcd_idx_start_polygons.push_back (sorted_idx);
00514       }
00515     }
00516 
00517     pcd_idx_start_polygons.push_back (sorted_idx);
00518 
00519     alpha_shape.points = alpha_shape_sorted.points;
00520 
00521     polygons.resize (pcd_idx_start_polygons.size () - 1);
00522 
00523     for (size_t poly_id = 0; poly_id < polygons.size (); poly_id++)
00524     {
00525       polygons[poly_id].vertices.resize (pcd_idx_start_polygons[poly_id + 1] - pcd_idx_start_polygons[poly_id] + 1);
00526       // populate points in the corresponding polygon
00527       for (int j = pcd_idx_start_polygons[poly_id]; j < pcd_idx_start_polygons[poly_id + 1]; ++j)
00528         polygons[poly_id].vertices[j - pcd_idx_start_polygons[poly_id]] = static_cast<uint32_t> (j);
00529 
00530       polygons[poly_id].vertices[polygons[poly_id].vertices.size () - 1] = pcd_idx_start_polygons[poly_id];
00531     }
00532 
00533     if (voronoi_centers_)
00534       voronoi_centers_->points.resize (dd);
00535   }
00536 
00537   qh_freeqhull (!qh_ALL);
00538   int curlong, totlong;
00539   qh_memfreeshort (&curlong, &totlong);
00540 
00541   Eigen::Affine3f transInverse = transform1.inverse ();
00542   pcl::transformPointCloud (alpha_shape, alpha_shape, transInverse);
00543   xyz_centroid[0] = - xyz_centroid[0];
00544   xyz_centroid[1] = - xyz_centroid[1];
00545   xyz_centroid[2] = - xyz_centroid[2];
00546   pcl::demeanPointCloud (alpha_shape, xyz_centroid, alpha_shape);
00547 
00548   // also transform voronoi_centers_...
00549   if (voronoi_centers_)
00550   {
00551     pcl::transformPointCloud (*voronoi_centers_, *voronoi_centers_, transInverse);
00552     pcl::demeanPointCloud (*voronoi_centers_, xyz_centroid, *voronoi_centers_);
00553   }
00554 
00555   if (keep_information_)
00556   {
00557     // build a tree with the original points
00558     pcl::KdTreeFLANN<PointInT> tree (true);
00559     tree.setInputCloud (input_, indices_);
00560 
00561     std::vector<int> neighbor;
00562     std::vector<float> distances;
00563     neighbor.resize (1);
00564     distances.resize (1);
00565 
00566     // for each point in the concave hull, search for the nearest neighbor in the original point cloud
00567 
00568     std::vector<int> indices;
00569     indices.resize (alpha_shape.points.size ());
00570 
00571     for (size_t i = 0; i < alpha_shape.points.size (); i++)
00572     {
00573       tree.nearestKSearch (alpha_shape.points[i], 1, neighbor, distances);
00574       indices[i] = neighbor[0];
00575     }
00576 
00577     // replace point with the closest neighbor in the original point cloud
00578     pcl::copyPointCloud (*input_, indices, alpha_shape);
00579   }
00580 }
00581 #ifdef __GNUC__
00582 #pragma GCC diagnostic warning "-Wold-style-cast"
00583 #endif
00584 
00586 template <typename PointInT> void
00587 pcl::ConcaveHull<PointInT>::performReconstruction (PolygonMesh &output)
00588 {
00589   // Perform reconstruction
00590   pcl::PointCloud<PointInT> hull_points;
00591   performReconstruction (hull_points, output.polygons);
00592 
00593   // Convert the PointCloud into a PointCloud2
00594   pcl::toROSMsg (hull_points, output.cloud);
00595 }
00596 
00598 template <typename PointInT> void
00599 pcl::ConcaveHull<PointInT>::performReconstruction (std::vector<pcl::Vertices> &polygons)
00600 {
00601   pcl::PointCloud<PointInT> hull_points;
00602   performReconstruction (hull_points, polygons);
00603 }
00604 
00605 
00606 #define PCL_INSTANTIATE_ConcaveHull(T) template class PCL_EXPORTS pcl::ConcaveHull<T>;
00607 
00608 #endif    // PCL_SURFACE_IMPL_CONCAVE_HULL_H_
00609 #endif


pcl
Author(s): Open Perception
autogenerated on Mon Oct 6 2014 03:14:42