一、说明
这是我的“点云处理”教程的第 4 篇文章。“点云处理”教程对初学者友好,我们将在其中简单地介绍从数据准备到数据分割和分类的点云处理管道。
在本教程中,我们将学习如何使用 Open3D 在 python 中过滤点云以进行下采样和异常值去除。使用 Open3D 进行点云预处理
【点云处理教程】00计算机视觉的Open3D简介
【点云处理教程】01如何创建和可视化点云
【点云处理教程】02从 Python 中的深度图像估计点云
【点云处理教程】03使用 Python 实现地面检测
【点云处理教程】04 Python 中的点云过滤
【点云处理教程】05-Python 中的点云分割
二、 简介
由于所使用的3D扫描仪(如结构光扫描仪)或捕获的场景(包括吸收红外光的材料)的性质,计算或收集的点云有时会产生噪声。另一方面,一些算法和/或计算机视觉技术对噪声很敏感,例如估计表面法线和曲率变化。
为了降低噪音,使用了滤波技术。一些滤波器还用于降低点云密度,从而减少计算时间。在本文中,我们将看到一些常见的过滤器,即:直通滤波器、统计异常值去除滤波器、半径异常值去除滤波器和下采样滤波器。
2.2 直通滤波器
直通筛选器对输入数据应用约束,这些约束通常是阈值或间隔。对于点云,如果一个点满足约束,则点通过过滤器,这些约束主要是沿一个或多个轴的间隔。为了降低噪声,间隔通常根据输入设备的性质和状态进行固定:深度数据在间隔内更准确,否则会变得更加嘈杂。直通滤波器不仅可用于滤除噪声输入,还可用于减少数据,例如考虑最近的点。
在版本 0.7.0 之前,Open3D 支持以下功能:crop_point_cloud(input, min_bound, max_bound)
input
是输入点云。
min_bound
是点坐标的最小界限。
max_bound
是点坐标的最大边界。返回区间内点的点云。
例如,要过滤点云以减少沿 Z 轴的噪声,请考虑间隔。对于 X 轴和 Y 轴,我们将边界设置为无穷大,因为我们没有沿着它们进行过滤:[0.8, 3]
from open3d import *
import math
import numpy as np
cropped = crop_point_cloud(pcd,
min_bound=np.array([-math.inf, -math.inf, 0.8]),
max_bound=np.array([math.inf, math.inf, 3]))
在0.7.0版本之后,可以使用裁剪点云的方法。与前面的函数类似,此方法返回裁剪的点云。为此,我们首先创建一个边界框,其中包含将要考虑的点。此边界框是根据区间边界的组合创建的(请参见)。在这里,我们只沿 Z 轴过滤:仅返回其 z 坐标之间的点。最后,使用创建的边界框对象裁剪输入点云:crop(bounding_box)
open3d.geometry.PointCloud
bounding_box_points
[0.8, 2]
import numpy as np
import open3d as o3d
import math
import itertools
if __name__ == '__main__':
# Read point cloud:
pcd = o3d.io.read_point_cloud("../data/depth_2_pcd.ply")
# Create bounding box:
bounds = [[-math.inf, math.inf], [-math.inf, math.inf], [0.8, 2]] # set the bounds
bounding_box_points = list(itertools.product(*bounds)) # create limit points
bounding_box = o3d.geometry.AxisAlignedBoundingBox.create_from_points(
o3d.utility.Vector3dVector(bounding_box_points)) # create bounding box object
# Crop the point cloud using the bounding box:
pcd_croped = pcd.crop(bounding_box)
# Display the cropped point cloud:
o3d.visualization.draw_geometries([pcd_croped])
生成的点云如下所示:
灰色:输入点云。绿色:生成的点云
2.3 下采样
下采样点云包括减少点数。例如,它通常用于减少处理步骤的运行时间或选择确切数量的点进行训练。
Open3D 库提供了三种不同的方法来降低采样点云:
random_down_sample(pcd, sampling_ratio)
:从输入点云中选择随机点。它可用于数据增强,因为每次都会选择不同的点。但是,它对噪音很敏感:可以选择它。n*sampling_ratio
pcd
uniform_down_sample(every_k_points)
:根据点的顺序统一选择点。它在每个点选择一个点。始终选择第一个点(标记为 0)。因此,所选点的索引为:0、、2 * 等。如果输入点云是有组织的,则该函数返回均匀的点云;否则,它类似于第一种方法,只是每次都生成相同的输出。every_k_points
every_k_points
every_k_points
voxel_down_sample(voxel_size)
:创建 3D 体素网格。体素格网将输入划分为一组××体素。每个体素都包含属于 3 个轴的相同间隔的点。然后对属于同一体素的点进行下采样并替换为其质心。此过滤器用于减小点云的大小并使其平滑。但是,这很耗时,因为它在将点云重组为体素后计算质心,并且对异常值很敏感。voxel_size
voxel_size
voxel_size
现在,让我们测试所有这些方法并显示生成的点云。为了获得更好的可视化效果,我们分别设置为 、to 和 for 和 。最后,我们应用平移在同一窗口中分别显示所有点云。sampling_ratio
0.005
every_k_points
200
voxel_size
0.4
random_down_sample
uniform_down_sample
voxel_down_sample
import open3d as o3d
import numpy as np
if __name__ == '__main__':
# Read point cloud:
pcd = o3d.io.read_point_cloud("../data/depth_2_pcd.ply")
# Random down-sampling:
random_pcd = pcd.random_down_sample(sampling_ratio=0.005)
# Uniform down-sampling:
uniform_pcd = pcd.uniform_down_sample(every_k_points=200)
# Voxel down-sampling:
voxel_pcd = pcd.voxel_down_sample(voxel_size=0.4)
# Translating point clouds:
points = np.asarray(random_pcd.points)
points += [-3, 3, 0]
random_pcd.points = o3d.utility.Vector3dVector(points)
points = np.asarray(uniform_pcd.points)
points += [0, 3, 0]
uniform_pcd.points = o3d.utility.Vector3dVector(points)
points = np.asarray(voxel_pcd.points)
points += [3, 3, 0]
voxel_pcd.points = o3d.utility.Vector3dVector(points)
# Display:
o3d.visualization.draw_geometries([pcd, random_pcd, uniform_pcd, voxel_pcd])
有组织的点云下采样。上图:输入点云。向下,从左到右:随机下采样、均匀下采样和基于体素的下采样。
请注意,该方法生成的点云均匀分布在 3D 空间中。这是因为输入是一个有组织的点云(点在列表中组织)。uniform_down_sample
让我们通过洗牌前一个点云的点来创建无组织的点云,如下所示:
points = np.asarray(pcd.points)
np.random.shuffle(points)
u_pcd= o3d.geometry.PointCloud()
u_pcd.points= o3d.utility.Vector3dVector(points)
然后与前面的示例类似,我们应用不同的下采样方法并显示结果。可视化窗口如下所示:u_pcd
无组织的点云下采样。上图:输入点云。向下,从左到右:随机下采样、均匀下采样和基于体素的下采样。
在这里,该方法得到的点云在3D空间中分布不均匀。它看起来更像是随机下采样,因为这些点是无组织的。但是,返回相同的点云,因为它将点重组为 3D 网格。uniform_down_sample
voxel_down_sample
2.4. 异常值去除过滤器
- 半径异常值移除是一种条件过滤器,用于移除给定半径的球体内相邻点数少于给定数量的每个点。Open3D 提供了以下方法:
remove_radius_outlier(nb_points, radius)
nb_points
是邻居的数量。
radius
是球体半径。返回:过滤点云的元组和内变量索引的列表。
- 统计异常值移除过滤器可移除距离其相邻点较远的点。对于每个点,计算从它到其所有相邻点的平均距离。然后,如果点的平均距离超出由全局距离平均值和标准差定义的区间,则该点是异常值。Open3D 提供了以下方法: [2]:
remove_statistical_outliers(nb_neighbors, std_ratio)
nb_neighbors
是邻居的数量。
std_ratio
是标准偏差比。返回:过滤点云的元组和内变量索引的列表。
让我们测试这两种方法并显示生成的点云。为了减少运行时间,我们首先应用下采样。应用异常值去除过滤器后,我们使用 .我们设置反转索引的选择。select_by_index(index, invert)
invert
True
import open3d as o3d
import numpy as np
if __name__ == '__main__':
# Read point cloud:
pcd = o3d.io.read_point_cloud("../data/depth_2_pcd.ply")
# Down sampling to reduce the running time:
pcd = pcd.voxel_down_sample(voxel_size=0.02)
# Radius outlier removal:
pcd_rad, ind_rad = pcd.remove_radius_outlier(nb_points=16, radius=0.05)
outlier_rad_pcd = pcd.select_by_index(ind_rad, invert=True)
outlier_rad_pcd.paint_uniform_color([1., 0., 1.])
# Statistical outlier removal:
pcd_stat, ind_stat = pcd.remove_statistical_outlier(nb_neighbors=20,
std_ratio=2.0)
outlier_stat_pcd = pcd.select_by_index(ind_stat, invert=True)
outlier_stat_pcd.paint_uniform_color([0., 0., 1.])
# Translate to visualize:
points = np.asarray(pcd_stat.points)
points += [3, 0, 0]
pcd_stat.points = o3d.utility.Vector3dVector(points)
points = np.asarray(outlier_stat_pcd.points)
points += [3, 0, 0]
outlier_stat_pcd.points = o3d.utility.Vector3dVector(points)
# Display:
o3d.visualization.draw_geometries([pcd_stat, pcd_rad, outlier_stat_pcd, outlier_rad_pcd])
点云异常值去除。左:半径滤波器。右:统计过滤器。
三、 结论
为此,我们引入了最著名的点云过滤器。这些过滤器在 Open3D 中实现。它们也在其他一些点云库中实现,例如 PCL。在大多数实时应用中,尤其是对于密集点云,需要过滤和减小点云的大小。下一个教程将讨论点云分割。您将看到,您可以仅使用一些Python库来简单地执行此操作。
四、引用
[1]open3d.geometry.crop_point_cloud