PCL——点云滤波处理

目录

1. 在PCL中使用直通滤波器对点云进行滤波处理

2. 使用 VoxelGrid 滤波器对点云进行下采样

3.  使用 StatisticalOutlierRemoval 滤波器移除离群点

4. 使用参数化模型投影点云

5. 从一个点云中提取索引(*)

6. 使用 ConditionalRemoval 或 RadiusOutlierRemoval 移除离群点


点云处理过程中,滤波处理作为预处理的第一步,去除的是点云数据中存在的一些离主体点云即被测物体点云较远的离散点,即离群点

PCL 中点云滤波方案

PCL 中总结了集中需要进行点云滤波处理的情况,分别为:

1) 点云数据密度不规则需要平滑;

2)因为遮挡等问题造成离群点需要去除;

3) 大量数据需要进行下采样Downsample);

4)噪声数据需要去除。

对应方法如下:

1)按具体给定的规则限制过滤去除点;

2)通过常用滤波算法修改点的部分属性;

3)对数据进行下采样

 

双边滤波算法

双边滤波算法是通过取邻近采样点的加权平均来修正当前采样点的位置,从而达到滤波的效果。同时也会选择剔除部分与当前采样点“差异”太大的相邻采样点,从而达到保持原特征的目的。

1. 在PCL中使用直通滤波器对点云进行滤波处理

实现去掉在用户指定范围内部(外部)的点。

代码解析:

利用随机数生成点云,作为点云的输入点云数据,并打印

// 生成并填充点云数据
	cloud->width = 5;		// 设置点云宽度及数量
	cloud->height = 1;		// 设置点云高度或标准
	cloud->points.resize(cloud->width * cloud->height);

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

	std::cerr << "Cloud before filtering: " << std::endl;
	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;

创建直通滤波器对象

设立它的参数,滤波字段名被设为 Z 轴方向,可接受的范围为(0,0,1.0),即将点云中所有点的 Z 坐标不在该范围内的点过滤掉(false)或保留(true),由函数 FilterLimitsNegative 设定。

// 设置滤波器对象
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);				// 设置输入点云
pass.setFilterFieldName("z");			// 设置过滤时所需要的点云类型的Z字段
pass.setFilterLimits(0.0, 1.0);			// 设置在过滤字段上的范围
pass.setFilterLimitsNegative (false);	// 设置保留范围内的还是过滤范围内的
pass.filter(*cloud_filtered);			// 执行过滤,保存过滤结果在 cloud_filtered

全部代码:

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>

int
main(int argc, char** argv)
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

	// 生成并填充点云数据
	cloud->width = 5;		// 设置点云宽度及数量
	cloud->height = 1;		// 设置点云高度或标准
	cloud->points.resize(cloud->width * cloud->height);

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

	std::cerr << "Cloud before filtering: " << std::endl;
	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;

	// 设置滤波器对象
	pcl::PassThrough<pcl::PointXYZ> pass;
	pass.setInputCloud(cloud);				// 设置输入点云
	pass.setFilterFieldName("z");			// 设置过滤时所需要的点云类型的Z字段
	pass.setFilterLimits(0.0, 1.0);			// 设置在过滤字段上的范围
	pass.setFilterLimitsNegative (false);	// 设置保留范围内的还是过滤范围内的
	pass.filter(*cloud_filtered);			// 执行过滤,保存过滤结果在 cloud_filtered

	std::cerr << "Cloud after filtering: " << std::endl;
	for (size_t i = 0; i < cloud_filtered->points.size(); ++i)
		std::cerr << "    " 
		<< cloud_filtered->points[i].x << " "
		<< cloud_filtered->points[i].y << " "
		<< cloud_filtered->points[i].z << std::endl;

	return (0);
}

2. 使用 VoxelGrid 滤波器对点云进行下采样

采用体素化网格的方法实现下采样,即减少点的数量减少点云数据,并同时保持点云的形状特征。

PCL 实现的 VoxelGrid 类通过输入的点云数据创建一个三维体素栅格(微小的空间三维立方体的集合),然后在每个体素(三维立方体)内用体素中的所有点的重心来近似显示体素中其他点,对于所有体素处理后得到过滤的点云。

代码解释分析:

从磁盘中读取点云数据

pcl::PCDReader reader;//点云读取对象
reader.read("table_scene_lms400.pcd", *cloud); // 读取点云文件中的数据到 cloud 对象

创建一个叶大小为 1cm 的 pcl::VoxelGrid 滤波器(sor),输入数据作为滤波器的输入,滤波计算后的输出被存储在 cloud_filtered

pcl::VoxelGrid<pcl::PCLPointCloud2> sor;	// 创建滤波对象
sor.setInputCloud(cloud);					// 设置需要过滤的点云给滤波对象
sor.setLeafSize(0.01f, 0.01f, 0.01f);		// 设置滤波时创建的体素体积为 1 cm3的立方体
sor.filter(*cloud_filtered);				// 执行滤波处理,存储在 cloud_filtered

3.  使用 StatisticalOutlierRemoval 滤波器移除离群点

本节使用统计分析技术,从一个点云数据集中移除测量噪声点离群点)。

稀疏离群点移除方法:对每个点,计算它到它的所有邻近点的平均距离

假设得到的结果是一个高斯分布正态分布),其形状由均值和标准差决定,平均距离在标准范围之外的点,可被定义为离群点

代码解释分析:

从文件(table_scene_lms400.pcd)中读取点云数据

pcl::PCDReader reader;
reader.read<pcl::PointXYZ>("table_scene_lms400.pcd", *cloud);// 读取点云文件

创建一个 StatisticalOutlierRemoval 滤波器,

对每个点分析的邻近点个数设为 50

标准差倍数设为 1

上述说明如果一个点的距离超出平均距离一个标准差以上,该点被标记为离群点,并被移除

pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;// 创建滤波器对象
sor.setInputCloud(cloud);// 设置待滤波的点云
sor.setMeanK(50);//设置在进行统计时考虑查询点邻近点数
sor.setStddevMulThresh(1.0);// 设置判断是否为离群点的阈值
sor.filter(*cloud_filtered);// 执行滤波处理保存内点到 cloud_filtered

利用函数 setNegative 设置使输出取外点(true),获取离群点数据

sor.setNegative(true);// 设置使输出的内容为离群点

将过滤后的点云数据写入(writer)在文件(table_scene_lms400_inliers.pcd)中

pcl::PCDWriter writer;// 创建存储点云文件
writer.write<pcl::PointXYZ>("table_scene_lms400_inliers.pcd", *cloud_filtered, false);

4. 使用参数化模型投影点云

参数化模型通过一组参数来设定,对于平面来说使用其等式形式(ax + by + cz +d = 0

代码解释分析:

声明 ModelCoefficients 结构和 Project_Inliers 滤波器对应头文件

#include <pcl/ModelCoefficients.h>
#include <pcl/filters/project_inliers.h>

定义 ModelCoefficients 模型系数对象 (coefficients),并填充

本例使用一个 ax + by + cz +d = 0 的平面模型,其中 a = b = d = 0,c = 1 (x-y 面)

pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());// 定义模型系数对象
coefficients->values.resize(4);
coefficients->values[0] = 0.0;
coefficients->values[1] = 0.0;
coefficients->values[2] = 1.0;
coefficients->values[3] = 0.0;

创建 ProjectInliers 对象(proj),并使用定义好的 ModelCoefficients 作为投影对象的模型参数

pcl::ProjectInliers<pcl::PointXYZ> proj;// 创建投影滤波对象
proj.setModelType(pcl::SACMODEL_PLANE);// 设置对象对应的投影模型
proj.setInputCloud(cloud);// 设置输入点云
proj.setModelCoefficients(coefficients);// 设置模型对应的系数
proj.filter(*cloud_projected);// 执行投影滤波,存储结果在 cloud_projected

5. 从一个点云中提取索引(*

使用一个 ExtractIndices 滤波器,基于某一分割算法提取点云中的一个子集

代码解释分析:

创建一个 VoxelGrid 滤波器对数据进行下采样

pcl::VoxelGrid<pcl::PCLPointCloud2> sor;// 体素栅格下采样对象
sor.setInputCloud(cloud_blob);// 设置下采样原始点云数据
sor.setLeafSize(0.01f, 0.01f, 0.01f);// 设置采样的体素大小
sor.filter(*cloud_filtered_blob);// 执行采样,并保存数据在 cloud_filtered_blob

参数化分割处理

创建分割对象(seg

pcl::SACSegmentation<pcl::PointXYZ> seg;// 创建分割对象
seg.setOptimizeCoefficients(true);// 设置对估计的模型参数进行优化处理
seg.setModelType(pcl::SACMODEL_PLANE);// 设置分割模型类别
seg.setMethodType(pcl::SAC_RANSAC);// 设置用哪个随机参数估计方法
seg.setMaxIterations(1000);// 设置最大迭代次数
seg.setDistanceThreshold(0.01);// 设置判断是否为模型内点的距离阈值

设置 extraction filter 的实际参数

建立点云提取对象(extract

pcl::ExtractIndices<pcl::PointXYZ> extract;// 创建点云提取对象
extract.setInputCloud(cloud_filtered);// 设置输入点云
extract.setIndices(inliers);// 设置分割后的内点为需要提供的点集
extract.setNegative(false);// 设置提取内点(false)
extract.filter(*cloud_p);// 提取输出存储到 cloud_p

为了处理点云中包含的多个模型,在一个循环中执行该过程并在每次模型被提取后,保存剩余的点并进行迭代

模型内点通过分割过程获取

seg.setInputCloud(cloud_filtered);
seg.segment(*inliers, *coefficients);// 通过分割获取模型内点

6. 使用 ConditionalRemoval 或 RadiusOutlierRemoval 移除离群点

ConditionalRemoval 滤波器:可以删除不满足对输入点云设定的一个或多个条件指标的所有数据点

RadiusOutlierRemoval 滤波器:可以删除在输入的点云一定范围指定半径)内没有达到足够多近邻点的所有数据点

ConditionalRemoval:

在点云数据中,用户指定每个数据点的一定范围内至少要有足够多的近邻点。

RadiusOutlierRemoval:

删除点云中不符合用户指定的一个或多个条件的数据点。

代码解释分析:

创建条件定义对象(range_cond

设置比较条件:大于 0.0 和小于 0.8 

只有满足上述条件的数据才能保存在点云中

pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new pcl::ConditionAnd<pcl::PointXYZ>());//创建条件定义对象 range_cond
		
//为 range_cond 添加比较算子
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0)));// 添加在 z 字段上大于0的比较算子
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8)));// 添加在 z 字段上小于0.8的比较算子

//创建滤波器
pcl::ConditionalRemoval<pcl::PointXYZ> condrem;
condrem.setCondition(range_cond);// 条件定义对象初始化 
condrem.setInputCloud(cloud);// 设置输入点云
condrem.setKeepOrganized(true);// 设置保持点云的结构
condrem.filter(*cloud_filtered);// 执行条件滤波,存储结果到 cloud_filtered

创建半径滤波器对象(outrem

设置参数并将其应用到输入的点云数据,搜索半径设为 0.8

在此半径内,点必须要有至少两个邻居时,此点才会被保留

// 创建滤波器
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
outrem.setInputCloud(cloud);// 设置输入点云
outrem.setRadiusSearch(0.8);// 设置在半径0.8半径的范围内找到邻近点
outrem.setMinNeighborsInRadius(2);// 设置查询点的邻近点点数小于2的删除
outrem.filter(*cloud_filtered);// 执行半径滤波,存储结果到 cloud_filtered

RadiusOutlierRemoval 滤波器类非常适合去除单个离群点

ConditionalRemoval 滤波器比较灵活,可以根据用户设置的条件灵活过滤

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值