如何在CGAL网格简化中指定目标剩余顶点数量?
网格简化至指定顶点数的CGAL实现方法
问题核心
你需要将ModelNet40数据集中的网格统一简化到5000个顶点,目前CGAL的边折叠工具仅支持按边数停止,想知道是否能直接指定目标顶点数。
解决方案:自定义顶点数停止谓词
CGAL的Surface Mesh Simplification模块确实没有内置的顶点数停止谓词,但你可以通过自定义停止谓词直接监控并控制顶点数量,这比通过欧拉公式估算边数的方法更准确。
原理:每次边折叠操作会将两个顶点合并为一个,因此每执行一次有效折叠,顶点数减少1。自定义谓词只需在每次折叠后检查当前顶点数是否达到目标值即可。
修改后的完整代码
#include <CGAL/Simple_cartesian.h> #include <CGAL/Surface_mesh.h> #include <CGAL/Surface_mesh_simplification/edge_collapse.h> #include <chrono> #include <fstream> #include <iostream> typedef CGAL::Simple_cartesian<double> Kernel; typedef Kernel::Point_3 Point_3; typedef CGAL::Surface_mesh<Point_3> Surface_mesh; namespace SMS = CGAL::Surface_mesh_simplification; // 自定义顶点数停止谓词 struct Vertex_count_stop_predicate { Vertex_count_stop_predicate(std::size_t target_vertices) : target(target_vertices) {} template<typename F, typename Profile> bool operator()(const F&, const Profile& profile, std::size_t) const { // 获取当前网格的顶点数 std::size_t current_vertices = profile.current_number_of_vertices(); // 当顶点数小于等于目标值时停止 return current_vertices <= target; } private: std::size_t target; }; int main(int argc, char** argv) { const int target_vertices = 5000; Surface_mesh surface_mesh; const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("out.off"); std::ifstream is(filename); if(!is || !(is >> surface_mesh)) { std::cerr << "Failed to read input mesh: " << filename << std::endl; return EXIT_FAILURE; } if(!CGAL::is_triangle_mesh(surface_mesh)) { std::cerr << "Input geometry is not triangulated." << std::endl; return EXIT_FAILURE; } // 过滤顶点数不足的网格 std::size_t current_vertices = surface_mesh.number_of_vertices(); if (current_vertices < target_vertices) { std::cerr << "Mesh has fewer than " << target_vertices << " vertices, skipping." << std::endl; return EXIT_FAILURE; } std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); // 使用自定义的顶点数停止谓词 Vertex_count_stop_predicate stop(target_vertices); int edges_removed = SMS::edge_collapse(surface_mesh, stop); std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); std::cout << "Finished!\n" << edges_removed << " edges removed.\n" << surface_mesh.number_of_edges() << " final edges, " << surface_mesh.number_of_vertices() << " final vertices.\n"; std::cout << "Time elapsed: " << std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count() << "ms" << std::endl; CGAL::IO::write_polygon_mesh((argc > 3) ? argv[3] : "out.off", surface_mesh, CGAL::parameters::stream_precision(17)); return EXIT_SUCCESS; }
关键说明
- 自定义谓词结构:
Vertex_count_stop_predicate实现了CGAL要求的谓词接口,通过profile.current_number_of_vertices()实时获取当前顶点数,当达到目标时返回true停止折叠。 - 网格过滤:在简化前直接判断顶点数,不足5000的直接跳过,符合你的需求。
- 准确性:相比通过欧拉公式估算边数的方法,直接监控顶点数能确保最终结果严格接近目标值(由于边折叠的特性,可能会有±1的误差,可通过微调目标值或额外处理修正)。
内容的提问来源于stack exchange,提问作者K. Zhu
相关产品推荐
相关产品推荐

