PCL 基于法向量的点云边界提取(boundary)

PCL专栏目录及须知-CSDN博客

注意:本方法对于规则的(如平面、立方体等)提取效果较好,工作中可以使用。

1.原理

根据法向量进行判断,计算某一个点云点与其它邻域点连线间的法向量夹角,若夹角大于设定的阈值,则认为该点为边界点。

(1)基于领域,计算点云法向量

(2)对每个点云点P,基于点云P法向量,做一个垂直于法向量的切平面

(3)取点云P周围领域范围内的点云,向其切平面投影(领域内的所有点云都拍到基于法向量做的切平面上)

(4)领域内的所有点与点云点P,两两连线成一系列的向量(下图中的绿色向量)。然后这些向量按照逆时针排序,连接成系列夹角(下图中的蓝色方向)

(5)找到最大的夹角(下图中的红色方向),作为点云点P的夹角α。

(6)判断α和用户设定的夹角阈值β之间的大小。如果α>β,那么判断为边界点;如果α<β,那么判断为不是边界点

如下图:

蓝色圈子:投影到切平面上的领域点。

绿色向量:两两连线成的一系列向量。

蓝色向量:两两向量之间的一系列夹角。

红色向量:找到的最大夹角。

2.使用场景

用于生成点云边界点

工作中较常用。

3.注意事项

4.关键函数

(1)法向量估计相关

1)取每个点云点P的K领域搜索的值,领域搜索取得的该部分点云,拟合平面,做平面的法向量,作为点云点的法向量。

setKSearch()

(1)估计点云边界相关

1)取每个点云点P的K领域搜索的值,领域搜索取得的该部分点云,将所有点云投影到法向量平面上,然后再做后续连线得向量,向量排序,得最大夹角等步骤

setKSearch()

2)边界信息保存为点云

最终结果boundary_point的个数和原始点云cloud的个数相同;其中boundary_point为一个char型的值,1为边界,0为非边界

    for (size_t i = 0; i < boundaries.points.size(); i++)
    {
        int flag = static_cast<int>(boundaries.points[i].boundary_point);                           // 最终结果boundary_point的个数和原始点云cloud的个数相同;其中boundary_point为一个char型的值,1为边界,0为非边界
        if (flag == 1)
            cloud_filtered->push_back(cloud->points[i]);
    }

5.代码

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/boundary.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>

int main()
{
    /****************法线估计边界法********************/
    // 原始点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile("D:/code/csdn/data/plane.pcd", *cloud);   // 加载原始点云数据
    // 结果点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

    // 生成点云法线
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normEst;                                      // 基于邻域的法线估计器
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);                    // 法向量保存
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());         // KD树
    normEst.setInputCloud(cloud);
    normEst.setSearchMethod(tree);
    normEst.setKSearch(50);                                                                         // 使用K领域搜索
    // normEst.setRadiusSearch(0.3);                                                                // 进行法向估计的领域半径(该半径范围内的点云拟合一个平面,生成点云法向量)
    normEst.compute(*normals);

    // 估计点云边界
    pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> features;                    // 基于法线的点云边界估计器
    pcl::PointCloud<pcl::Boundary> boundaries;                                                      // 保存点云边界
    features.setInputCloud(cloud);
    features.setInputNormals(normals);
    features.setSearchMethod(tree);                                                                 // 树搜索
    features.setKSearch(50);                                                                        // K领域搜索
    //features.setRadiusSearch(0.2);                                                                // 也可使用半径搜索
    features.compute(boundaries);

    // 边界信息保存为点云
    for (size_t i = 0; i < boundaries.points.size(); i++)
    {
        int flag = static_cast<int>(boundaries.points[i].boundary_point);                           // 最终结果boundary_point的个数和原始点云cloud的个数相同;其中boundary_point为一个char型的值,1为边界,0为非边界
        if (flag == 1)
            cloud_filtered->push_back(cloud->points[i]);
    }

    /****************展示********************/
    boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("raw"));
    view_raw->addPointCloud<pcl::PointXYZ>(cloud, "raw cloud");
    view_raw->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "raw cloud");

    boost::shared_ptr<pcl::visualization::PCLVisualizer> view_filtered(new pcl::visualization::PCLVisualizer("filter"));
    view_filtered->addPointCloud<pcl::PointXYZ>(cloud_filtered, "filtered cloud");
    view_filtered->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "filtered cloud");

    while (!view_raw->wasStopped())
    {
        view_raw->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }
    while (!view_filtered->wasStopped())
    {
        view_filtered->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }

    return 0;
}

6.结果展示

PCL(Point Cloud Library)是一个用于处理点云数据的开源库,其中包含了许多用于点云分析和处理的算法PCL可以基于向量点云进行分割。 点云是由大量的点组成的三维数据集。在进行点云分割时,我们希望将点云分成一些具有相似特征的子集,以便进一步进行各种分析和处理。 向量是指点云中每个点周围表面的线方向。通过计算每个点的向量,我们可以获取点云中的结构信息,如平面、曲线等。在基于向量分割点云时,我们通过分析点云中每个点的向量来判断其是否属于同一个表面。 基于向量点云分割算法通常包括以下步骤: 1. 首先,通过某个算法(如最近邻算法)计算每个点的向量。这些向量可以表示点云中每个点周围表面的方向。 2. 接下来,我们选择一个点作为种子点,并按照一定的条件将其加入到一个分割的子集中。 3. 然后,我们检查周围的点,判断它们的向量与种子点的向量是否一致。如果一致,我们将这些点也加入到分割的子集中。 4. 重复步骤3,直到没有点满足条件为止。 5. 最后,我们切换到下一个未分割的点,然后重复步骤2-4,直到所有点都被分割完毕。 基于向量点云分割可以帮助我们识别出点云中的不同表面,例如建筑物的墙面、地面、屋顶等。这对于进一步的点云处理和分析非常有帮助,如物体识别、建模、匹配等应用。PCL提供了丰富的函数和算法,可以方便地实现基于向量点云分割。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值