2、点云数据基础:从文件格式到读写实战

点云数据,说白了就是一堆三维点的集合。但怎么存、怎么读、怎么用,这里头门道不少。我刚开始接触点云时,就被各种文件格式搞得晕头转向——PCD、PLY、LAS、BIN,每个都有自己的脾气。今天咱们就把这些基础打扎实。

2.1 点云数据格式:四种主流格式怎么选?

先说说最常见的四种格式。我个人习惯用PCD做实验,用LAS处理正式项目。为什么?往下看。

2.1.1 PCD格式(Point Cloud Data)

PCL库的亲儿子。结构清晰,读写快。我在项目中遇到过一个问题:PCD文件头部信息不全,导致后续处理报错。嗯,这里要注意——PCD的头部必须包含WIDTHHEIGHTPOINTS等字段,缺一不可。

PCD头部示例:
# .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规范。

避坑指南:LAS 1.2和LAS 1.4的点格式不同。我曾经因为版本不匹配,解析出来的坐标全是错的。建议统一用LAS 1.4。

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)
个人经验:Open3D读取LAS格式需要安装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 实战建议

最后分享几个我踩过的坑:

  1. 坐标系问题:不同传感器的坐标系定义不同。LiDAR一般是前x左y上z,但相机是右x下y前z。转换时别搞反了。
  2. 数据类型:PCL默认用float32,但有些数据集用float64。精度不够会导致坐标偏移。
  3. 文件大小:一个64线激光雷达一小时的数据,可能超过100GB。我建议用BIN格式存储原始数据,用PCD做中间处理。
一句话总结:格式选对,事半功倍。结构搞清,少走弯路。读写熟练,效率翻倍。

好了,点云数据基础就讲到这里。下一章咱们聊聊点云滤波——怎么去掉那些讨厌的噪声点。