13.5 其他曲面重建方法

除了 泊松重建推进前沿尺度空间 重建外,CGAL还提供了多边形曲面重建和动力学曲面重建两种方法。

除了泊松重建、推进前沿和尺度空间重建外,CGAL 还提供了多边形曲面重建和动力学曲面重建两种方法。这些方法针对特定应用场景设计,具有独特的优势。

13.5.1 多边形曲面重建

多边形曲面重建(Polygonal Surface Reconstruction)由 Nan 和 Wonka 提出,专门用于从分段平面点云重建多边形网格模型。该方法假设输入点云已被分割为平面区域。

数学基础

问题定义

给定:

  • 点云
  • 法向
  • 平面标签 ,其中 表示点 所属的平面( 表示未分配)

目标:找到一个水密的多边形网格 ,使得:

  1. 的面片是平面多边形
  2. 插值输入点云
  3. 是流形且水密的

候选面生成

算法首先通过平面相交生成候选面:

  1. 对于每对平面 ,计算交线
  2. 对于每三个平面 ,计算交点
  3. 构建平面排列(plane arrangement),提取有界面作为候选面

混合整数优化

候选面选择被建模为混合整数规划(MIP)问题:

约束条件:

  • 每条边关联 0 或 2 个面(水密性)
  • 每个顶点的邻接面形成有效流形

架构分析

核心类

template <class GeomTraits>
class Polygonal_surface_reconstruction
{
public:
    typedef typename GeomTraits::FT FT;
    typedef typename GeomTraits::Point_3 Point;
    typedef typename GeomTraits::Vector_3 Vector;
    typedef typename GeomTraits::Plane_3 Plane;
    
    // 构造函数
    template <typename PointRange, typename PointMap, typename NormalMap, typename IndexMap>
    Polygonal_surface_reconstruction(
        const PointRange& points,
        PointMap point_map,
        NormalMap normal_map,
        IndexMap index_map
    );
    
    // 重建
    template <typename MixedIntegerProgramTraits, typename PolygonMesh>
    bool reconstruct(
        PolygonMesh& output_mesh,
        double wt_fitting = 0.43,
        double wt_coverage = 0.27,
        double wt_complexity = 0.30
    );
    
    // 访问候选面
    template <typename PolygonMesh>
    void output_candidate_faces(PolygonMesh& candidate_faces) const;
    
    // 错误信息
    const std::string& error_message() const;
};

内部组件

Polygonal_surface_reconstruction
    |
    +-- internal::Point_set_with_planes<GeomTraits>
    |       +-- 点云和平面信息存储
    |
    +-- internal::Hypothesis<GeomTraits>
    |       +-- 候选面生成
    |       +-- 平面相交计算
    |
    +-- internal::Candidate_confidences<GeomTraits>
            +-- 置信度计算

使用示例

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygonal_surface_reconstruction.h>
#include <CGAL/Shape_detection/Region_growing/Region_growing.h>
#include <CGAL/Shape_detection/Region_growing/Point_set.h>
#include <CGAL/SCIP_mixed_integer_program_traits.h>  // 或 GLPK
 
#include <vector>
#include <fstream>
 
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef boost::tuple<Point, Vector, int> PNI;  // Point, Normal, Index
typedef std::vector<PNI> Point_vector;
 
typedef CGAL::Nth_of_tuple_property_map<0, PNI> Point_map;
typedef CGAL::Nth_of_tuple_property_map<1, PNI> Normal_map;
typedef CGAL::Nth_of_tuple_property_map<2, PNI> Plane_index_map;
 
typedef CGAL::Polygonal_surface_reconstruction<Kernel> Polygonal_reconstruction;
typedef CGAL::Surface_mesh<Point> Surface_mesh;
typedef CGAL::SCIP_mixed_integer_program_traits<double> MIP_Solver;
 
int main(int argc, char* argv[])
{
    // 1. 读取点云(带法向)
    Point_vector points;
    // ... 读取代码 ...
    
    // 2. 平面检测(使用区域生长)
    // ... 区域生长代码,为每个点分配平面索引 ...
    
    // 3. 设置平面索引到点云
    for (std::size_t i = 0; i < points.size(); ++i)
        points[i].get<2>() = plane_index[i];
    
    // 4. 创建重建对象
    Polygonal_reconstruction algo(points, Point_map(), Normal_map(), Plane_index_map());
    
    // 5. 执行重建
    Surface_mesh model;
    if (\!algo.reconstruct<MIP_Solver>(model, 0.43, 0.27, 0.30)) {
        std::cerr << "Failed: " << algo.error_message() << std::endl;
        return EXIT_FAILURE;
    }
    
    // 6. 保存结果
    std::ofstream out("result.off");
    CGAL::IO::write_OFF(out, model);
    out.close();
    
    return EXIT_SUCCESS;
}

复杂度分析

步骤复杂度说明
候选面生成 为平面数,三平面求交
邻接关系提取 为候选面数
MIP 求解指数级(最坏情况)实际通常可接受

适用场景

  • 建筑场景重建(墙面、屋顶等平面结构)
  • CAD 模型逆向工程
  • 室内环境扫描
  • 任何具有明显平面特征的对象

13.5.2 动力学曲面重建

动力学曲面重建(Kinetic Surface Reconstruction, KSR)是 CGAL 较新添加的重建方法,结合了形状检测、动力学空间划分和图割优化。

数学基础

动力学空间划分

KSR 使用动力学空间划分(Kinetic Space Partition)将空间划分为凸多面体单元:

  1. 从检测到的平面创建初始多边形
  2. 多边形沿法向”扩展”(kinetic 行为)
  3. 当多边形相交时,创建新的边和顶点
  4. 最终形成空间划分

内外标记优化

重建问题被转化为体积标记问题:

其中:

  • 是体积标签
  • 是数据项(基于点云可见性投票)
  • 是平滑项(基于面片面积)
  • 平衡数据保真度和模型复杂度

使用图割(Graph Cut)算法高效求解。

架构分析

核心类

template <typename GeomTraits, typename PointRange, 
          typename PointMap, typename NormalMap, 
          typename IntersectionKernel = CGAL::Exact_predicates_exact_constructions_kernel>
class Kinetic_surface_reconstruction_3
{
public:
    using Kernel = GeomTraits;
    using Intersection_kernel = IntersectionKernel;
    using FT = typename Kernel::FT;
    using Point_3 = typename Kernel::Point_3;
    using Plane_3 = typename Kernel::Plane_3;
    using KSP = Kinetic_space_partition_3<Kernel, Intersection_kernel>;
    
    // 构造函数
    template <typename NamedParameters>
    Kinetic_surface_reconstruction_3(Point_range& points, 
                                      const NamedParameters& np = parameters::default_values());
    
    // 形状检测
    template <typename NamedParameters>
    std::size_t detect_planar_shapes(const NamedParameters& np = parameters::default_values());
    
    // 初始化划分
    template <typename NamedParameters>
    void initialize_partition(const NamedParameters& np = parameters::default_values());
    
    // 执行划分
    void partition(std::size_t k);
    
    // 联合检测和划分
    template <typename NamedParameters>
    void detection_and_partition(std::size_t k, const NamedParameters& np);
    
    // 重建(带地面检测)
    template <class OutputPointIterator, class OutputPolygonIterator>
    void reconstruct_with_ground(FT lambda, OutputPointIterator pit, OutputPolygonIterator polyit);
    
    // 重建(自定义外部节点)
    template <class OutputPointIterator, class OutputPolygonIterator>
    void reconstruct(FT lambda, std::map<typename KSP::Face_support, bool> external_nodes,
                     OutputPointIterator pit, OutputPolygonIterator polyit);
    
    // 访问检测到的形状
    const std::vector<Plane_3>& detected_planar_shapes();
    const std::vector<std::vector<std::size_t>>& detected_planar_shape_indices();
    
    // 访问划分
    const KSP& kinetic_partition() const;
};

处理流程

输入点云
    |
    v
平面形状检测(区域生长)
    |
    v
形状正则化(可选:平行、正交、共面)
    |
    v
动力学空间划分初始化
    |
    v
多边形扩展与相交处理
    |
    v
能量项设置(数据项 + 平滑项)
    |
    v
图割优化(内外标记)
    |
    v
提取标记边界作为表面
    |
    v
多边形化与输出

使用示例

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Kinetic_surface_reconstruction_3.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/IO/read_points.h>
 
#include <vector>
#include <fstream>
 
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::FT FT;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef CGAL::Point_set_3<Point, Vector> Point_set;
 
typedef CGAL::Kinetic_surface_reconstruction_3<Kernel, 
                                                    Point_set,
                                                    Point_set::Point_map,
                                                    Point_set::Vector_map> KSR;
 
int main(int argc, char* argv[])
{
    // 1. 读取点云
    Point_set points;
    std::ifstream input((argc > 1) ? argv[1] : "input.ply");
    input >> points;
    input.close();
    
    std::cout << "Loaded " << points.size() << " points" << std::endl;
    
    // 2. 创建 KSR 对象
    KSR ksr(points,
            CGAL::parameters::point_map(points.point_map())
                             .normal_map(points.normal_map()));
    
    // 3. 检测平面形状并创建划分
    // k: 每个多边形允许的最大交点数
    std::size_t k = 10;
    ksr.detection_and_partition(k,
        CGAL::parameters::maximum_distance(0.1)
                         .maximum_angle(15.0)
                         .minimum_region_size(100)
                         .regularize_parallelism(true)
                         .regularize_orthogonality(true)
                         .regularize_coplanarity(true));
    
    std::cout << "Detected " << ksr.detected_planar_shapes().size() << " planes" << std::endl;
    std::cout << "Partition has " << ksr.kinetic_partition().number_of_volumes() << " volumes" << std::endl;
    
    // 4. 重建(带地面检测)
    FT lambda = 0.5;  // 平衡参数 [0, 1)
    std::vector<Point> output_points;
    std::vector<std::vector<std::size_t>> output_polygons;
    
    ksr.reconstruct_with_ground(lambda,
                                std::back_inserter(output_points),
                                std::back_inserter(output_polygons));
    
    // 5. 保存结果
    std::ofstream out("result.ply");
    CGAL::IO::write_PLY(out, output_points, output_polygons);
    out.close();
    
    return EXIT_SUCCESS;
}

复杂度分析

步骤复杂度说明
形状检测基于 k-d 树的区域生长
形状正则化 为形状数
动力学划分 为多边形数
图割优化 为体积数, 为面片数

适用场景

  • 大规模建筑场景重建
  • 城市模型生成
  • 室内环境重建
  • 需要多边形输出的 CAD 应用

13.5.3 方法比较

特性多边形重建动力学重建
输入要求平面分割 + 法向有向点云
输出类型多边形网格多边形网格
水密性是(优化保证)是(体积标记)
平面检测外部提供内置
形状正则化支持
地面检测支持
典型应用CAD 逆向工程建筑/城市场景

13.5.4 选择建议

何时选择多边形重建

  • 已有高质量的平面分割结果
  • 需要精确控制重建权重
  • 处理相对简单的分段平面对象
  • 需要与特定 MIP 求解器集成

何时选择动力学重建

  • 处理大规模建筑场景
  • 需要自动平面检测和正则化
  • 需要地面检测功能
  • 希望端到端的重建流程

参考文献

  1. Nan, L., & Wonka, P. (2017). Polyfit: Polygonal surface reconstruction from point clouds. In Proceedings of the IEEE International Conference on Computer Vision (pp. 2353-2361).

  2. Oesau, S., Lafarge, F., & Alliez, P. (2023). Kinetic shape reconstruction. ACM Transactions on Graphics, 42(4), 1-15.

  3. Chauve, A. L., Labatut, P., & Pons, J. P. (2010). Robust piecewise-planar 3D reconstruction and completion from large-scale unstructured point data. In CVPR (pp. 1261-1268).

  4. CGAL Documentation: Polygonal Surface Reconstruction. https://doc.cgal.org/latest/Polygonal_surface_reconstruction/

  5. CGAL Documentation: Kinetic Surface Reconstruction. https://doc.cgal.org/latest/Kinetic_surface_reconstruction/