滤波原因

在获取点云数据时,由于设备精度、操作者经验、环境因素等带来的影响,以及电磁波衍射特性、被测物体表面性质变化和数据拼接配准操作过程中的影响,点云数据中将不可避免将出现一些噪声点。实际应用中,除了这些测量随机误差产生的噪声点之外,由于收到外界干扰,如视线遮挡、障碍物等因素的影响,点云数据中往往存在些离主体点云较远的离群点。

在点云处理流程中,滤波处理作为预处理的第一步,对后续(配准、特征提取、曲面重建)处理影响很大。

PCL中点云滤波模块提供了很多灵活实用的滤波处理算法,如双边滤波、高斯滤波、条件滤波、直通滤波、体素滤波等等。

什么情况下需要滤波呢,有以下几种:

  • 点云数据密度不规则需要平滑
  • 因为遮挡等问题造成离群点需要去除
  • 大量数据需要进行下采样
  • 噪音数据需哟去除

下面通过PCL的库 实现 几种 滤波

PCL中的filters模块及类

pcl_filters模块提供对噪声点和离群点去除的具体实现。filters模块用32个类和5个函数实现了对点云数据进行不同的滤波以达到去除不需要点的目的。

可以打开pcl库下面的filters的文件夹,里面是该模块的各个头文件,在使用某个模块时必须要包含该头文件。
例如:
passthrough.h 直通滤波
voxel_grid.h 体素滤波
statistical_outlier_removal.h 离群点滤波
project_inliers.h 投影滤波

Code

===========================================================

#include <iostream>
#include <ctime>
#include <pcl/point_types.h>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/passthrough.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/ModelCoefficients.h>
#include <pcl/filters/project_inliers.h>

需要包含的头文件
里面有上面提到的几种滤波的头文件

格外说明的是,在CMakeList.txt文件中要 find_package 下 filters模块,否则会报错的

# 寻找PCL的库
find_package(PCL REQUIRED COMPONENT common io visualization filters)

===========================================================

    //声明一个点云 指针
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

    //从PCD 中 读取点云
    pcl::io::loadPCDFile ("../wolf.pcd", *cloud);

声明一个点云 指针,然后从PCD 文件中读取

这个点云是从网上找的一个 狼的 点云。 下面的滤波也就从 这 个 上面 演示效果

可以通过 下面指令 看下 原始点云

这个样子的

===========================================================

直通滤波

直通滤波:对指定的某一纬度 实行 一个 简单 的滤波,即去掉指定范围内部或者外部的点。

    /* 声明 直通滤波 后 的点云 */
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_PassThrough_filtered (new       pcl::PointCloud<pcl::PointXYZ>);

    /* 声明直通滤波 的 类实例 */
    pcl::PassThrough< pcl::PointXYZ >  pass;
    /* 设置输入点云 */
    pass.setInputCloud (cloud);
    /* 设置滤波的维度 */
    pass.setFilterFieldName ("z");
    /* 设置滤波的范围 */
    pass.setFilterLimits (-50.0,50.0);
    /* 设置去掉的 是 范围内(true) 还是 范围外(false)   */
    pass.setFilterLimitsNegative (1);
    /* 执行滤波 返回 滤波后 的 点云 */
    pass.filter (*cloud_PassThrough_filtered);


    // 显示直通滤波后的点云
    /* 创建显示 类 实例 */
    pcl::visualization::CloudViewer viewer1("Cloud Viewer PassThrough"); 

    /* 显示点云 */
    viewer1.showCloud(cloud_PassThrough_filtered);

        /*持续显示*/
    while (!viewer1.wasStopped ())
    {
    }

总结下代码 实现步骤
1、声明直通滤波 的 类实例
2、设置输入点云
3、设置滤波的维度
4、设置滤波的范围
5、设置去掉的 是 范围内(true) 还是 范围外(false)
6、执行滤波 返回 滤波后 的 点云

然后通过显示 来 看下 滤波后的效果

//将这个改为0  即 去除 范围外 的 点 的效果
 pass.setFilterLimitsNegative (0);

下半部分被保存

===========================================================

体素滤波

体素滤波是 体素化网格方法实现下采样,即减少点的数量,并同时保持点云的形状特征,在提高配准、曲面重建、形状识别等算法速度中非常实用。

说直白点就是 一个体素 就是一个小的单元,这个单元里面 原始点云有很多点的话会求 这些 点 的 重心点,然后 在这个单元中 多个点就变成了一个点

    /* 声明 体素滤波 后 的点云 */
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_VoxelGrid_filtered (new pcl::PointCloud<pcl::PointXYZ>);

    /* 声明体素滤波 的 类实例 */
    pcl::VoxelGrid<pcl::PointXYZ> sor;
    /* 设置输入点云 */
    sor.setInputCloud (cloud);
    /* 设置体素滤波的参数 */  //10.1 米的立方体
    sor.setLeafSize (10.1f, 10.1f, 10.1f);
    /* 执行滤波 返回 滤波后 的 点云 */
    sor.filter (*cloud_VoxelGrid_filtered);

        // 显示体素滤波后的点云
    /* 创建显示 类 实例 */
    pcl::visualization::CloudViewer viewer2("Cloud Viewer VoxelGrid"); 

    /* 显示点云 */
    viewer1.showCloud(cloud_VoxelGrid_filtered);

        /*持续显示*/
    while (!viewer1.wasStopped ())
    {
    }

总结下代码 实现步骤
1、声明体素滤波 的 类实例
2、设置输入点云
3、设置体素滤波的参数
4、执行滤波 返回 滤波后 的 点云

然后通过显示 来 看下 滤波后的效果

滤波前


滤波后

可以看到点云稀疏了很多,但是外形没有变化。面部细节少了很多

===========================================================

离群点滤波

顾名思义,就是去掉 离 点云群较远的点。

    /* 声明 离群点滤波 后 的点云 */
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_OutRemove_filtered (new pcl::PointCloud<pcl::PointXYZ>);

    /* 声明 离群点滤波 的 类实例 */
    pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor_OutRemove;
    /* 设置输入点云 */
    sor_OutRemove.setInputCloud (cloud);
    /* 设置在进行统计时考虑查询点邻近点数 */
    sor_OutRemove.setMeanK (30);
    /* 设置判断是否为离群点 的 阈值  设置为1的 话 表示为:如果一个点的距离超过平均距离一个标准差以上则为离群点 */
    sor_OutRemove.setStddevMulThresh (1.0);
     /* 执行滤波 返回 滤波后 的 点云 */
    sor_OutRemove.filter (*cloud_OutRemove_filtered);


    /* 打印滤波前后的点数 */
    std::cout<<"size of clound :"<<cloud->points.size()<<"   size of  outremove "<<cloud_OutRemove_filtered->points.size()<<endl;

总结下代码 实现步骤
1、声明 离群点滤波 的 类实例
2、设置输入点云
3、设置在进行统计时考虑查询点邻近点数
4、设置判断是否为离群点 的 阈值 设置为1的 话 表示为:如果一个点的距离超过平均距离一个标准差以上则为离群点
5、执行滤波 返回 滤波后 的 点云


滤波前3400个点 ,滤波后2702个点

同样和直通滤波类似,可以设置

pass.setFilterLimitsNegative (0);

来返回被滤掉的离群点

===========================================================

投影滤波

通过使用参数化模型,将点投影到参数化模型上。参数化模型通过一组参数来设定,对于平面来说,形式也就是
ax+by+cz+d = 0
在pcl中有特意存储常见模型系数的数据结构

    /* 声明 投影 滤波 后 的点云 */
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_projected (new pcl::PointCloud<pcl::PointXYZ>);

    /*定义模型 系数 对象 ,并填充 对应 的数据  如果是 平面 ax+by+cz+d = 0  投影到z平面 即 a、b、d 为0  c为1 */
    pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());
    coefficients->values.resize (4);
    coefficients->values[0] = 0;
    coefficients->values[1] = 0;
    coefficients->values[2] = 1;
    coefficients->values[3] = 0;

    /* 声明 投影滤波 的 类实例  */
    pcl::ProjectInliers<pcl::PointXYZ> proj;
    /* 设置参数化模型的类型 */
    proj.setModelType (pcl::SACMODEL_PLANE);
    /* 设置输入点云 */
    proj.setInputCloud (cloud);
    /* 设置上面填充完的模型 */
    proj.setModelCoefficients (coefficients);
    /*  执行滤波 返回 滤波后 的 点云 */
    proj.filter (*cloud_projected);

总结下代码 实现步骤
1、定义模型 系数 对象 ,并填充 对应 的数据
2、声明 投影滤波 的 类实例
3、设置参数化模型的类型
4、设置输入点云
5、设置上面填充完的模型
6、执行滤波 返回 滤波后 的 点云

上面的模型为平面 模型 方程即为 ax+by+cz+d = 0
设置参数 为 a=b=d = 0 c=1
即投影到z平面

下面时投影的效果

被压平了

===========================================================

Result

原始点云

直通滤波


体素滤波

投影滤波