本小节演示了如何在滤波器模块使用几种不同的方法移除离群点。首先,我们将看看如何使用ConditionalRemoval滤波器,它可以一次删除满足对输入的点云设定的一个或多个条件指标的所有数据点。然后我们将学习如何使用RadiusOutlierRemoval滤波器,它可以删除在输入的点云一定范围内没有至少达到足够多近邻的所有数据点。
代码
首先,在PCL(Point Cloud Learning)中国协助发行的书[1]提供光盘的第8章例6文件夹中,打开名为remove_outliers.cpp的代码文件。
RadiusOutlierRemoval背景知识
如图1所示,有助于形象化理解RadiusOutlierRemoval的作用,在点云数据中,用户指定每个的点一定范围内周围至少要有足够多的近邻。例如,如果指定至少要有1个邻居,只有黄色的点会被删除,如果指定至少要有2个邻居,黄色和绿色的点都将被删除。
图1 RadiusOutlierRemoval滤波处理示意图
ConditionalRemoval背景知识
无需过多解释,这个滤波器删除点云中不符合用户指定的一个或多个条件的数据点。
解释分析
下面解析源代码的关键语句,首先程序会确保用户输入了正确的命令行参数。
if(argc!=2)
{
std::cerr<<"please specify command line arg '-r' or '-c'"<<std::endl;
exit(0);
在接下来的几行,我们首先定义了点云对象,并对其中一个赋予随机点数据。
pcl::PointCloud<pcl::PointXYZ>::Ptrcloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptrcloud_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);
下面的代码对于两个滤波器有一点不同,取决于使用的是哪一个滤波器类。如果用户希望使用CondtionalRemoval类,需要指定命令行参数“-c”。这会触发一个if语句,并执行以下这些代码:
elseif(strcmp(argv[1],"-c")==0){
// 创建条件限定下的滤波器
pcl::ConditionAnd<pcl::PointXYZ>::Ptrrange_cond(new pcl::ConditionAnd<pcl::PointXYZ>()); //创建条件定义对象
//为条件定义对象添加比较算子
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new pcl::FieldComparison<pcl::PointXYZ>("z",pcl::ComparisonOps::GT,0.0)));
//添加在z字段上大于0的比较算子
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new pcl::FieldComparison<pcl::PointXYZ>("z",pcl::ComparisonOps::LT,0.8)));
//添加在z字段上小于0.8的比较算子
// 创建滤波器并用条件定义对象初始化
pcl::ConditionalRemoval<pcl::PointXYZ> condrem(range_cond);
condrem.setInputCloud(cloud); //设置输入点云
condrem.setKeepOrganized(true); //设置保持点云的结构
condrem.filter (*cloud_filtered); //执行条件滤波,存储结果到cloud_filtered
创建和填充输入点云数据后,我们创建一个指定条件,只有满足条件的数据才能保留在点云中。要做到这一点,我们必须添加两个比较的条件。我们使用大于0.0和小于0.8这两个条件用于建立滤波器。
对于RadiusOutlierRemoval滤波器来说,用户必须指定“-r”作为命令行参数代码才会执行。
if(strcmp(argv[1],"-r")==0){
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滤波器对象,设置它的参数并将其应用到我们输入的点云数据,搜索半径设为0.8,在此半径内点必须要有至少两个邻居时,此点才会被保留。
在这两种情况下上面的代码都将创建我们要使用的滤波器对象,并设置滤波器工作的必需参数。下面的代码只是负责输出滤波前的点云和无论应用何种滤波器作用后的点云。
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;
// 打印出点云坐标
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<<" "
编译和运行程序
利用光盘提供的CMakeLists.txt文件,在cmake中建立工程文件,并生成相应的可执行文件,生成执行文件后,就可以运行了。如果你想使用ConditionalRemoval只需执行:
...>remove_outliers.exe -c
或者如果你想使用RadiusOutlierRemoval只需执行:
...>remove_outliers.exe -r
你会看到类似图2所示的结果(取决于你使用哪一个滤波器),RadiusOutlierRemoval滤波器类非常适合去除单个的离群点,ConditionalRemoval比较灵活,可以根据用户设置的条件灵活过滤。
图2 例6条件滤波与离群点滤波的运行结果
敬请关注PCL(Point Cloud Learning)中国更多的点云库PCL(Point Cloud Library)相关官方教程。
参考文献:
1.朱德海、郭浩、苏伟.点云库PCL学习教程(ISBN 978-7-5124-0954-5)北京航空航天出版社 2012-10