在获取点云数据时,由于设备精度,操作者经验,以及环境因素等带来的影响。点云数据将不可避免的出现一些噪点。
在点云处理流程中滤波处理作为预处理的第一步,往往对后续处理影响很大。 只有在滤波处理中将噪声点,离群点,孔洞,数据压缩等按照后续处理定制,才能够更好的进行配准,特征提取,曲面重建等。
1.PCL中点云滤波方案
PCL总结了几种需要进行点云滤波处理的情况:
- 点云数据密度不规则,需要平滑
- 因为遮挡等问题造成离群点,需要去除。
- 大量数据需要进行下采样
- 噪声数据需要去除。
2. 滤波器介绍
双边滤波器(Bilateral filter): 是一种可以保边去噪的滤波器。可以滤除图像数据中的噪声,且还会保留住图像的边缘、纹理等。
直通滤波器: 对于在空间分布有一定空间特征的点云数据,比如使用线结构光扫描的方式采集点云,沿z向分布较广,但x,y向的分布处于有限范围内。此时可使用直通滤波器,确定点云在x或y方向上的范围,可较快剪除离群点,达到第一步粗处理的目的。
体素滤波器:体素的概念类似于像素,使用AABB包围盒将点云数据体素化,一般体素越密集的地方信息越多,噪音点及离群点可通过体素网格去除。另一方面如果使用高分辨率相机等设备对点云进行采集,往往点云会较为密集。过多的点云数量会对后续分割工作带来困难。体素滤波器可以达到向下采样同时不破坏点云本身几何结构的功能。
PCL的VoxelGrid类通过输入的点云数据创建一个三维体素栅格(三维体素栅格可以想象为微小的三维立方体的集合)然后在每个体素内(即三维立方体内),用体素中所有点的重心来近似显示体素内中其他的所有点,这样该体素内所有点就用一个重心点来最终表示 ,对所有体素处理之后,就得到下采样的滤波点云。
统计滤波器:考虑到离群点的特征,则可以定义某处点云小于某个密度,既点云无效。计算每个点到其最近的k个点平均距离。则点云中所有点的距离应构成高斯分布。给定均值与方差,可剔除3∑之外的点。
条件滤波:条件滤波器通过设定滤波条件进行滤波,有点分段函数的味道,当点云在一定范围则留下,不在则舍弃。
半径滤波器:半径滤波器与统计滤波器相比更加简单粗暴。以某点为中心画一个圆计算落在该圆中点的数量,当数量大于给定值时,则保留该点,数量小于给定值则剔除该点。 此算法运行速度快,依序迭代留下的点一定是最密集的,但是圆的半径和圆内点的数目都需要人工指定。
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h> //直通滤波器头文件
#include <pcl/filters/voxel_grid.h> //体素滤波器头文件
#include <pcl/filters/conditional_removal.h> //条件滤波器头文件
#include <pcl/filters/statistical_outlier_removal.h> //统计滤波器头文件
#include <pcl/filters/radius_outlier_removal.h> //半径滤波器头文件
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/features/normal_3d.h>
using namespace std;
using namespace pcl;
//简单点云的显示
boost::shared_ptr<visualization::PCLVisualizer> viewerPointXYZ(PointCloud<PointXYZ>::ConstPtr cloud)
{
//创建可视化窗口
boost::shared_ptr<visualization::PCLVisualizer> viewer(new visualization::PCLVisualizer);
//设置背景
viewer->setBackgroundColor(0,0,0);
//在视窗中添加点云数据
viewer->addPointCloud<PointXYZ>(cloud, "PointXYZ");
//设置点云的显示渲染方式
viewer->setPointCloudRenderingProperties(visualization::PCL_VISUALIZER_POINT_SIZE, 1, "PointXYZ");
//添加坐标系
viewer->addCoordinateSystem(1);
//设置默认的摄像机参数,已从默认方向和角度观察点云
viewer->initCameraParameters();
return viewer;
}
int main()
{
//创建点云数据
PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
cloud->width = 500;
cloud->height = 1;
cloud->points.resize(cloud->width*cloud->height);
cout << "创建点云数据"<<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++)
{
cout << " " << cloud->points[i].x
<< " " << cloud->points[i].y
<< " " << cloud->points[i].z << endl;
}
cout << "原始点云数据" << endl << endl;
/*--------------------------------------------------------------------------
直通滤波器对点云的处理
*/
PointCloud<PointXYZ>::Ptr cloud_passThroud(new PointCloud<PointXYZ>);
PassThrough<PointXYZ> passthroud; //直通滤波器;
//输入点云数据
passthroud.setInputCloud(cloud);
//对Z轴进行操作
passthroud.setFilterFieldName("z");
//设置直通滤波器的操作范围
passthroud.setFilterLimits(100.0, 300.0);
//设置保留区域
passthroud.setFilterLimitsNegative(false);//true表示保留范围内,false表示保留范围外
//执行滤波操作,并将滤波结果存储到cloud_passThroud
passthroud.filter(*cloud_passThroud);
/*--------------------------------------------------------------------------
体素滤波器实现下采样
*/
PointCloud<PointXYZ>::Ptr cloud_voxelgrid(new PointCloud<PointXYZ>);
//创建体素滤波器
VoxelGrid<PointXYZ> voxelgrid;
//输入点云数据
voxelgrid.setInputCloud(cloud);
//AABB长宽高
voxelgrid.setLeafSize(10.0f, 10.0f, 10.0f);
//执行滤波操作
voxelgrid.filter(*cloud_voxelgrid);
/*--------------------------------------------------------------------------
统计滤波器滤波
*/
PointCloud<PointXYZ>::Ptr cloud_StatisticalRemover(new PointCloud<PointXYZ>);
StatisticalOutlierRemoval<PointXYZ> Statical;
Statical.setInputCloud(cloud);
//取平均值的临近点
Statical.setMeanK(20);
//临近点的数目少于多少会被舍弃
Statical.setStddevMulThresh(5); //设置阈值
Statical.filter(*cloud_StatisticalRemover);
/*--------------------------------------------------------------------------
条件滤波
*/
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);
/*--------------------------------------------------------------------------
半径滤波
*/
PointCloud<PointXYZ>::Ptr cloud_Radius(new PointCloud<PointXYZ>);
//半径滤波器
RadiusOutlierRemoval<PointXYZ> radiusoutlier;
//输入点云
radiusoutlier.setInputCloud(cloud);
//设置半径为100的范围内找临近点
radiusoutlier.setRadiusSearch(100);
//设置查询点的邻域点集数小于2的删除
radiusoutlier.setMinNeighborsInRadius(2);
//执行滤波操作
radiusoutlier.filter(*cloud_Radius);
boost::shared_ptr<visualization::PCLVisualizer> viewer;
viewer = viewerPointXYZ(cloud_after_Condition);
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
system("pause");
return 0;
}