PCL从理解到应用【10】 三维点云 | 表面重建 | 泊松Poisson

前言

在PCL中,用于处理点云表面的主要方法包括:泊松表面重建、贪心投影三角化、Alpha Shapes等。

本文介绍泊松表面重建,包括原理分析,思路流程,两个实践应用和代码。

使用泊松算法,对三维点云表面重建,可视化效果:

随机生成一个圆柱体的点云,并使用泊松表面重建算法,可视化效果。

1、泊松表面重建

泊松表面重建,也称为Poisson Surface Reconstruction,是一种从点云数据生成三维表面模型的方法。

  • 点云是三维空间中的一组离散点,这些点没有连接信息,因此需要重建一个连续的表面。
  • 泊松表面重建方法基于泊松方程,通过对点云中的法线进行全局最优化生成表面
  • 它将表面重建问题转化为求解泊松方程的过程,结果是一个光滑的表面

适用场景:

适用于有噪声和不规则分布的点云数据,特别是那些需要生成平滑表面的应用。

示例步骤:

  1. 估计点云中的法线。
  2. 构建八叉树(Octree)以分割空间。
  3. 使用泊松方程求解生成表面。

2、泊松表面重建思路流程

 泊松表面重建的算法思路,如下所示:

2.1 数据准备

  • 输入:一个点云数据集。
  • 预处理:滤波,去除噪声和离群点。下采样,如果点云密度过高,可以进行下采样以减少计算量。

2.2 法向估计

  • 目的:估计每个点的法向量,法向量是与点所在表面垂直的向量。
  • 方法:可以使用PCA(主成分分析)或其他局部几何方法来估计法向量。

2.3 场函数构造

  • 定义:构造一个三维标量场函数Φ,使得在点云的每个点处,该函数的梯度近似于法向量。
  • 泊松方程:ΔΦ=∇⋅N,其中Δ是拉普拉斯算子,∇⋅N是法向量场的散度。

2.4 求解泊松方程

  • 方法:将泊松方程离散化,并通过数值方法(如多重网格方法)求解。
  • 结果:一个三维标量场函数Φ\PhiΦ。

2.5 等值面提取

  • 定义:等值面是标量场中所有具有相同值的点的集合。
  • 提取方法:使用Marching Cubes算法从标量场Φ中提取等值面,生成多边形网格。

2.6 表面平滑与后处理

  • 目的:去除噪声并改善表面质量。
  • 方法:对生成的表面进行平滑处理、孔洞填补和细节增强等。

泊松表面重建的优势:处理噪声数据的能力强、生成的表面平滑且闭合。

缺点:计算复杂度较高、对法向估计的依赖性较强。

3、泊松表面重建——实践应用1

随机生成离散点云,并使用泊松表面重建方法处理点云数据并进行可视化。

  • 生成随机点云数据:生成一个包含随机点的点云。
  • 估计法线:计算每个点的法线信息。
  • 泊松表面重建:使用泊松表面重建算法生成三维表面。
  • 可视化:使用PCL的可视化工具展示结果。

示例效果:

示例代码:

#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/poisson.h>
#include <pcl/filters/filter.h>
#include <random>
#include <thread>
#include <chrono>

int main() {
    // 创建点云对象并生成随机点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    cloud->width = 1000;
    cloud->height = 1;
    cloud->points.resize(cloud->width * cloud->height);

    // 使用随机数生成器生成点云数据
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(-1.0, 1.0);

    for (auto& point : cloud->points) {
        point.x = dis(gen);
        point.y = dis(gen);
        point.z = dis(gen);
    }

    // 估计法线
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    tree->setInputCloud(cloud);
    ne.setInputCloud(cloud);
    ne.setSearchMethod(tree);
    ne.setKSearch(20);
    ne.compute(*normals);

    // 将点云和法线合并
    pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
    pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);

    // 泊松表面重建
    pcl::Poisson<pcl::PointNormal> poisson;
    pcl::PolygonMesh mesh;
    poisson.setDepth(8);  // 设置八叉树的深度
    poisson.setSolverDivide(8);  // 设置泊松方程求解的分区数
    poisson.setIsoDivide(8);  // 设置等值面提取的分区数
    poisson.setPointWeight(4.0f);  // 设置每个点对解的影响权重
    poisson.setInputCloud(cloud_with_normals);
    poisson.reconstruct(mesh);

    // 可视化
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPolygonMesh(mesh, "mesh");
    viewer->addCoordinateSystem(1.0);
    viewer->initCameraParameters();

    while (!viewer->wasStopped()) {
        viewer->spinOnce(100);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    return 0;
}

代码说明:

  • 生成随机点云数据:使用C++标准库中的随机数生成器生成1000个随机点,每个点的坐标在[-1, 1]范围内。

  • 估计法线:使用 NormalEstimation 类计算每个点的法线。

  • 合并点云和法线:将点云和法线信息合并成一个包含法线信息的点云。

  • 泊松表面重建:使用 Poisson 类进行三角化。设置参数包括八叉树深度、泊松方程求解分区数、等值面提取分区数和点权重。

  • 可视化:使用 PCLVisualizer 类可视化生成的三角网格。

4、泊松表面重建——实践应用2

生成一个圆柱体的密集点云,并使用泊松表面重建算法,进行表面重建和可视化。

  • 生成圆柱的密集点云数据:生成一个圆柱形的点云。
  • 泊松表面重建:使用Poisson算法进行表面重建。
  • 可视化:使用PCL的可视化工具展示处理结果。

可视化效果:

示例代码:

#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/poisson.h>
#include <random>
#include <thread>
#include <chrono>

// 生成圆柱体点云
void generateCylinderPointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, float radius, float height, int num_points) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis_height(0, height);
    std::uniform_real_distribution<> dis_angle(0, 2 * M_PI);

    for (int i = 0; i < num_points; ++i) {
        float h = dis_height(gen);
        float angle = dis_angle(gen);
        pcl::PointXYZ point;
        point.x = radius * cos(angle);
        point.y = radius * sin(angle);
        point.z = h;
        cloud->points.push_back(point);
    }
    cloud->width = cloud->points.size();
    cloud->height = 1;
    cloud->is_dense = true;
}

int main() {
    // 创建点云对象并生成圆柱点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    generateCylinderPointCloud(cloud, 1.0f, 2.0f, 3000); // 半径1.0,高度2.0,点数3000

    // 估计法线
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    tree->setInputCloud(cloud);
    ne.setInputCloud(cloud);
    ne.setSearchMethod(tree);
    ne.setKSearch(20);
    ne.compute(*normals);

    // 将点云和法线合并
    pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
    pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);

    // 泊松表面重建
    pcl::Poisson<pcl::PointNormal> poisson;
    pcl::PolygonMesh mesh;
    poisson.setDepth(8);  // 设置八叉树的深度
    poisson.setSolverDivide(8);  // 设置泊松方程求解的分区数
    poisson.setIsoDivide(8);  // 设置等值面提取的分区数
    poisson.setPointWeight(4.0f);  // 设置每个点对解的影响权重
    poisson.setInputCloud(cloud_with_normals);
    poisson.reconstruct(mesh);

    // 保存结果
    pcl::io::savePLYFile("poisson_cylinder_mesh.ply", mesh);

    // 可视化
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPolygonMesh(mesh, "mesh");
    viewer->addCoordinateSystem(1.0);
    viewer->initCameraParameters();

    while (!viewer->wasStopped()) {
        viewer->spinOnce(100);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    return 0;
}

代码说明:

  • 生成圆柱的点云数据generateCylinderPointCloud 函数生成一个圆柱形点云,参数包括圆柱的半径、高度和点的数量。

  • 估计法线:使用 NormalEstimation 类计算每个点的法线。

  • 合并点云和法线:将点云和法线信息合并成一个包含法线信息的点云。

  • 泊松表面重建:使用 Poisson 类进行表面重建。设置八叉树深度、泊松方程求解的分区数、等值面提取的分区数和点权重。

  • 保存和可视化:使用 PCLVisualizer 类可视化生成的三角网格,并保存为PLY文件。

分享完成~

三维表面重建是一种常用的点数据处理方法,可以将离散的点数据重建成连续的三维表面模型。PCL(Point Cloud Library)是一个开源的点处理库,提供了许多实用的算法和工具函数,包括三维表面重建算法。 在PCL中使用三维表面重建算法,你可以按照以下步骤进行操作: 1. 使用PCL加载点数据:首先,你需要使用PCL库中的函数将点数据加载到程序中。可以使用`pcl::io::loadPCDFile()`函数加载`.pcd`格式的点文件,或者使用其他合适的函数加载其他格式的点数据。 2. 对点进行预处理:在进行表面重建之前,有时需要对点进行预处理,例如去除离群点、滤波等。PCL提供了许多预处理的方法,可以根据具体情况选择合适的方法进行处理。 3. 执行三维表面重建:使用`pcl::Poisson`类可以进行三维表面重建。你需要创建一个`pcl::Poisson`对象,并将预处理后的点数据传递给它。然后,调用`performReconstruction()`函数执行重建过程。 4. 获取重建的三维模型:重建完成后,你可以使用`pcl::PolygonMesh`对象来获取重建的三维模型。可以使用`pcl::toPCLPointCloud2()`函数将重建的模型转换为点格式,或者直接保存为`.ply`等格式的文件。 需要注意的是,三维表面重建是一个计算密集型的算法,对于大规模的点数据可能需要较长的运行时间。此外,表面重建的结果可能受到输入点的质量和密度等因素的影响,需要根据具体情况进行调整和优化。 希望以上信息对你有帮助!如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗小树x

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值