统计滤波
一、算法原理
1、原理概述
激光雷达产生的点云数据集时有时会错误的接收回波信号,这个会导致产生一些错误的值时,而由于扫描结果所造成的测量误差的影响,在对点云进行其他处理时必然会产生误差,而由于设备的精度、周围环境等因素,点云数据中将不可避免的出现一些噪点,只有在滤波处理中将点云中的离群点及噪点进行去除,才能对后续点云数据进行更好的处理,例如点云分割、配准、重建等操作。为了去除掉这些误差,采用统计滤波方法进行处理激光点云数据统计滤波是对每个点的邻域进行统计分析,将超出正常范围值的点定义为离群噪点进行去除。离群点云通常是在大部分点云之外的零散点云,这种噪声对点云的成像、分割和后续目标识别都有较大影响,这类点的空间距离要大于普通点云之间的距离,在点云成像中,点云数量可以表示一块空间中的成像物体,一块区域内点云数量越多,越密集,则可以说明这块区域内所包含信息越多,越有可能含有物体,而数目越少则越有可能不是被测目标,而为噪声点。通过对点云内平均距离计算在,在一定范围之外的点,可以视为离群点去除。如图1为点云统计滤波示意图,在此计算点云模型如图1所示,计算点四邻域内的平均距离为黑圈,而计算阈值为绿圈,a,c 两点均为有效点云,而超过阈值距离的点 b 被视为离群点剔除。
2、计算过程
算法流程图如图所示,对所有点云进行邻域计算,计算每个点与其 k k k 邻域之间的距离集合,再计算所有点到其 k k k 邻域距离的均值 μ μ μ 与标准差 σ σ σ,则距离阈值 d m a x d_{max} dmax 可表示为 d m a x = μ + α × σ d_{max}=μ+α×σ dmax=μ+α×σ, α α α 是比例系数;对所有点云进行处理,对比剔除与 k k k 个邻居点的平均距离大于 d m a x d_{max} dmax 的点。算法的步骤为:
-
计算点云总数目 n n n,对于点 p p p 通过 k k k 邻域搜索找出它邻域,并计算出 p p p 点距离其邻域每个点的平均距离;
-
将得到的 n n n 个距离集合{ d 1 , d 2 , d 3 , … … d n d_1,d_2,d_3,……d_n d1,d2,d3,……dn},进行均值 μ μ μ 和标准差 σ σ σ 的计算;
均值为:
μ = 1 n ∑ i = 1 n d i (1) μ=\frac{1}{n}\sum_{i=1}^n\ d_i\tag{1} μ=n1i=1∑n di(1)
标准差为:
σ = 1 n ∑ i = 1 n ( d i − μ ) 2 (2) σ= \sqrt{\frac{1}{n}\sum_{i=1}^n\ (d_i-μ)^2}\tag{2} σ=n1i=1∑n (di−μ)2 (2) -
根据均值 μ μ μ 和标准差 σ σ σ 得到基于高斯分布的最大阈值 d m a x = μ + α × σ d_{max}=μ+α×σ dmax=μ+α×σ,其中 a a a 为标准差系数, a a a 的大小决定阈值取值范围,对于不同的点云分布和不同程度的噪声, a a a 的取值也需变换,如果系数取得过小会将部分有用点云也视为离群点去除,如果过大则去噪效果会不好,需要选取适当的参数,才能得到较好的滤波效果。
-
比较每个平均距离 μ μ μ 与阈值 d m a x d_{max} dmax 的大小并对点进行处理,将超过阈值 d m a x d_{max} dmax值得点定义为离群噪声点去除。
二、代码实现
from pclpy import pcl
def point_cloud_viewer(cloud, cloud_filtered):
# Open 3D viewer and add point cloud and normals
viewer = pcl.visualization.PCLVisualizer("viewer")
v0 = 1
viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v0)
viewer.setBackgroundColor(0.0, 0.0, 0.0, v0)
single_color = pcl.visualization.PointCloudColorHandlerCustom.PointXYZ(cloud, 0.0, 255.0, 0.0)
viewer.addPointCloud(cloud, single_color, "sample cloud1", v0)
v1 = 2
viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v1)
viewer.setBackgroundColor(0.0, 0.0, 0.0, v1)
single_color = pcl.visualization.PointCloudColorHandlerCustom.PointXYZ(cloud_filtered, 255.0, 0.0, 0.0)
viewer.addPointCloud(cloud_filtered, single_color, "sample cloud2", v1)
viewer.setPointCloudRenderingProperties(0, 1, "sample cloud1", v0)
viewer.setPointCloudRenderingProperties(0, 1, "sample cloud2", v1)
viewer.addCoordinateSystem(1.0)
while not viewer.wasStopped():
viewer.spinOnce(10)
if __name__ == '__main__':
# 读取点云数据
cloud = pcl.PointCloud.PointXYZ()
filtered_cloud = pcl.PointCloud.PointXYZ()
pcl.io.loadPCDFile('table_scene_lms4001.pcd', cloud)
# reader = pcl.io.PCDReader()
# reader.read('table_scene_lms4001.pcd', cloud)
print("滤波前点的个数为: ", cloud.size())
# 创建sor滤波器
sor = pcl.filters.StatisticalOutlierRemoval.PointXYZ()
sor.setInputCloud(cloud)
sor.setMeanK(50)
sor.setStddevMulThresh(1.0)
sor.filter(filtered_cloud)
print("滤波后点的个数为: ", filtered_cloud.size())
writer = pcl.io.PCDWriter()
writer.write("table_scene_lms400_inliers.pcd", filtered_cloud)
cloud_filtered_outliers= pcl.PointCloud.PointXYZ()
sor.setNegative(True)
sor.filter(cloud_filtered_outliers)
writer.write("table_scene_lms400_outliers.pcd", cloud_filtered_outliers)
# 主循环
point_cloud_viewer(cloud, filtered_cloud)