PCL点云 滤波去噪的方法

为什么进行点云滤波处理:

(1) 点云数据密度不规则需要平滑
(2) 因为遮挡等问题造成离群点需要去除
(3) 大量数据需要下采样
(4) 噪声数据需要去除

点云数据去噪滤波方法:

双边滤波、高斯滤波、分箱去噪、KD-Tree、直通滤波、随机采样一致性滤波等

方法定义以及适用性:
1.双边滤波:将距离和空间结构结合去噪,效果较好。只适用于有序点云

2.高斯滤波(标准差去噪):适用于呈正态分布的数据。考虑到离群点的特征,则可以定义某处点云小于某个密度,既点云无效。计算每个点到其最近的k个点平均距离。则点云中所有点的距离应构成高斯分布。给定均值与方差,可剔除3∑之外的点。

3.分箱去噪:适用于呈偏态分布的数据。

4.dbscan:基于聚类原理去噪,复杂度较高。

5.KD-Tree(孤立森林):复杂度高。构建KD树,随机取点求平均距离d,删掉所有大于2d的点。适用于无序点云去噪。

6.条件滤波:条件滤波器通过设定滤波条件进行滤波,有点分段函数的味道,当点云在一定范围则留下,不在则舍弃。

7.直通滤波:对于在空间分布有一定空间特征的点云数据,比如使用线结构光扫描的方式采集点云,沿z向分布较广,但x,y向的分布处于有限范围内。此时可使用直通滤波器,确定点云在x或y方向上的范围,可较快剪除离群点,达到第一步粗处理的目的。

8.随机采样一致性滤波

9.体素滤波:体素的概念类似于像素,使用AABB包围盒将点云数据体素化,一般体素越密集的地方信息越多,噪音点及离群点可通过体素网格去除。另一方面如果使用高分辨率相机等设备对点云进行采集,往往点云会较为密集。过多的点云数量会对后续分割工作带来困难。体素滤波器可以达到向下采样同时不破坏点云本身几何结构的功能。

10.半径滤波:半径滤波器与统计滤波器相比更加简单粗暴。以某点为中心画一个圆计算落在该圆中点的数量,当数量大于给定值时,则保留该点,数量小于给定值则剔除该点。此算法运行速度快,依序迭代留下的点一定是最密集的,但是圆的半径和圆内点的数目都需要人工指定。

原理以及核心代码:
 

// 直通滤波
pcl::PassThrough<pcl::PointXYZ> pass;	//创建直通滤波器对象
pass.setInputCloud(cloud);		        //设置待滤波的点云
pass.setFilterFieldName("z");           //设置过滤时所需要点云类型为Z字段
pass.setFilterLimits(-5, 1);        //设置在过滤字段的范围
pass.setFilterLimitsNegative(true);     //设置保留还是过滤掉字段范围内的点,设置为true表示过滤掉字段范围内的点
pass.filter(*cloud_filtered);		//执行滤波

// 半径滤波
pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;
ror.setInputCloud(cloud);            //设置待滤波的点云
ror.setRadiusSearch(0.5);            // 设置搜索半径
ror.setMinNeighborsInRadius(230);    // 设置一个内点最少的邻居数目
ror.filter (*cloud_filtered);        //滤波结果存储到cloud_filtered

// 统计滤波
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);               //设置待滤波的点云
sor.setMeanK(100);                      //取平均值的临近点数
sor.setStddevMulThresh(3);              //临近点数数目少于多少时会被舍弃
sor.filter(*cloud_filtered);

// 体素滤波
pcl::VoxelGrid<pcl::PointXYZ> vg;       //体素栅格下采样对象
vg.setInputCloud (cloud);               //设置待滤波的点云
vg.setLeafSize (0.01f, 0.01f, 0.01f);   //设置采样的体素大小
vg.filter (*cloud_filtered);            //执行采样保存数据


一般来说,滤波对应的方案有如下几种:

(1)按照给定的规则限制过滤去除点
(2)通过常用滤波算法修改点的部分属性
(3)对数据进行下采样

具体实现:

#include<iostream>
#include<pcl/point_types.h>
#include<pcl/filters/passthrough.h>  //直通滤波器头文件
#include<pcl/filters/voxel_grid.h>  //体素滤波器头文件
#include<pcl/filters/statistical_outlier_removal.h>   //统计滤波器头文件
#include <pcl/filters/conditional_removal.h>    //条件滤波器头文件
#include <pcl/filters/radius_outlier_removal.h>   //半径滤波器头文件


int main(int argc, char** argv)
{
    /*创建点云数据集。*/
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    cloud->width = 500;
    cloud->height = 1;
    cloud->points.resize(cloud->width*cloud->height);
    std::cout << "创建原始点云数据" << std::endl;
    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
        cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
        cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
    }
    for (size_t i = 0; i < cloud->points.size(); i++)
    {
        std::cerr << " " << cloud->points[i].x << " "
            << cloud->points[i].y << " "
            << cloud->points[i].z << std::endl;
    }
    std::cout << "原始点云数据点数:" << cloud->points.size() << std::endl << std::endl;


    /*直通滤波器对点云进行处理*/
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_PassThrough(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PassThrough<pcl::PointXYZ> pass;
    pass.setInputCloud(cloud);//输入点云
    pass.setFilterFieldName("z");//对z轴进行操作
    pass.setFilterLimits(0.0, 400.0);//设置直通滤波器操作范围
    //pass.setFilterLimitsNegative(true);//true表示保留范围内,false表示保留范围外
    pass.filter(*cloud_after_PassThrough);//执行滤波,过滤结果保存在 cloud_after_PassThrough
    std::cout << "直通滤波后点云数据点数:" << cloud_after_PassThrough->points.size() << std::endl;


    /*体素滤波器实现下采样*/
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_voxelgrid(new pcl::PointCloud<pcl::PointXYZ>);//
    pcl::VoxelGrid<pcl::PointXYZ> vg;
    vg.setInputCloud(cloud);//输入点云数据
    vg.setLeafSize(0.01f, 0.01f, 0.01f);  //AABB长宽高
    vg.filter(*cloud_after_voxelgrid);
    std::cout << "体素化网格方法后点云数据点数:" << cloud_after_voxelgrid->points.size() << std::endl;


    /*统计滤波器滤波*/
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_StatisticalRemoval(new pcl::PointCloud<pcl::PointXYZ>);//
    pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
    sor.setInputCloud(cloud);
    sor.setMeanK(20);//取平均值的临近点数
    sor.setStddevMulThresh(5);//临近点数数目少于多少时会被舍弃
    sor.filter(*cloud_after_StatisticalRemoval);
    std::cout << "统计分析滤波后点云数据点数:" << cloud_after_StatisticalRemoval->points.size() << std::endl;


    /*半径滤波器*/
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_Radius(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;  //创建滤波器
    ror.setInputCloud(cloud);    //设置输入点云
    ror.setRadiusSearch(100);     //设置半径为100的范围内找临近点
    ror.setMinNeighborsInRadius(2); //设置查询点的邻域点集数小于2的删除
    ror.filter(*cloud_after_Radius);
    std::cout << "半径滤波后点云数据点数:" << cloud_after_Radius->points.size() << std::endl;


    /*条件滤波器*/
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_Condition(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_condition(new pcl::ConditionAnd<pcl::PointXYZ>());
    range_condition->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
        pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0)));  //GT表示大于等于
    range_condition->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
        pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8)));  //LT表示小于等于

    pcl::ConditionalRemoval<pcl::PointXYZ> condition;
    condition.setCondition(range_condition);
    condition.setInputCloud(cloud);                   //输入点云
    condition.setKeepOrganized(true);
    condition.filter(*cloud_after_Condition);
    std::cout << "条件滤波后点云数据点数:" << cloud_after_Condition->points.size() << std::endl;


    int a;
    std::cin >> a;
    return (0);

}
发布了52 篇原创文章 · 获赞 34 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43789195/article/details/103120999