第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)。

小技巧: 二进制PLY文件体积小、读写快。我建议生产环境用二进制,调试阶段用ASCII方便查看。

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生态里,我推荐用 open3dlaspy。下面是用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
注意: LAS文件中的坐标通常是投影坐标(比如UTM),不是经纬度。如果你需要地理坐标,记得做投影转换。我有个同事直接把LAS坐标当经纬度用,结果点云跑到非洲去了。

2.3.3 存储优化建议

点云数据量大,存储时要注意几点:

  • 二进制优先:二进制文件比ASCII小3-5倍,读写快10倍以上
  • 压缩存储:LAS格式可以用LAZ压缩,体积能缩小到原来的1/5
  • 分块存储:超过1亿点的数据,建议按空间分块存储,避免一次性加载
  • 精度控制:如果不需要毫米级精度,可以转成float32甚至定点数

2.4 本章知识体系

下面这张图,是我梳理的点云数据结构核心脉络:

点云数据结构 基本属性 XYZ坐标 强度 RGB颜色 数据格式 PCD PLY LAS TXT 存储与读取 PCL读取 Open3D laspy 存储优化 图:点云数据结构知识体系 核心:理解属性 → 选对格式 → 高效读写

说白了,点云数据结构就三件事:属性是什么、格式怎么选、读写怎么做。把这三点吃透了,后面处理点云就顺手多了。

我的建议: 刚开始学点云,别急着上复杂算法。先花点时间把数据格式和读写搞明白。我见过太多人——包括我自己早期——因为格式问题浪费大量时间。数据读对了,后面的事就顺了。

公众号:蓝海资料掘金营,微信deep3321