背景知识:
激光扫描通常会产生密度不均匀的点云数据集。另外,测量中的误差会产生稀疏的离群点,使效果更糟。估计局部点云特征(例如采样点处法向量或曲率变化率)的运算很复杂,这会导致错误的数值,反过来有可能导致点云的配准等后期处理失败。以下方法可以解决其中部分问题:对每个点的邻域进行一个统计分析,并修剪掉那些不符合一定标准的点。我们的稀疏离群点移除方法基于在输入数据中对点到临近点的距离分布的计算。对每个点,我们计算它到它的所有临近点的平均距离。假设得到的结果是一个高斯分布,其形状由均值和标准差决定,平均距离在标准范围(由全局距离平均值和方差定义)之外的点,可被定义为离群点并可从数据集中去除掉。
图1展示了稀疏离群点分析和移除的效果:左图为原始数据集,右图为处理结果。该图展示了处理前和处理后,一个点邻域范围内的k近邻邻域平均距离。
图1 稀疏离群点分析和移除的效果对比图
代码
首先,在PCL(Point Cloud Learning)中国协助发行的书[1]提供光盘的第8章例3文件夹中,打开名为statistical_removal.cpp的代码文件,同文件夹下可以找到相关的测试点云文件table_scene_lms400.pcd。
解释分析
下面来解析上面打开源代码的关键语句。下列代码将从磁盘中读取点云数据。
pcl::PCDReaderreader;//定义读取对象
reader.read<pcl::PointXYZ>("table_scene_lms400.pcd",*cloud);//读取点云文件
然后,创建了一个pcl::StatisticalOutlierRemoval滤波器,将对每个点分析的临近点个数设为50,并将标准差倍数设为1,这意味着如果一个点的距离超出平均距离一个标准差以上,则该点被标记为离群点,并将被移除。计算后的输出结果储存在cloud_filtered中。
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;// 创建滤波器对象
sor.setInputCloud(cloud); //设置呆滤波的点云
sor.setMeanK(50); //设置在进行统计时考虑查询点邻近点数
sor.setStddevMulThresh(1.0); //设置判断是否为离群点的阈值
sor.filter(*cloud_filtered); //执行滤波处理保存内点到cloud_filtered
剩下的数据(内部点)将被存入磁盘,以供其他使用,例如可视化等。
pcl::PCDWriterwriter;
writer.write<pcl::PointXYZ>("table_scene_lms400_inliers.pcd",*cloud_filtered,false);
然后,使用同样的参数再次调用该滤波器,但是利用函数setNegative设置使输出取外点,以获取离群点数据(也就是原本滤除掉的点)。
sor.setNegative(true);
sor.filter(*cloud_filtered);
并将数据写回到磁盘。
writer.write<pcl::PointXYZ>("table_scene_lms400_outliers.pcd",*cloud_filtered,false);
编译和运行程序
利用光盘提供的CMakeLists.txt文件,在cmake中建立工程文件,并生成相应的可执行文件,生成执行文件后,就可以运行了,在cmd中键入命令:
...>statistical_removal.exe
你会看到类似如图2所示的结果,因为本程序的点云读取默认的点云文件当前目录下,所以用户测试时保证文件与exe文件在同一目录下,程序运行后在当前目录下生成两个点云文件,分别存储内点和外点,其可视化结果如图3所示,左边为内点的可视化结果,后边为外点的可视化结果,很明显可以看出,该滤波处理非常适合对点云中的离群点进行去除。
图2 点云去除离群点运行结果
图3 例3中滤波后得到的内点与外点可视化结果
背景知识:激光扫描通常会产生密度不均匀的点云数据集。另外,测量中的误差会产生稀疏的离群点,使效果更糟。
以下方法可以解决的其中部分问题:
对每个点的临域进行一个统计分析,并修剪掉那些不符合一定标准的点。稀疏离群点移除方法基于在输入数据中对点到临近点的距离分布的计算。对每个点,我们计算它到它的所有临近点的平均距离。假设得到的结果是一个高斯分布,其形状由均值和标准差决定,平均距离在标准范围(由全局距离平均值和方差定义)之外的点,可被定义为离群点并从数据集中去除掉。
#include<iostream>
#include<pcl\point_cloud.h>
#include<pcl\point_types.h>
#include<pcl\io\io.h>
#include<pcl\io\pcd_io.h>
#include<pcl\filters\statistical_outlier_removal.h>
#include<pcl\visualization\cloud_viewer.h>
using namespace std;
int main()
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::visualization::CloudViewer viewer("cloud_filered");
if(pcl::io::loadPCDFile("G:\\PCD_data\\rabbit.pcd",*cloud)!=-1)
return 0;
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);//设置输入点云
sor.setMeanK(50);//对每个点分析的临近点个数设为50
sor.setStddevMulThresh(1.0);标准差倍数设为1
sor.filter(*cloud_filtered);
viewer.showCloud(cloud_filtered);
while(!viewer.wasStopped())
{
}
return 0;
}