点云滤波的重要性及方法
1 点云为什么需要滤波
在获取点云数据时,由于设备的精度、周围环境等因素,点云数据中将不可避免的出现一些噪点,只有在滤波预处理中将噪点、离群点、孔洞按照后续处理定制,才能更好的进行配准,特征提取,曲面重建等操作。
我们可以很明显的看到下图的右下方有很多离散的点,而这些噪声点对于我们后续的操作完全没有任何意义,甚至会产生不利的影响。
1.1 哪些情况需要滤波
- 点云数据密度不均匀
- 因为遮挡等问题造成离群点需要去除
- 大量数据需要下采样
- 噪声数据需要去除
2 几种现有的滤波方法及其适用领域
2.1 直通滤波
对于在空间分布有一定空间特征的点云数据,比如使用线结构光扫描的方式采集点云,沿z向分布较广,但x,y向的分布处于有限范围内。此时可使用直通滤波器,确定点云在x或y方向上的范围,可较快剪除离群点,达到第一步粗处理的目的。(在Z轴方向除去多余的点)。
2.2 体素滤波
体素的概念类似于像素,使用AABB包围盒将点云数据体素化,一般体素越密集的地方信息越多,噪音点及离群点可通过体素网格去除。另一方面如果使用高分辨率相机等设备对点云进行采集,往往点云会较为密集。过多的点云数量会对后续分割工作带来困难。体素滤波器可以达到向下采样同时不破坏点云本身几何结构的功能。
2.3 统计滤波
考虑到离群点的特征,则可以定义某处点云小于某个密度,既点云无效。计算每个点到其最近的k个点平均距离。则点云中所有点的距离应构成高斯分布。给定均值与方差,可剔除3∑之外的点。
2.4 条件滤波
条件滤波器通过设定滤波条件进行滤波,有点分段函数的味道,当点云在一定范围则留下,不在则舍弃。
2.5 半径滤波
半径滤波器与统计滤波器相比更加简单粗暴,以某点为中心画一个圆计算落在该圆中点的数量,当数量大于给定值时,则保留该点,数量小于给定值则剔除该点。此算法运行速度快,依序迭代留下的点一定是最密集的,但是圆的半径和圆内点的数目都需要人工指定。
3 统计滤波的实现
第二节所列出的5种滤波方法在pcl库中都有相应封装好的函数,直接进行调用就能对pcd文件进行滤波操作。
下面是统计滤波具体实现代码(cpp源文件):
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.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>);
// 定义读取对象
pcl::PCDReader reader;
// 读取点云文件
reader.read<pcl::PointXYZ>(argv[1], *cloud);
std::cerr << "Cloud before filtering: " << std::endl;
std::cerr << *cloud << std::endl;
// 创建滤波器,对每个点分析的临近点的个数设置为50 ,并将标准差的倍数设置为1 这意味着如果一
//个点的距离超出了平均距离一个标准差以上,则该点被标记为离群点,并将它移除,存储起来
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; //创建滤波器对象
sor.setInputCloud(cloud); //设置待滤波的点云
sor.setMeanK(50); //设置在进行统计时考虑查询点临近点数
sor.setStddevMulThresh(1.0); //设置判断是否为离群点的阀值
sor.filter(*cloud_filtered); //存储
std::cerr << "Cloud after filtering: " << std::endl;
std::cerr << *cloud_filtered << std::endl;
//保存滤波后的点云
pcl::PCDWriter writer;
writer.write<pcl::PointXYZ>("after_filter.pcd", *cloud_filtered, false);
//sor.setNegative(true);
//sor.filter(*cloud_filtered);
//writer.write<pcl::PointXYZ>("1_outliers.pcd", *cloud_filtered, false);
return (0);
}
本人所使用的编译环境为Ubuntu16.04 + ros-kinetic(ros自带pcl1.7)+ roboware(IDE),所以只需要在CMakeLists.txt中进行库的调用就能使用pcl库了。这里强烈推荐使用roboware,它能帮我们自动生成相应的CMakeLists文件,很方便我们进行库文件管理,节省很多开发时间。考虑到普适性,这里给出适用于只安装了pcl库的版本。
CMakeLists.txt文件代码:
cmake_minimum_required( VERSION 2.8 )
PROJECT(statisticalOutlierRemoval)
set( CMAKE_BUILD_TYPE "Release" )
find_package(PCL 1.7 REQUIRED COMPONENTS)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable(statisticalOutlierRemoval
statisticalOutlierRemoval.cpp
)
target_link_libraries(statisticalOutlierRemoval
${PCL_LIBRARIES}
)
写好两个文件(cpp源文件与CMakeLists文件)后放入同一文件夹下
然后执行如下操作:
mkdir build
cd build
cmake …
make
生成相应的可执行文件如下图:
在此文件夹下打开终端执行该文件:
./statisticalOutlierRemoval 1.pcd
通过终端的显示我们可以看到有将近1W个点被过滤掉,最后我们通过以下命令进行可视化(终端路径不变):
pcl_viewer 1.pcd
pcl_viewer after_filter.pcd
通过对比我们可以发现,在经过统计滤波操作后,原图片中大量的离散的噪点被去除掉,虽然剩下的点云仍然有我们不需要也不希望存在的部分,但是过滤掉一些噪点仍然有助于我们后续的处理。
如果该文章对您有帮助,请顺便点个赞,如果文中有错误的地方,请提出并指正!