You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
}

关键说明

  1. 自定义谓词结构:Vertex_count_stop_predicate实现了CGAL要求的谓词接口,通过profile.current_number_of_vertices()实时获取当前顶点数,当达到目标时返回true停止折叠。
  2. 网格过滤:在简化前直接判断顶点数,不足5000的直接跳过,符合你的需求。
  3. 准确性:相比通过欧拉公式估算边数的方法,直接监控顶点数能确保最终结果严格接近目标值(由于边折叠的特性,可能会有±1的误差,可通过微调目标值或额外处理修正)。

内容的提问来源于stack exchange,提问作者K. Zhu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 09:55:19