PCL法向量空间下采样滤波(normal_space)

PCL专栏目录及须知

注意:在既需要对密集点云进行抽稀,又需要保持点云边缘时候很好用(点云不密集时,计算可能有错)。

1.原理

对于原始点云,通过其点云法向量进行下采样,在法向量变化大的地方采样密度大,在法向量变化小的地方,采样密度小

(1)计算点云的空间法向量。

(2)依次计算相邻点之间的法向量夹角,以此近似判断每个点的曲率。曲率大的地方判断为边缘点,曲率小的地方判断为内部点。

(3)对于曲率大的地方(边缘点),下采样量级小(稠密)。曲率小的地方(内部点),下采样量级大(稀疏)。以此达到采样的同时保存边缘特征的目的。

(4)输出结果点云。

以下图为例:

红色即为法向量变化小的点云块,他们的曲率变化小,那么抽稀阈值大,抽稀过后较稀疏。

绿色即为法向量变化大的点云块,他们的曲率变化大,那么抽稀阈值小,抽稀过后较稠密。

2.使用场景

本示例较为重要。

应用场景为,需要抽稀且保留点云的特征边缘的情况。

实际工作中,一般是应用在像要抽稀,且保留物体轮廓的情形。

一般是先用阈值较小的体素抽稀抽稀一次,抽稀过后,然后再使用法向量点云下采样滤波进行抽稀,保留边缘特征。(两次抽稀让点云数量变少,另外保留了边缘特征点,之后拟合或者是单体化的时候也好弄)

3.注意事项

这个方法可能需要用户手动调参,所以要把pcl::NormalSpaceSampling<pcl::PointXYZRGB, pcl::Normal>::setBins和pcl::PointCloud<pcl::Normal>::setKSearch两个方法用可视化界面开放出来。

4.关键函数

(1)pcl::PointCloud<pcl::Normal>生成法向量相关

1)设置K邻域搜索个数(该值设置了多少,那么每次邻域搜索就会在当前点云点附近获取多少个点作为邻域点)

setKSearch()

2)设置邻域搜索半径(半径设置为多大,那么每次邻域搜索就会获取当前点云点附近多大范围的点作为邻域点)

setRadiusSearch()

(2)pcl::NormalSpaceSampling<pcl::PointXYZRGB, pcl::Normal>法向量空间下采样滤波相关

1)设置xyz法向量的分类组数;该函数对法向量空间下采样滤波有较大影响。

setBins()

在测试中,该值范围在2-8时较为正常,数值过大产生如下图情况:

对某些部分几乎无下采样,对某些部分过度采样。

2)设置随机数种子;如果不设置该参数,那么可能每次采样的结果都不一致。

setSeed()

3)设置下采样的点数;该值你设置多少,那么最终结果点云就会出现多少个点。

setSample()

5.代码

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/model_outlier_removal.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/filters/normal_space.h>

int main()
{
    /****************法向量空间下采样滤波********************/
    // 原始点云
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
    pcl::io::loadPCDFile("D:/code/csdn/data/bunny.pcd", *cloud);   // 加载原始点云数据
    // 结果点云
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);

    // 计算法向量
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    pcl::NormalEstimationOMP<pcl::PointXYZRGB, pcl::Normal> n;
    n.setInputCloud(cloud);
    pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZRGB>());           // kd树
    n.setSearchMethod(tree);                                                                                // 设置搜索树(法向量搜索通过邻域搜索得到一小块点云,这得到一小块点云拟合的平面的法向量,当做该点的法向量)
    n.setNumberOfThreads(8);                                                                                // 8线程
    n.setKSearch(30);                                                                                       // k邻域搜索
    n.compute(*normals);

    // 法线空间下采样滤波
    pcl::NormalSpaceSampling<pcl::PointXYZRGB, pcl::Normal> filters;
    filters.setInputCloud(cloud);                                                                           // 输入点云
    filters.setNormals(normals);                                                                            // 设置法线
    filters.setBins(200, 200, 200);                                                                         // 设置法向空间的分类组数(这个方法会影响到最终的结果)
    filters.setSeed(0);                                                                                     // 设置随机数种子点,这个点设置之后,可以保证每次生成的结果一致
    filters.setSample(cloud->points.size() / 10);                                                           // 结果点云的个数
    filters.filter(*cloud_filtered);

    /****************展示********************/
    boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("raw"));
    view_raw->addPointCloud<pcl::PointXYZRGB>(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::PointXYZRGB>(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.结果展示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值