爆肝5万字❤️Open3D 点云数据处理基础(Python版)
Posted 没事就要敲代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爆肝5万字❤️Open3D 点云数据处理基础(Python版)相关的知识,希望对你有一定的参考价值。
文章目录
历时一个月,Open3D 点云数据处理基础(Python版) 终于初具雏形!目前主要是一些基础内容,后续会继续完善,希望互相学习!
1 概述
Open3D是一个开源库,支持快速开发处理3D数据的软件。Open3D后端是用C++实现的,经过高度优化并通过Python的前端接口公开。Open3D提供了三种数据结构:点云(point cloud)、网格(mesh)和RGB-D图像。对于每个表示,open3D都实现了一整套基本处理算法,如I/O、采样、可视化和数据转换。此外,还包括一些常用的算法,如法线估计、ICP配准等。
open3D包含了九个模块,如下表所示:
模块 | 功能 |
---|---|
Geometry 几何模块 | 数据结构和基本处理算法 |
Camera 相机模块 | 相机模型和相机轨迹 |
Odometry 里程计模块 | RGB-D图像的跟踪与对齐 |
Registration 配准模块 | 全局和局部配准 |
Integration 积分模块 | 体积积分 |
I/O 输入输出模块 | 读写3维数据 |
Visualization 可视化模块 | 使用OpenGL呈现3D数据的可自定义GUI |
Utility 辅助功能模块 | 辅助功能,如控制台输出、文件系统和特征包装器 |
Python 模块 | Open3D Python绑定和教程 |
本文旨在快速入门Python点云数据处理,原理性知识会在后续专题中给出,这里不做过多描述。
2 安装
2.1 PyCharm 与 Python 安装
PyCharm Community 2021.2 安装教程(附汉化教程!)
2.3 Anaconda 安装
2.4 Open3D 0.13.0 安装
在成功安装 Anaconda3 之后,在所有应用中找到 Anaconda3,选择 Anaconda Powershell Prompt
打开之后如下所示
手动输入
pip install open3d
开始安装 open3d-0.13.0,需要漫长的等待…
安装成功!
2.5 新建一个 Python 项目
文件 > 新建项目,输入(若路径不存在,则自动创建)或选择位置路径,选择已经由Conda配置好的解释器
新建Python文件
输入新建的Python文件名,按回车完成新建
测试代码
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("test.pcd")
print(pcd)
print("->正在保存点云")
o3d.io.write_point_cloud("write.pcd", pcd, True) # 默认false,保存为Binarty;True 保存为ASICC形式
print(pcd)
3 点云读写
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("test.pcd")
print(pcd)
print("->正在保存点云")
o3d.io.write_point_cloud("write.pcd", pcd, True) # 默认false,保存为Binarty;True 保存为ASICC形式
print(pcd)
输出结果:
->正在加载点云...
PointCloud with 356478 points.
->正在保存点云
PointCloud with 356478 points.
默认情况下,Open3D尝试通过文件扩展名推断文件类型。支持以下点云文件类型:
格式 | 描述 |
---|---|
xyz | 每一行包含 [x, y, z] , 其中的x , y , z 是三维坐标 |
xyzn | 每一行包含 [x, y, z, nx, ny, nz] , 其中的 nx , ny , nz 是法线向量 |
xyzrgb | 每一行包含 [x, y, z, r, g, b] , 其中的 r , g , b 是范围在 [0, 1] 的float类型 |
pts | 第一行是表示点数的整数。每个后续行包含[x, y, z, i, r, g, b] , 其中的 r , g , b 是uint8 类型 |
ply | 查看 Polygon File Format, ply文件能够同时包含点云和网格数据 |
pcd | 查看 Point Cloud Data |
也可以显式地指定文件类型。在这种情况下,文件扩展名将被忽略。
pcd = o3d.io.read_point_cloud("../../test_data/my_points.txt", format='xyz')
4 点云可视化
2.1 可视化单个点云
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("test.pcd")
print(pcd)
print("->正在可视化点云")
o3d.visualization.draw_geometries([pcd])
输出结果:
->正在加载点云...
PointCloud with 356478 points.
->正在可视化点云
可视化结果展示:
可以通过键盘上的 +
、-
,实时调整点的大小。
2.2 同一窗口可视化多个点云
函数描述:
o3d.visualization.draw_geometries([pcd1, pcd2, ... ,pcdn])
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云1... ")
pcd1 = o3d.io.read_point_cloud("bunny.pcd")
print(pcd1)
print("->正在加载点云2...")
pcd2 = o3d.io.read_point_cloud("bunny0.pcd")
print(pcd2)
print("->正在同时可视化两个点云...")
o3d.visualization.draw_geometries([pcd1, pcd2])
输出结果:
->正在加载点云1...
PointCloud with 35947 points.
->正在加载点云2...
PointCloud with 35947 points.
->正在同时可视化两个点云...
可视化结果:
2.3 可视化的属性设置
函数原型1:
draw_geometries(geometry_list, window_name='Open3D', width=1920, height=1080, left=50, top=50, point_show_normal=False, mesh_show_wireframe=False, mesh_show_back_face=False)
参数说明
参数名 | 描述 |
---|---|
geometry_list | 要可视化的几何体列表 |
window_name | (str, optional, default='Open3D') ,可视化窗口的显示标题 |
width | (int, optional, default=1920) ,可视化窗口的宽度 |
height | (int, optional, default=1080) ,可视化窗口的高度 |
left | (int, optional, default=50) ,可视化窗口的左边距 |
top | (int, optional, default=50) ,可视化窗口的顶部边距 |
point_show_normal | (bool, optional, default=False) ,如果设置为true,则可视化点法线,需要事先计算点云法线 |
mesh_show_wireframe | (bool, optional, default=False) ,如果设置为true,则可视化网格线框 |
mesh_show_back_face | (bool, optional, default=False) ,可视化网格三角形的背面 |
函数原型2:
draw_geometries(geometry_list, window_name='Open3D', width=1920, height=1080, left=50, top=50, point_show_normal=False, mesh_show_wireframe=False, mesh_show_back_face=False, lookat, up, front, zoom)
参数说明
参数名 | 描述 |
---|---|
geometry_list | 要可视化的几何体列表 |
window_name | (str, optional, default='Open3D') ,可视化窗口的显示标题 |
width | (int, optional, default=1920) ,可视化窗口的宽度 |
height | (int, optional, default=1080) ,可视化窗口的高度 |
left | (int, optional, default=50) ,可视化窗口的左边距 |
top | (int, optional, default=50) ,可视化窗口的顶部边距 |
point_show_normal | (bool, optional, default=False) ,如果设置为true,则可视化点法线 |
mesh_show_wireframe | (bool, optional, default=False) ,如果设置为true,则可视化网格线框 |
mesh_show_back_face | (bool, optional, default=False) ,可视化网格三角形的背面 |
lookat | (numpy.ndarray[float64[3, 1]]) ,相机的注视向量 |
up | (numpy.ndarray[float64[3, 1]]) ,相机的上方向向量 |
front | (numpy.ndarray[float64[3, 1]]) ,相机的前矢量 |
zoom | (float) ,相机的缩放倍数 |
代码:
import open3d as o3d
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("bunny.pcd")
print(pcd)
# 法线估计
radius = 0.01 # 搜索半径
max_nn = 30 # 邻域内用于估算法线的最大点数
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius, max_nn)) # 执行法线估计
# 可视化
o3d.visualization.draw_geometries([pcd],
window_name = "可视化参数设置",
width = 600,
height = 450,
left = 30,
top = 30,
point_show_normal = True)
可视化结果:
5 k-d tree 与 Octree
5.1 k-d tree
o3d.geometry.KDTreeFlann(pcd)
:建立KDTree
search_knn_vector_3d(search_Pt, k)
:K 近邻搜索search_radius_vector_3d(search_Pt, radius)
:半径 R 近邻搜索search_hybrid_vector_3d(search_pt, radius, max_nn)
:混合邻域搜索,返回半径radius
内不超过max_nn
个近邻点
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("bunny.pcd")
print(pcd)
# 将点云设置为灰色
pcd.paint_uniform_color([0.5, 0.5, 0.5])
# 建立KDTree
pcd_tree = o3d.geometry.KDTreeFlann(pcd)
# 将第1500个点设置为紫色
pcd.colors[1500] = [0.5, 0, 0.5]
# 使用K近邻,将第1500个点最近的5000个点设置为蓝色
print("使用K近邻,将第1500个点最近的5000个点设置为蓝色")
k = 5000 # 设置K的大小
[num_k, idx_k, _] = pcd_tree.search_knn_vector_3d(pcd.points[1500], k) # 返回邻域点的个数和索引
np.asarray(pcd.colors)[idx_k[1:], :] = [0, 0, 1] # 跳过最近邻点(查询点本身)进行赋色
print("k邻域内的点数为:", num_k)
# 使用半径R近邻,将第1500个点半径(0.02)范围内的点设置为红色
print("使用半径R近邻,将第1500个点半径(0.02)范围内的点设置为红色")
radius = 0.02 # 设置半径大小
[num_radius, idx_radius, _] = pcd_tree.search_radius_vector_3d(pcd.points[1500], radius) # 返回邻域点的个数和索引
np.asarray(pcd.colors)[idx_radius[1:], :] = [1, 0, 0] # 跳过最近邻点(查询点本身)进行赋色
print("半径r邻域内的点数为:", num_radius)
# 使用混合邻域,将半径R邻域内不超过max_num个点设置为绿色
print("使用混合邻域,将第1500个点半径R邻域内不超过max_num个点设置为绿色")
max_nn = 200 # 半径R邻域内最大点数
[num_hybrid, idx_hybrid, _] = pcd_tree.search_hybrid_vector_3d(pcd.points[1500], radius, max_nn)
np.asarray(pcd.colors)[idx_hybrid[1:], :] = [0, 1, 0] # 跳过最近邻点(查询点本身)进行赋色
print("混合邻域内的点数为:", num_hybrid)
print("->正在可视化点云...")
o3d.visualization.draw_geometries([pcd])
输出结果:
->正在加载点云...
PointCloud with 35947 points.
使用K近邻,将第1500个点最近的5000个点设置为蓝色
k邻域内的点数为: 5000
使用半径R近邻,将第1500个点半径(0.02)范围内的点设置为红色
半径r邻域内的点数为: 751
使用混合邻域,将第1500个点半径R邻域内不超过max_num个点设置为绿色
混合邻域内的点数为: 200
->正在可视化点云...
结果展示:
5.2 Octree
八叉树(Octree)是一种树数据结构,其中每个内部节点有八个子节点。八叉树通常用于三维点云的空间划分。八叉树的非空叶节点包含属于同一空间细分的一个或多个点。八叉树是三维空间的有用描述,可用于快速查找附近的点。Open3D具有几何体类型的八叉树,可用于创建、搜索和遍历具有用户指定的最大树深度的八叉树max_depth
。
5.2.1 从点云中构建Octree
可以使用convert_from_point_cloud
方法从点云构造八叉树。通过遵循从根节点到“最大深度”(depth max_depth)处的相应叶节点的路径,将每个点插入到树中。随着树深度的增加,内部(最终是叶子)节点表示三维空间的较小分区。
如果点云具有颜色,则对应的叶节点将采用上次插入点的颜色。size_expand
参数会增加根八叉树节点的大小,使其略大于原始点云边界以容纳所有点。
代码:
import open3d as o3d
import numpy as np
# --------------------------- 加载点云 ---------------------------
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("tree.pcd")
print("原始点云:", pcd)
# ==============================================================
# ------------------------- 构建Octree --------------------------
print('octree 分割')
octree = o3d.geometry.Octree(max_depth=4)
octree.convert_from_point_cloud(pcd, size_expand=0.01)
print("->正在可视化Octree...")
o3d.visualization.draw_geometries([octree])
# ==============================================================
输出结果:
->正在加载点云...
原始点云: PointCloud with 5746 points.
octree 分割
->正在可视化Octree...
结果展示:
5.2.2 从体素栅格中构建Octree
还可以使用create_from_voxel_grid
的方法,从Open3D体素网格VoxelGrid
几何体构建八叉树。每个体素被视为三维空间中的一个点,坐标对应于体素的原点。每个叶节点采用其相应体素的颜色。
代码:
import open3d as o3d
import numpy as np
# --------------------------- 加载点云 ---------------------------
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("tree.pcd")
print("原始点云:", pcd)
# ==============================================================
# ------------------------- 构建Octree --------------------------
print('体素化')
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.2)
print("体素:", voxel_grid)
print("正在可视化体素...")
o3d.visualization.draw_geometries([voxel_grid])
print('Octree 分割')
octree = o3d.geometry.Octree(max_depth=4)
octree.create_from_voxel_grid(voxel_grid)
print("Octree:", octree)
print("正在可视化Octree...")
o3d.visualization.draw_geometries([octree])
# ==============================================================
输出结果:
->正在加载点云...
原始点云: PointCloud with 5746 points.
体素化
体素: VoxelGrid with 861 voxels.
正在可视化体素...
Octree 分割
Octree: Octree with origin: [64.647, -54.2659, -20.2166], size: 7, max_depth: 4
正在可视化Octree...
可视化结果:
6 点云滤波
6.1 体素下采样
体素下采样使用规则体素栅格从输入点云创建均匀下采样点云。该算法分两步操作:
- 将点云进行进行体素划分
- 对所有非空体素,取体素内点云的质心作为该体素的点的位置。
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("test.pcd")
print(pcd)
print("->正在可视化原始点云")
o3d.visualization.draw_geometries([pcd])
print("->正在体素下采样...")
voxel_size = 0.5
downpcd = pcd.voxel_down_sample(voxel_size)
print(downpcd)
print("->正在可视化下采样点云")
o3d.visualization.draw_geometries([downpcd])
输出结果:
->正在加载点云...
PointCloud with 356478 points.
->正在可视化原始点云
->正在体素下采样...
PointCloud with 11141 points.
->正在可视化下采样点云
可视化展示:
6.2 统计滤波
statistical_outlier_removal
会移除距离相邻点更远的点。它需要两个输入参数:
num_neighbors
,指定为了计算给定点的平均距离,需要考虑多少个邻居。即K邻域的点数std_ratio
,允许根据点云平均距离的标准偏差设置阈值水平。该数值越低,滤除的点数就越多
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("desk.pcd")
print("原始点云:", pcd)
# ------------------------- 统计滤波 --------------------------
print("->正在进行统计滤波...")
num_neighbors = 20 # K邻域点的个数
std_ratio = 2.0 # 标准差乘数
# 执行统计滤波,返回滤波后的点云sor_pcd和对应的索引ind
sor_pcd, ind = pcd.remove_statistical_outlier(num_neighbors, std_ratio)
sor_pcd.paint_uniform_color([0, 0, 1])
print("统计滤波后的点云:", sor_pcd)
sor_pcd.paint_uniform_color([0, 0, 1])
# 提取噪声点云
sor_noise_pcd = pcd.select_by_index(ind,invert = True)
print("噪声点云:", sor_noise_pcd)
sor_noise_pcd.paint_uniform_color([1, 0, 0])
# ===========================================================
# 可视化统计滤波后的点云和噪声点云
o3d.visualization.draw_geometries([sor_pcd, sor_noise_pcd])
输出结果:
->正在加载点云...
原始点云: PointCloud with 41049 points.
->正在进行统计滤波...
统计滤波后的点云: PointCloud with 40061 points.
噪声点云: PointCloud with 988 points.
可视化结果:
6.3 半径滤波
radius_outlier_removal
移除给定球体中几乎没有邻居的点。需要两个参数:
num_points
,邻域球内的最少点数,低于该值的点为噪声点
radius
,邻域半径的大小
代码:
import open3d as o3d
import numpy as np
print("->正在加载点云... ")
pcd = o3d.io.read_point_cloud("desk.pcd")
print("原始点云:", pcd)
# ------------------------- 半径滤波 --------------------------
print("->正在进行半径滤波...")
num_points = 20 # 邻域球内的最少点数,低于该值的点为噪声点
radius = 0.05 # 邻域半径大小
# 执行半径滤波,返回滤波后的点云sor_pcd和对应的索引ind
sor_pcd, ind = pcd.remove_radius_outlier(num_points, radius)
sor_pcd.paint_uniform_color([0, 0, 1])
print("半径滤波后的点云:", sor_pcd)
sor_pcd.paint_uniform_color([0, 0, 1])
# 提取噪声点云
sor_noise_pcd = pcd.select_by_index(ind,invert = True)
print("噪声点云:", sor_noise_pcd)
sor_noise_pcd.paint_uniform_color([1, 0, 0])
# ===========================================================
# 可视化半径滤波后的点云和噪声点云
o3d.visualization.draw_geometries([sor_pcd, sor_noise_pcd])
输出结果:
->正在加载点云...
原始点云: PointCloud with 41049 points.
->正在进行半径滤波...
半径滤波后的点云: PointCloud with 40146 points.
噪声点云: PointCloud with 903 points.
可视化结果:
❤️爆肝万字整理的综合架构web服务之nginx详解❤️,附建议收藏
❤️爆肝万字!一文最全总结之Spring从入门到入土❤️(建议收藏)
Python OpenCV实战画图——这次一定能行!爆肝万字,建议点赞收藏~❤️❤️❤️