在获取点云数据时,由于设备精度,操作者经验,以及环境因素等带来的影响。点云数据将不可避免的出现一些噪点

   在点云处理流程中滤波处理作为预处理的第一步,往往对后续处理影响很大。 只有在滤波处理中将噪声点,离群点,孔洞,数据压缩等按照后续处理定制,才能够更好的进行配准,特征提取,曲面重建等。


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;
}