第2章:点云数据结构解析
点云数据,说白了就是一堆三维空间中的点。但这一堆点怎么组织、怎么存、怎么读,里面门道可不少。我刚开始接触点云时,以为就是三个坐标值而已,后来才发现——嗯,事情没那么简单。
2.1 点云的基本属性
每个点云点,至少包含一个属性:三维坐标。但实际项目中,我们往往需要更多信息。
2.1.1 XYZ坐标
这是点云最核心的数据。每个点用 (x, y, z) 表示在三维空间中的位置。单位通常是米,但有些传感器用毫米。我个人习惯在读取数据后,先检查一下坐标范围——如果数值特别大(比如几十万),多半是毫米单位,需要归一化。
2.1.2 强度(Intensity)
强度值反映的是激光雷达回波信号的强弱。我在做道路点云项目时,强度值帮了大忙——沥青路面和车道线的强度差异很明显,可以用来做地面分割。强度值一般范围是0-255或0-65535,具体看传感器型号。
2.1.3 RGB颜色
彩色点云通常是通过相机和激光雷达融合得到的。每个点附带R、G、B三个通道,各占8位(0-255)。注意:不是所有点云格式都支持颜色,比如纯激光雷达采集的PCD文件可能只有XYZ和强度。
2.2 常见的点云数据格式
你想想看,点云数据量动辄几百万甚至上亿个点,存储格式直接影响读写效率和文件大小。下面这四种格式,是我工作中最常打交道的。
| 格式 | 扩展名 | 特点 | 适用场景 |
|---|---|---|---|
| PCD | .pcd | PCL库原生格式,支持ASCII和二进制 | 算法开发、PCL生态 |
| PLY | .ply | 支持顶点和面片,可含颜色 | 三维重建、网格处理 |
| LAS | .las/.laz | 行业标准,支持分类、回波等 | 测绘、地理信息系统 |
| TXT | .txt/.xyz | 纯文本,每行一个点 | 数据交换、快速查看 |
2.2.1 PCD格式
PCL(Point Cloud Library)的官方格式。我特别喜欢它的头文件设计——清晰明了。一个典型的PCD文件长这样:
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z intensity
SIZE 4 4 4 4
TYPE F F F F
COUNT 1 1 1 1
WIDTH 1000
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 1000
DATA ascii
1.0 2.0 3.0 128
1.1 2.1 3.1 130
...
注意那个 HEIGHT 1——这表示是无序点云。如果HEIGHT大于1,那就是有序点云(比如深度图转过来的)。
2.2.2 PLY格式
PLY格式比PCD更灵活,支持顶点和面片。我在做三维重建时,经常用PLY保存Mesh结果。它有两种变体:ASCII和二进制(little endian / big endian)。
2.2.3 LAS格式
这是测绘领域的标准格式。除了XYZ和强度,还包含分类(地面、建筑、植被等)、回波次数、GPS时间等。LAS 1.4版本支持最多15个额外字段。说实话,做自动驾驶时我很少用LAS,但做测绘项目就绕不开它。
2.2.4 TXT格式
最简单的格式,每行一个点,空格或逗号分隔。比如:
x y z intensity r g b
1.0 2.0 3.0 128 255 0 0
1.1 2.1 3.1 130 0 255 0
这种格式兼容性最好,任何文本编辑器都能打开。但文件体积大,读取慢。我一般只用来做数据交换或快速验证。
2.3 点云数据的存储与读取
这部分我踩过不少坑。直接上代码,咱们边看边聊。
2.3.1 使用PCL读取PCD文件
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
if (pcl::io::loadPCDFile<pcl::PointXYZI>("scan.pcd", *cloud) == -1) {
PCL_ERROR("Couldn't read file!\n");
return -1;
}
std::cout << "Loaded " << cloud->points.size() << " points." << std::endl;
注意:PointXYZI 包含XYZ和强度,PointXYZRGB 包含XYZ和颜色。选错类型会导致数据丢失。我曾经犯过这个错——读了一整天数据,发现强度值全是0,就是因为用了 PointXYZ。
2.3.2 使用Python读取点云
Python生态里,我推荐用 open3d 或 laspy。下面是用Open3D读取PLY的例子:
import open3d as o3d
pcd = o3d.io.read_point_cloud("model.ply")
print(f"点云点数: {len(pcd.points)}")
print(f"点云颜色: {pcd.colors}")
# 转成numpy数组
points = np.asarray(pcd.points)
colors = np.asarray(pcd.colors)
读取LAS格式需要用 laspy:
import laspy
with laspy.open("lidar.las") as f:
las = f.read()
x = las.x
y = las.y
z = las.z
intensity = las.intensity
classification = las.classification
2.3.3 存储优化建议
点云数据量大,存储时要注意几点:
- 二进制优先:二进制文件比ASCII小3-5倍,读写快10倍以上
- 压缩存储:LAS格式可以用LAZ压缩,体积能缩小到原来的1/5
- 分块存储:超过1亿点的数据,建议按空间分块存储,避免一次性加载
- 精度控制:如果不需要毫米级精度,可以转成float32甚至定点数
2.4 本章知识体系
下面这张图,是我梳理的点云数据结构核心脉络:
说白了,点云数据结构就三件事:属性是什么、格式怎么选、读写怎么做。把这三点吃透了,后面处理点云就顺手多了。
公众号:蓝海资料掘金营,微信deep3321