// If the mesh is a topological disk, extract its longest border, // else compute a very simple cut to make it homeomorphic to a disk. // Return the border of this region (empty on error) // // CAUTION: this cutting algorithm is very naive. Write your own! static Seam cut_mesh(Parameterization_polyhedron_adaptor& mesh_adaptor) { // Helper class to compute genus or extract borders typedef CGAL::Parameterization_mesh_feature_extractor<Parameterization_polyhedron_adaptor> Mesh_feature_extractor; Seam seam; // returned list // Get reference to Polyhedron_3 mesh Polyhedron& mesh = mesh_adaptor.get_adapted_mesh(); // Extract mesh borders and compute genus Mesh_feature_extractor feature_extractor(mesh_adaptor); int nb_borders = feature_extractor.get_nb_borders(); int genus = feature_extractor.get_genus(); // If mesh is a topological disk if (genus == 0 && nb_borders > 0) { // Pick the longest border seam = feature_extractor.get_longest_border(); } else // if mesh is *not* a topological disk, create a virtual cut { const int CUT_LENGTH = 6; // Build consecutive halfedges array Polyhedron::Halfedge_handle seam_halfedges[CUT_LENGTH]; seam_halfedges[0] = mesh.halfedges_begin(); if (seam_halfedges[0] == NULL) return seam; // return empty list int i; for (i=1; i<CUT_LENGTH; i++) { seam_halfedges[i] = seam_halfedges[i-1]->next()->opposite()->next(); if (seam_halfedges[i] == NULL) return seam; // return empty list } // Convert halfedges array to two-ways vertices list for (i=0; i<CUT_LENGTH; i++) seam.push_back(seam_halfedges[i]->vertex()); for (i=CUT_LENGTH-1; i>=0; i--) seam.push_back(seam_halfedges[i]->opposite()->vertex()); } return seam; }
Eigen::Matrix4f align(pcl::PointCloud<pcl::PointXYZRGB>::Ptr source, pcl::PointCloud<pcl::PointXYZRGB>::Ptr target, int keypoint_type, int descriptor_type) { // if (argc < 6) // { // pcl::console::print_info ("Syntax is: %s <source-pcd-file> <target-pcd-file> <keypoint-method> <descriptor-type> <surface-reconstruction-method>\n", argv[0]); // pcl::console::print_info ("available <keypoint-methods>: 1 = Sift3D\n"); // pcl::console::print_info (" 2 = Harris3D\n"); // pcl::console::print_info (" 3 = Tomasi3D\n"); // pcl::console::print_info (" 4 = Noble3D\n"); // pcl::console::print_info (" 5 = Lowe3D\n"); // pcl::console::print_info (" 6 = Curvature3D\n\n"); // pcl::console::print_info ("available <descriptor-types>: 1 = FPFH\n"); // pcl::console::print_info (" 2 = SHOTRGB\n"); // pcl::console::print_info (" 3 = PFH\n"); // pcl::console::print_info (" 4 = PFHRGB\n\n"); // pcl::console::print_info ("available <surface-methods>: 1 = Greedy Projection\n"); // pcl::console::print_info (" 2 = Marching Cubes\n"); // return (1); // } // pcl::console::print_info ("== MENU ==\n"); // pcl::console::print_info ("1 - show/hide source point cloud\n"); // pcl::console::print_info ("2 - show/hide target point cloud\n"); // pcl::console::print_info ("3 - show/hide segmented source point cloud\n"); // pcl::console::print_info ("4 - show/hide segmented target point cloud\n"); // pcl::console::print_info ("5 - show/hide source key points\n"); // pcl::console::print_info ("6 - show/hide target key points\n"); // pcl::console::print_info ("7 - show/hide source2target correspondences\n"); // pcl::console::print_info ("8 - show/hide target2source correspondences\n"); // pcl::console::print_info ("9 - show/hide consistent correspondences\n"); // pcl::console::print_info ("i - show/hide initial alignment\n"); // pcl::console::print_info ("r - show/hide final registration\n"); // pcl::console::print_info ("t - show/hide triangulation (surface reconstruction)\n"); // pcl::console::print_info ("h - show visualizer options\n"); // pcl::console::print_info ("q - quit\n"); // pcl::PointCloud<pcl::PointXYZRGB>::Ptr source (new pcl::PointCloud<pcl::PointXYZRGB>); // pcl::io::loadPCDFile (argv[1], *source); // pcl::PointCloud<pcl::PointXYZRGB>::Ptr target (new pcl::PointCloud<pcl::PointXYZRGB>); // pcl::io::loadPCDFile (argv[2], *target); // int keypoint_type = atoi (argv[3]); // int descriptor_type = atoi (argv[4]); // int surface_type = atoi (argv[5]); boost::shared_ptr<pcl::Keypoint<pcl::PointXYZRGB, pcl::PointXYZI> > keypoint_detector; if (keypoint_type == 1) { pcl::SIFTKeypoint<pcl::PointXYZRGB, pcl::PointXYZI>* sift3D = new pcl::SIFTKeypoint<pcl::PointXYZRGB, pcl::PointXYZI>; sift3D->setScales(0.01, 3, 2); sift3D->setMinimumContrast(0.0); keypoint_detector.reset(sift3D); } else { pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>* harris3D = new pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI> (pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::HARRIS); harris3D->setNonMaxSupression(true); harris3D->setRadius (0.01); harris3D->setRadiusSearch (0.01); keypoint_detector.reset(harris3D); switch (keypoint_type) { case 2: harris3D->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::HARRIS); break; case 3: harris3D->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::TOMASI); break; case 4: harris3D->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::NOBLE); break; case 5: harris3D->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::LOWE); break; case 6: harris3D->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::CURVATURE); break; default: pcl::console::print_error("unknown key point detection method %d\n expecting values between 1 and 6", keypoint_type); exit (1); break; } } boost::shared_ptr<pcl::PCLSurfaceBase<pcl::PointXYZRGBNormal> > surface_reconstruction; // if (surface_type == 1) // { // pcl::GreedyProjectionTriangulation<pcl::PointXYZRGBNormal>* gp3 = new pcl::GreedyProjectionTriangulation<pcl::PointXYZRGBNormal>; // // Set the maximum distance between connected points (maximum edge length) // gp3->setSearchRadius (0.025); // // Set typical values for the parameters // gp3->setMu (2.5); // gp3->setMaximumNearestNeighbors (100); // gp3->setMaximumSurfaceAngle(M_PI/4); // 45 degrees // gp3->setMinimumAngle(M_PI/18); // 10 degrees // gp3->setMaximumAngle(2*M_PI/3); // 120 degrees // gp3->setNormalConsistency(false); // surface_reconstruction.reset(gp3); // } // else if (surface_type == 2) // { // pcl::MarchingCubes<pcl::PointXYZRGBNormal>* mc = new pcl::MarchingCubesHoppe<pcl::PointXYZRGBNormal>; // mc->setIsoLevel(0.001); // mc->setGridResolution(50, 50, 50); // surface_reconstruction.reset(mc); // } // else // { // pcl::console::print_error("unknown surface reconstruction method %d\n expecting values between 1 and 2", surface_type); // exit (1); // } Eigen::Matrix4f tf; switch (descriptor_type) { case 1: { pcl::Feature<pcl::PointXYZRGB, pcl::FPFHSignature33>::Ptr feature_extractor (new pcl::FPFHEstimationOMP<pcl::PointXYZRGB, pcl::Normal, pcl::FPFHSignature33>); feature_extractor->setSearchMethod (pcl::search::Search<pcl::PointXYZRGB>::Ptr (new pcl::search::KdTree<pcl::PointXYZRGB>)); feature_extractor->setRadiusSearch (0.05); ICCVTutorial<pcl::FPFHSignature33> tutorial (keypoint_detector, feature_extractor, surface_reconstruction, source, target); tutorial.run (); tf = tutorial.transformation_matrix_ * tutorial.initial_transformation_matrix_; } break; case 2: { pcl::SHOTEstimationOMP<pcl::PointXYZRGB, pcl::Normal, pcl::SHOT>* shot = new pcl::SHOTEstimationOMP<pcl::PointXYZRGB, pcl::Normal, pcl::SHOT>; shot->setRadiusSearch (0.04); pcl::Feature<pcl::PointXYZRGB, pcl::SHOT>::Ptr feature_extractor (shot); ICCVTutorial<pcl::SHOT> tutorial (keypoint_detector, feature_extractor, surface_reconstruction, source, target); tutorial.run (); tf = tutorial.transformation_matrix_ * tutorial.initial_transformation_matrix_; } break; case 3: { pcl::Feature<pcl::PointXYZRGB, pcl::PFHSignature125>::Ptr feature_extractor (new pcl::PFHEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::PFHSignature125>); feature_extractor->setKSearch(50); ICCVTutorial<pcl::PFHSignature125> tutorial (keypoint_detector, feature_extractor, surface_reconstruction, source, target); tutorial.run (); tf = tutorial.transformation_matrix_ * tutorial.initial_transformation_matrix_; } break; case 4: { pcl::Feature<pcl::PointXYZRGB, pcl::PFHRGBSignature250>::Ptr feature_extractor (new pcl::PFHRGBEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::PFHRGBSignature250>); feature_extractor->setKSearch(50); ICCVTutorial<pcl::PFHRGBSignature250> tutorial (keypoint_detector, feature_extractor, surface_reconstruction, source, target); tutorial.run (); tf = tutorial.transformation_matrix_ * tutorial.initial_transformation_matrix_; } break; default: pcl::console::print_error("unknown descriptor type %d\n expecting values between 1 and 4", descriptor_type); exit (1); break; } return tf; }
// Cut the mesh to make it homeomorphic to a disk // or extract a region homeomorphic to a disc. // Return the border of this region (empty on error) // // CAUTION: // This method is provided "as is". It is very buggy and simply part of this example. // Developers using this package should implement a more robust cut algorithm! static Seam cut_mesh(Parameterization_polyhedron_adaptor& mesh_adaptor) { // Helper class to compute genus or extract borders typedef CGAL::Parameterization_mesh_feature_extractor<Parameterization_polyhedron_adaptor_ex> Mesh_feature_extractor; typedef Mesh_cutter::Backbone Backbone; Seam seam; // returned list // Get refererence to Polyhedron_3 mesh Polyhedron& mesh = mesh_adaptor.get_adapted_mesh(); // Extract mesh borders and compute genus Mesh_feature_extractor feature_extractor(mesh_adaptor); int nb_borders = feature_extractor.get_nb_borders(); int genus = feature_extractor.get_genus(); // If mesh is a topological disk if (genus == 0 && nb_borders > 0) { // Pick the longest border seam = feature_extractor.get_longest_border(); } else // if mesh is *not* a topological disk, create a virtual cut { Backbone seamingBackbone; // result of cutting Backbone::iterator he; // Compute a cutting path that makes the mesh a "virtual" topological disk mesh.compute_facet_centers(); Mesh_cutter cutter(mesh); if (genus == 0) { // no border, we need to cut the mesh assert (nb_borders == 0); cutter.cut(seamingBackbone); // simple cut } else // genus > 0 -> cut the mesh { cutter.cut_genus(seamingBackbone); } // The Mesh_cutter class is quite buggy // => we check that seamingBackbone is valid // // 1) Check that seamingBackbone is not empty if (seamingBackbone.begin() == seamingBackbone.end()) return seam; // return empty list // // 2) Check that seamingBackbone is a loop and // count occurences of seam halfedges mesh.tag_halfedges(0); // Reset counters for (he = seamingBackbone.begin(); he != seamingBackbone.end(); he++) { // Get next halfedge iterator (looping) Backbone::iterator next_he = he; next_he++; if (next_he == seamingBackbone.end()) next_he = seamingBackbone.begin(); // Check that seamingBackbone is a loop: check that // end of current HE == start of next one if ((*he)->vertex() != (*next_he)->opposite()->vertex()) return seam; // return empty list // Increment counter (in "tag" field) of seam halfedges (*he)->tag( (*he)->tag()+1 ); } // // 3) check that the seamingBackbone is a two-way list for (he = seamingBackbone.begin(); he != seamingBackbone.end(); he++) { // Counter of halfedge and opposite halfedge must be 1 if ((*he)->tag() != 1 || (*he)->opposite()->tag() != 1) return seam; // return empty list } // Convert list of halfedges to a list of vertices for (he = seamingBackbone.begin(); he != seamingBackbone.end(); he++) seam.push_back((*he)->vertex()); } return seam; }