|
Point Cloud Library (PCL)
1.6.0
|
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
1.7.6.1