2、点云数据基础:从文件格式到读写实战
点云数据,说白了就是一堆三维点的集合。但怎么存、怎么读、怎么用,这里头门道不少。我刚开始接触点云时,就被各种文件格式搞得晕头转向——PCD、PLY、LAS、BIN,每个都有自己的脾气。今天咱们就把这些基础打扎实。
2.1 点云数据格式:四种主流格式怎么选?
先说说最常见的四种格式。我个人习惯用PCD做实验,用LAS处理正式项目。为什么?往下看。
2.1.1 PCD格式(Point Cloud Data)
PCL库的亲儿子。结构清晰,读写快。我在项目中遇到过一个问题:PCD文件头部信息不全,导致后续处理报错。嗯,这里要注意——PCD的头部必须包含WIDTH、HEIGHT、POINTS等字段,缺一不可。
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z rgb
SIZE 4 4 4 4
TYPE F F F U
COUNT 1 1 1 1
WIDTH 213
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 213
DATA ascii
2.1.2 PLY格式(Polygon File Format)
PLY的好处是能同时存点云和网格。我建议做三维重建时优先考虑它。但要注意,PLY的二进制版本和ASCII版本读写方式不同,别搞混了。
2.1.3 LAS格式(LASer)
测绘行业的标配。为什么?因为它支持分类、强度、回波数等属性。我记得第一次处理机载LiDAR数据,甲方指定要LAS格式,我临时补了一下午的LAS规范。
2.1.4 BIN格式(Binary)
说白了就是裸数据。KITTI数据集用的就是BIN格式。优点是读写极快,缺点是没有头部信息,你得自己记住数据结构。我一般只在性能敏感的场景用BIN。
| 格式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| PCD | PCL原生支持,结构清晰 | 文件较大 | 算法开发、实验 |
| PLY | 支持网格+点云 | 解析稍复杂 | 三维重建 |
| LAS | 属性丰富,行业标准 | 库依赖较多 | 测绘、自动驾驶 |
| BIN | 读写极快 | 无头部信息 | 高性能场景 |
2.2 点云数据结构:XYZ、XYZI、XYZRGB
你想想看,一个点除了坐标,还能带什么信息?这取决于你的传感器和应用。
2.2.1 XYZ——最基础的结构
只有三维坐标。够用吗?说实话,大多数场景不够。但做几何处理时,XYZ就够了。
2.2.2 XYZI——带强度
强度值反映了物体对激光的反射能力。我在做路面检测时,就靠强度值区分车道线和沥青路面。强度值一般归一化到0-255或0-65535。
- 高反射:交通标志、车牌
- 中反射:路面、建筑物
- 低反射:树木、沥青
2.2.3 XYZRGB——带颜色
相机和LiDAR融合的结果。颜色信息能帮你做语义分割。我建议用RGB而不是BGR,虽然OpenCV默认是BGR,但PCL和Open3D都用RGB。
2.3 点云属性:坐标、强度、时间戳、环号
一个完整的点,其实包含很多属性。我整理了一下常用的:
- 坐标(x, y, z):核心属性,单位通常是米
- 强度(intensity):反射强度,用于分类
- 时间戳(timestamp):每个点的时间,用于去畸变
- 环号(ring):激光雷达的线束编号,0-15或0-31
2.4 点云文件读写:PCL、Open3D、Python
三种工具各有千秋。我一般这样选:
- PCL(C++):性能最好,适合生产环境
- Open3D(Python):开发效率高,适合快速验证
- Python原生:轻量级,适合简单任务
2.4.1 PCL读写示例
// 读取PCD文件
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("input.pcd", *cloud);
// 写入PCD文件
pcl::io::savePCDFileASCII("output.pcd", *cloud);
2.4.2 Open3D读写示例
import open3d as o3d
# 读取点云
pcd = o3d.io.read_point_cloud("input.pcd")
print(f"点云点数: {len(pcd.points)}")
# 写入点云
o3d.io.write_point_cloud("output.ply", pcd)
laspy。我建议用pip install open3d[las]一次性装好。
2.4.3 Python原生读写BIN
import numpy as np
# 读取KITTI格式的BIN文件
points = np.fromfile("000000.bin", dtype=np.float32).reshape(-1, 4)
xyz = points[:, :3] # 前三列是坐标
intensity = points[:, 3] # 第四列是强度
# 写入BIN文件
points.tofile("output.bin")
嗯,这里要注意:BIN文件没有头部,你必须知道每行有几个字段。KITTI是4个字段(x, y, z, intensity),其他数据集可能不同。
2.5 实战建议
最后分享几个我踩过的坑:
- 坐标系问题:不同传感器的坐标系定义不同。LiDAR一般是前x左y上z,但相机是右x下y前z。转换时别搞反了。
- 数据类型:PCL默认用float32,但有些数据集用float64。精度不够会导致坐标偏移。
- 文件大小:一个64线激光雷达一小时的数据,可能超过100GB。我建议用BIN格式存储原始数据,用PCD做中间处理。
好了,点云数据基础就讲到这里。下一章咱们聊聊点云滤波——怎么去掉那些讨厌的噪声点。