既然已经学习了CC中的滤波功能,就想着把PCL中的滤波功能也顺便学习一下。
PassThrough过滤器其功能非常的简单,是在用户给定的点云某个字段的限定下,对点云进行简单的基本过滤,就比如我们如果只想要高度0.5~1m之间的点云,则我们只需要将其中的范围设置为0.5-1.0即可。
类pcl::PassThrough< PointT >中主要的函数:
详细描述请看:http://docs.pointclouds.org/trunk/classpcl_1_1_pass_through.html
代码实现:
#include
#include //pcl中关于读写类的头文件
#include //pcl中点类型的头文件
#include //pcl中的直通过滤器头文件
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//读取文件
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); //创建一个:PointCloud boost共享的智能指针
if (pcl::io::loadPCDFile<pcl::PointXYZ>("Grass.pcd",*cloud)==-1) //加载文件并判断是否加载成功
{
PCL_ERROR("Can't open pcd file!!!");
return 0;
}
pcl::PointCloud<pcl::PointXYZ> cloudWrite; //创建一个点云数据对象
//pcl::KdTreeFLANN::Ptr tree(new pcl::KdTreeFLANN); // 建立kdtree
//直通滤波器
pcl::PassThrough<pcl::PointXYZ> passThrough(false); //其中的false表示不允许提取被删除点云的索引
passThrough.setInputCloud(cloud); //设置输入的点云
passThrough.setFilterFieldName("z"); //设置要进行过滤的字段
passThrough.setFilterLimits(-1.5, -0.5); //设置进行过滤的范围
passThrough.filter(cloudWrite); //执行滤波过程
//写入文件
if (pcl::io::savePCDFileASCII("Grass_test.pcd",cloudWrite)==-1) //保存点云数据
{
PCL_ERROR("Writing failed!!!");
}
cout << "处理成功!!!" << endl;
return a.exec();
}
因为我现在对PCL中的可视化类不太熟悉,所以就把过滤之后的数据用CC软件打开显示了一下哈哈。
VoxelGrid方法是根据我们输入的点云数据构建一个三维体素栅格并进行下采样从而达到滤波效果的方法,在这个过程中每一个体素所包含的点最终都会被这些点的质心所代替,这种方式虽然比用体素中心近似它们要慢一点,但是它更准确地表示了表面。而且使用该方法可以在保证点云的形状特征的同时,实现对原始点云数据的压缩,这对于之后提高点云配准、曲面重建、形状识别等过程有着很大的影响。
关于类VoxelGrid的详细描述:http://docs.pointclouds.org/trunk/classpcl_1_1_voxel_grid.html#details
核心代码:
//VoxelGrid数据压缩
pcl::VoxelGrid<pcl::PointXYZ> voxelGrid; //创建VoxelGrid对象
voxelGrid.setInputCloud(cloud); //设置输入点云
voxelGrid.setLeafSize(0.1f, 0.1f, 0.1f); //设置体素大小0.1m*0.1m*0.1m
voxelGrid.filter(cloudWrite);
处理效果:
(1)原数据
(2)VoxelGrid方法处理之后的点云数据
激光扫描通常会产生密度不均匀的点云数据,除此之外测量中的误差也会产生稀疏的离群点,而使用估计局部点云特征的运算较为复杂,且会导致错误。因此为了解决这其中的部分问题,就出现了像StatisticalOutlierRemoval这样的方法,该方法可以通过查询点与邻域点集之间的距离统计判断来进行过滤离群点。对于这其中的每一个点,可以计算出它到它所有临近点的平均距离,则大于这个平均距离的点就可以被定义为是离散点而被删除。
类StatisticalOutlierRemoval的详细描述:http://docs.pointclouds.org/trunk/classpcl_1_1_statistical_outlier_removal.html
核心代码:
//StatisticalOutlierRemoval过滤器
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; //创建StatisticalOutlierRemoval类对象
sor.setInputCloud(cloud);
sor.setMeanK(50); //设置查询临近点的点数
sor.setStddevMulThresh(1.0); //设置判读为离散点的阈值
sor.filter(cloudWrite);
这两种过滤器就相对好理解很多,ConditionalRemoval过滤器从名字就可以看出,它会删除满足对输入点云设置的一个或多个条件的所有数据点,它更像是直通滤波器的强化版;而RadiusOutlierRemoval过滤器其原理则是为输入的点云中每一个点设定一个范围(半径为r的圆),如果在该范围内没有达到某一个设定的点数值,则该数据点将会被删除,重复上述此过程直到最后一个数据点,即完成该滤波过程。
可以通过下图更好的理解RadiusOutlierRemoval方法,如图所示:
如果指定至少要有一个邻居,则最左侧的点将会被删除。
类ConditionalRemoval详细描述:http://docs.pointclouds.org/trunk/classpcl_1_1_conditional_removal.html
类RadiusOutlierRemoval详细描述:http://docs.pointclouds.org/trunk/classpcl_1_1_radius_outlier_removal.html
核心代码:
ConditionalRemoval:
// 创建环境
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new
pcl::ConditionAnd<pcl::PointXYZ>()); //创建条件“与”对象
//添加比较算子
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, -1.5))); //z值大于-1.5的点云数据点
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, -0.5))); //z值小于-0.5的点云数据点
// 创建滤波器
pcl::ConditionalRemoval<pcl::PointXYZ> condrem(range_cond);
condrem.setInputCloud(cloud);
condrem.setKeepOrganized(true); //设置保持点云的结构
// 应用滤波器
condrem.filter(cloudWrite);
RadiusOutlierRemoval:
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
// 创建滤波器
outrem.setInputCloud(cloud);
outrem.setRadiusSearch(0.1); //设置搜索的半径
outrem.setMinNeighborsInRadius(5); //设置至少要有多少个邻居
// 应用滤波器
outrem.filter(cloudWrite);
参考书籍:《点云库PCL学习教程》