3、点云基础数据结构:点云的数学表示(Nx3矩阵),Open3D中的PointCloud对象,点云的基本属性(坐标、颜色、法向量)
点云这东西,说白了就是一堆三维空间里的点。你想想看,我们拿激光雷达或者深度相机扫一圈,得到的无非就是成千上万个点的坐标。那这些点在计算机里怎么存?怎么用?这就是今天要聊的核心。
我个人习惯把点云理解成「三维世界的像素」。图像是二维网格上的颜色值,点云就是三维空间里的坐标集合。嗯,这个类比虽然不完全准确,但初学者这么理解,上手会快很多。
3.1 点云的数学表示:Nx3矩阵
先从最基础的数学表示说起。一个点云,在数学上就是一个 N行3列 的矩阵。N代表点的个数,3代表X、Y、Z三个坐标轴。
举个例子,假设我们有3个点:
- 点A: (1.0, 2.0, 3.0)
- 点B: (4.0, 5.0, 6.0)
- 点C: (7.0, 8.0, 9.0)
那这个点云在数学上就是:
[[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0]]
这个矩阵的形状就是 (3, 3)。在Python里,我们通常用NumPy的ndarray来表示它。
核心要点:点云 = Nx3的浮点数矩阵。每一行是一个点,每一列是一个坐标轴。
我在项目中遇到过一个问题:有人把点云存成了Nx4的矩阵,多了一列强度值。这本身没问题,但如果你直接把它喂给Open3D的标准函数,就会报错。所以,搞清楚你的数据格式,是第一步。
3.2 Open3D中的PointCloud对象
Open3D把点云封装成了一个叫 PointCloud 的类。这个类里,最核心的成员变量就是 points。
怎么创建一个点云?看代码:
import open3d as o3d
import numpy as np
# 创建一个空的点云对象
pcd = o3d.geometry.PointCloud()
# 生成一些随机点(1000个点)
points = np.random.rand(1000, 3)
# 赋值给点云对象
pcd.points = o3d.utility.Vector3dVector(points)
# 看一眼
print(pcd)
print(f"点的数量: {len(pcd.points)}")
这里有个细节要注意:pcd.points 不能直接赋一个NumPy数组,必须用 o3d.utility.Vector3dVector() 包装一下。为什么?因为Open3D内部有自己的数据结构,它需要这种转换。嗯,刚开始用的时候我也觉得多此一举,但用习惯了就发现,这种设计其实是为了性能优化。
小技巧:如果你从文件读取点云,直接用 o3d.io.read_point_cloud("文件路径.pcd") 就行,它会自动帮你创建好PointCloud对象。
3.3 点云的基本属性:坐标、颜色、法向量
一个完整的点云,通常不止有坐标。还有颜色和法向量。这三个属性,就是点云的「三件套」。
3.3.1 坐标(points)
坐标是点云的基础,没有坐标就不叫点云了。坐标的数据类型是 float64,范围理论上没有限制,但实际应用中,我们经常会对坐标做归一化处理。
3.3.2 颜色(colors)
颜色信息通常来自RGB相机。Open3D中,颜色的取值范围是 [0, 1] 的浮点数,而不是常见的[0, 255]整数。这一点很容易踩坑。
我曾经在项目里从图片读取颜色值,直接用了0-255的整数赋值给点云,结果渲染出来一片惨白。排查了半天才发现是范围问题。
# 正确的颜色赋值方式
colors = np.random.rand(1000, 3) # 范围[0, 1]
pcd.colors = o3d.utility.Vector3dVector(colors)
# 错误的做法(如果你有0-255的数据,记得除以255)
# colors_255 = np.random.randint(0, 255, (1000, 3))
# pcd.colors = o3d.utility.Vector3dVector(colors_255 / 255.0)
3.3.3 法向量(normals)
法向量是点云里比较「高级」的属性。它表示每个点所在局部表面的朝向。法向量有什么用?
- 点云渲染:有法向量的点云看起来更立体
- 点云配准:很多配准算法需要法向量信息
- 表面重建:法向量是重建三角网格的关键输入
法向量不是天然存在的,需要计算。Open3D提供了现成的函数:
# 估计法向量
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=0.1, max_nn=30
)
)
# 查看法向量
print(pcd.normals[0]) # 第一个点的法向量
避坑指南:我曾经在计算法向量时,把搜索半径设得太小,结果法向量方向乱七八糟。后来我总结了一个经验:搜索半径一般取点云平均点间距的3-5倍。你可以先用 pcd.compute_nearest_neighbor_distance() 算一下平均距离。
3.4 知识体系结构图
下面这张图,帮你理清点云数据结构的核心脉络:
3.5 实战中的一些经验
最后,分享几个我在实际项目中积累的经验:
- 数据检查很重要:拿到点云后,先打印
pcd.points[0:5]看看数据范围。有时候文件读进来坐标是毫米单位,但你的算法期望的是米单位,不检查就会出问题。 - 颜色和法向量是可选的:不是所有点云都有颜色和法向量。如果你只需要几何信息,只存坐标就够了。但如果你要做可视化,颜色和法向量能帮你省很多事。
- 法向量方向一致性:Open3D计算的法向量方向可能不一致(有的朝里有的朝外)。如果你要做表面重建,记得用
orient_normals_consistent_tangent_plane()统一方向。
一句话总结:点云就是Nx3的矩阵,Open3D把它包装成了PointCloud对象,然后你可以往里面塞坐标、颜色、法向量。就这么简单。