【CGAL_网格处理】平滑处理

CGAL网格处理中封装了针对网格平滑或形状平滑的算法来实现三角网格区域的平滑。

  • 形状平滑:CGAL::Polygon_mesh_processing::smooth_shape()通过使用平均曲率流来平滑网格的整体形状。沿着平均曲率流将顶点逐渐移向其邻居的加权重心。基于Desbrun等人的用于形状平滑的曲率流算法。
  • 网格平滑:CGAL::Polygon_mesh_processing::angle_and_area_smoothing()通过移动(非约束顶点)使三角形角度和面积分布尽可能均匀。

形状平滑

template<typename TriangleMesh , typename FaceRange , typename NamedParameters = parameters::Default_named_parameters>
void CGAL::Polygon_mesh_processing::smooth_shape(const FaceRange & 			faces,
												 TriangleMesh & 			tmesh,
												 const double 				time,
												 const NamedParameters & 	np = parameters::default_values() 
												)	

#include <CGAL/Polygon_mesh_processing/smooth_shape.h>

参数名解释
tmesha polygon mesh with triangulated surface patches to be smoothed.
facesthe range of triangular faces defining one or several surface patches to be smoothed.
timea time step that corresponds to the speed by which the surface is smoothed. A larger time step results in faster convergence but details may be distorted to a larger extent compared to more iterations with a smaller step. Typical values scale in the interval (1e-6, 1].
npan optional sequence of Named Parameters among the ones listed below

tmesh:三角化表面后的多边形网格。

faces:面片序列。

time:时间步长。越大收敛越快,平滑速度越快。但是细节上可能会导致丢失。取值(1e-6, 1]。

可选np:

  • number_of_iterations:平滑操作迭代次数。默认1次平滑操作。
  • vertex_is_constrained_map:一个包含了tmesh每个顶点在平滑过程中是否受约束的property map。如果某一顶点受约束,其在平滑操作中将不会被修改。

测试代码

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/smooth_shape.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/draw_surface_mesh.h>
#include <iostream>
#include <set>
#include <string>
typedef CGAL::Exact_predicates_inexact_constructions_kernel   K;
typedef CGAL::Surface_mesh<K::Point_3>                        Mesh;
namespace PMP = CGAL::Polygon_mesh_processing;
int main()
{
    const std::string filename = "data/remeshing_surface_layer_1.off";
    Mesh mesh;
    if (!PMP::IO::read_polygon_mesh(filename, mesh))
    {
        std::cerr << "Invalid input." << std::endl;
        return 1;
    }
    CGAL::draw(mesh);
    const unsigned int nb_iterations = 5;
    const double time = 0.3;
    std::set<Mesh::Vertex_index> constrained_vertices;
    for (Mesh::Vertex_index v : vertices(mesh))
    {
        if (is_border(v, mesh))
            constrained_vertices.insert(v);
    }
    std::cout << "Constraining: " << constrained_vertices.size() << " border vertices" << std::endl;
    CGAL::Boolean_property_map<std::set<Mesh::Vertex_index> > vcmap(constrained_vertices);
    std::cout << "Smoothing shape... (" << nb_iterations << " iterations)" << std::endl;
    PMP::smooth_shape(mesh, time, CGAL::parameters::number_of_iterations(nb_iterations)
        .vertex_is_constrained_map(vcmap));
    CGAL::IO::write_polygon_mesh("mesh_shape_smoothed.off", mesh, CGAL::parameters::stream_precision(17));
    std::cout << "Done!" << std::endl;
    CGAL::draw(mesh);
    return EXIT_SUCCESS;
}

报错如下:

1>D:\CGAL\CGAL-5.4.1\include\CGAL/Polygon_mesh_processing/smooth_shape.h(149,1): error C2338: static_assert failed: 'Eigen3 version 3.2 or later is required.'

原因很简单,Eigen3版本太低。所以我们只需重新下载安装最新版本的eigen即可。

参考下文进行Eigen3的安装和在VS中的配置:

VS2015中配置Eigen_Aria_J的博客-CSDN博客_vs2015配置eigen

测试结果

下面是pig.stl进行形状平滑前后的对照。

参数:time=0.5,number_of_iterations=10。

image-20220812132831145

image-20220812133438172
image-20220812134135238
image-20220812134117998

下图是默认vertex_is_constrained_map下,对有界网格进行形状平滑结果对照。

参数:time=0.3,number_of_iterations=5。

image-20220812134803235

image-20220812134837714

可见,边界点未约束将造成边界萎缩。

网格平滑

template<typename TriangleMesh , typename FaceRange , typename NamedParameters = parameters::Default_named_parameters>
void CGAL::Polygon_mesh_processing::angle_and_area_smoothing(const FaceRange & 			faces,
															 TriangleMesh & 			tmesh,
															 const NamedParameters & 	np = parameters::default_values() 
															)		

#include <CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h>

注意,如果CGAL版本是5.5以前,angle_and_area_smoothing应改为smooth_mesh,否则报错找不到头文件。

参数名解释
tmesha polygon mesh with triangulated surface patches to be smoothed.
facesthe range of triangular faces defining one or several surface patches to be smoothed.
npan optional sequence of Named Parameters among the ones listed below

tmesh:同上smooth_shape()

faces:同上smooth_shape()

部分可选np:

  • number_of_iterations:平滑操作迭代次数。默认1次平滑操作。
  • using_angle_smoothingusing_area_smoothing:基于角度平滑和基于面积平滑的开关,默认为true。
  • vertex_is_constrained_mapedge_is_constrained_map:顶点和边的是否受约束映射关系。默认没有顶点和边受约束,即在平滑过程中都会被调整。
  • use_safety_constraints:布尔值。如果为true,会忽略那些使网格恶化的顶点移动行为。默认为false。
  • use_Delaunay_flips:布尔值。如果为true,则基于面积的平滑将通过基于 Delaunay 的边缘翻转来完成,以防止产生被拉长的三角面片。

测试代码

#define CGAL_PMP_USE_CERES_SOLVER
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/smooth_mesh.h>
#include <CGAL/Polygon_mesh_processing/detect_features.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/draw_surface_mesh.h>
#include <iostream>
#include <string>
typedef CGAL::Exact_predicates_inexact_constructions_kernel     K;
typedef CGAL::Surface_mesh<K::Point_3>                          Mesh;
typedef boost::graph_traits<Mesh>::edge_descriptor              edge_descriptor;
namespace PMP = CGAL::Polygon_mesh_processing;

int main()
{
    const std::string filename = "meshes/anchor_dense.off";
    Mesh mesh;
    if (!PMP::IO::read_polygon_mesh(filename, mesh))
    {
        std::cerr << "Invalid input." << std::endl;
        return 1;
    }
    CGAL::draw(mesh);
    // Constrain edges with a dihedral angle over 60°
    typedef boost::property_map<Mesh, CGAL::edge_is_feature_t>::type EIFMap;
    EIFMap eif = get(CGAL::edge_is_feature, mesh);
    PMP::detect_sharp_edges(mesh, 60, eif);
    int sharp_counter = 0;
    for (edge_descriptor e : edges(mesh))
        if (get(eif, e))
            ++sharp_counter;
    std::cout << sharp_counter << " sharp edges" << std::endl;
    const unsigned int nb_iterations = 10;
    std::cout << "Smoothing mesh... (" << nb_iterations << " iterations)" << std::endl;
    // Smooth with both angle and area criteria + Delaunay flips
    PMP::smooth_mesh(mesh, CGAL::parameters::number_of_iterations(nb_iterations)
        .use_safety_constraints(false) // authorize all moves
        .edge_is_constrained_map(eif));
    CGAL::IO::write_polygon_mesh("mesh_smoothed.off", mesh, CGAL::parameters::stream_precision(17));
    std::cout << "Done!" << std::endl;
    CGAL::draw(mesh);
    return EXIT_SUCCESS;
}

运行上面代码,程序未报错,但是控制台输出如下内容:

Area-based smoothing requires the Ceres Library, which is not available.
No such smoothing will be performed!

查看CGAL5.4.2官方文档:

image-20220812163639546

果不其然,因此我们需要按照第三方库Ceres。

参见这篇文章:Win10 + VS2017 + Ceres配置_四片叶子的三叶草的博客-CSDN博客_ceres vs2017,安装并配置完成后,程序成功运行(运行时间比较长)。

注意:在程序最开始需加入如下一行:

#define CGAL_PMP_USE_CERES_SOLVER

测试结果

原网格:

image-20220812211240555

Smooth with angle,number_of_iterations=10.

image-20220812211316440

Smooth with area,number_of_iterations=10.

image-20220812211601771

Smooth with both angle and area criteria + Delaunay flips,number_of_iterations=10.

image-20220812211803547

问题与总结

进一步理解了可选命名参数vertex_is_constrained_map

  • Type: a class model of ReadWritePropertyMap with boost::graph_traits<TriangleMesh>::vertex_descriptor as key type and bool as value type. It must be default constructible.
  • Default: a default property map where no vertex is constrained
  • Extra: A constrained vertex cannot be modified at all during smoothing.

对于ReadWritePropertyMap的构造。

std::set<Mesh::Vertex_index> constrained_vertices;
for (Mesh::Vertex_index v : vertices(mesh))
{
    if (is_border(v, mesh))
        constrained_vertices.insert(v);
}

首先获得std::set<Mesh::Vertex_index>类型的变量constrained_vertices,其中元素类型为Mesh::Vertex_index,即顶点的索引值。这里通过is_border()判断mesh中的所有顶点,并将边界顶点放到constrained_vertices中。

这里的

接下来,使用constrained_vertices实例化CGAL::Boolean_property_map<std::set<Mesh::Vertex_index>>对象vcmap。

CGAL::Boolean_property_map<std::set<Mesh::Vertex_index> > vcmap(constrained_vertices);

这里我们转到Boolean_property_map的定义。

/// \ingroup PkgPropertyMapRef
/// Read-write property map turning a set (such a `std::set`,
/// `boost::unordered_set`, `std::unordered_set`) into a property map
/// associating a Boolean to the value type of the set. The function `get` will
/// return `true` if the key is inside the set and `false` otherwise. The `put`
/// function will insert an element in the set if `true` is passed and erase it
/// otherwise.
/// \cgalModels `ReadWritePropertyMap`
template<class Set>
struct Boolean_property_map
{
  typedef typename Set::value_type key_type;
  typedef bool value_type;
  typedef bool reference;
  typedef boost::read_write_property_map_tag category;

  Set* set_ptr;
  /// Constructor taking a copy of the set. Note that `set_` must be valid
  /// while the property map is in use.
  Boolean_property_map(Set& set_) : set_ptr(&set_) {}
  Boolean_property_map() : set_ptr(nullptr) {}

  friend bool get(const Boolean_property_map<Set>& pm, const key_type& k)
  {
    CGAL_assertion(pm.set_ptr!=nullptr);
    return pm.set_ptr->count(k) != 0;
  }

  friend void put(Boolean_property_map<Set>& pm, const key_type& k, bool v)
  {
    CGAL_assertion(pm.set_ptr!=nullptr);
    if (v)
      pm.set_ptr->insert(k);
    else
      pm.set_ptr->erase(k);
  }
};

以上分析证实了vertex_is_constrained_map的参数类型。

Type: a class model of ReadWritePropertyMap with boost::graph_traits<TriangleMesh>::vertex_descriptor as key type and bool as value type. It must be default constructible.

edge_is_constrained_map构造同理。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CGAL(Computational Geometry Algorithms Library)是一个用于计算几何的C++库,提供了许多算法和数据结构来解决各种计算几何问题,包括网格填洞。 实现网格填洞的一种常见方法是使用Delaunay三角剖分。Delaunay三角剖分是一种将点集分成三角形的方法,这些三角形满足一些性质,例如没有任何点在其外接圆内部。如果我们将网格表现为一个点集,我们可以使用Delaunay三角剖分来创建一组三角形,并填补任何缺口。 以下是使用CGAL进行网格填洞的基本步骤: 1. 加载网格使用CGAL加载网格,这可以通过读取支持的网格格式文件来实现。例如,可以使用CGAL提供的OFF格式读取器来加载OFF格式网格。 2. 创建Delaunay三角剖分:使用CGAL中的Delaunay三角剖分算法创建一个三角剖分。 3. 检测缺口:使用CGAL中的检测缺口算法检测网格中的缺口。 4. 填充缺口:使用CGAL中的填充缺口算法填充网格中的缺口。 5. 保存网格使用CGAL中的保存网格算法将填充后的网格保存到文件中。 以下是使用CGAL进行网格填洞的示例代码: ```cpp #include <CGAL/Simple_cartesian.h> #include <CGAL/Polyhedron_3.h> #include <CGAL/Polyhedron_incremental_builder_3.h> #include <CGAL/Polyhedron_traits_with_normals_3.h> #include <CGAL/Delaunay_triangulation_3.h> #include <CGAL/make_mesh_3.h> #include <CGAL/IO/Complex_3_in_triangulation_vertex_base.h> #include <CGAL/IO/Complex_3_in_triangulation_cell_base.h> #include <CGAL/IO/Complex_3_in_triangulation_geom_traits.h> #include <CGAL/IO/Polyhedron_iostream.h> typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Polyhedron_traits_with_normals_3<K> Traits; typedef CGAL::Complex_3_in_triangulation_vertex_base<K,Traits> Vb; typedef CGAL::Complex_3_in_triangulation_cell_base<K> Cb; typedef CGAL::Triangulation_data_structure_3<Vb,Cb> Tds; typedef CGAL::Delaunay_triangulation_3<K,Tds> Delaunay; typedef K::Point_3 Point_3; typedef CGAL::Polyhedron_3<Traits,Tds> Polyhedron; int main() { // Load mesh Polyhedron mesh; std::ifstream input("input.off"); input >> mesh; // Create Delaunay triangulation Delaunay dt; dt.insert(mesh.points_begin(), mesh.points_end()); // Fill holes CGAL::Polygon_mesh_processing::triangulate_hole_polygons(dt); // Save mesh std::ofstream output("output.off"); output << mesh; return 0; } ``` 在这个示例代码中,我们首先加载了一个网格文件(假设是OFF格式)。然后,我们创建了一个Delaunay三角剖分,并将网格的顶点插入到三角剖分中。接下来,我们使用CGAL中的`triangulate_hole_polygons`函数来填充任何缺口。最后,我们将填充后的网格保存到文件中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值