第1章:点云数据基础——数据结构与存储格式
大家好,我是老张。做点云处理这些年,我踩过最多的坑,其实不是算法本身,而是数据读写那点事儿。你想想看,一个几百兆的LAS文件读进来要半分钟,滤波算法跑起来反而只要几秒——这合理吗?
所以咱们开篇先聊聊点云数据的基础。说白了,就是搞清楚三件事:点云长什么样、存在哪里、怎么读写最快。
1.1 点云数据结构:三种主流格式
点云数据本质上是一堆三维点的集合。每个点至少包含X、Y、Z坐标。但实际项目中,我们往往还需要颜色、强度、分类等信息。这就引出了不同的数据结构。
PCD格式(Point Cloud Data)
PCD是PCL库的亲儿子。我个人最喜欢它的地方是——头部信息极其清晰。你看一眼文件头,就知道里面存了多少点、每个点有哪些字段、数据是二进制还是ASCII。
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 F
COUNT 1 1 1 1
WIDTH 640
HEIGHT 480
VIEWPOINT 0 0 0 1 0 0 0
POINTS 307200
DATA ascii
注意那个WIDTH和HEIGHT——如果HEIGHT为1,说明是无序点云;否则就是有序点云(比如深度相机扫出来的)。这个区别在滤波时很重要,我后面会讲。
PLY格式(Polygon File Format)
PLY格式最早是为斯坦福的兔子模型设计的。它的特点是既能存点云,也能存三角网格。如果你做三维重建,PLY几乎是标配。
PLY也有头部,但写法跟PCD不太一样。它用element vertex和property来定义点的结构。嗯,这里要注意:PLY的二进制格式有两种变体——大端和小端。跨平台读写时容易出问题,我曾在Windows和Linux之间传文件吃过这个亏。
LAS格式(LASer)
LAS是测绘和遥感领域的标准格式。它不像PCD和PLY那样是通用格式,而是专门为机载激光雷达设计的。LAS格式里除了坐标,还强制包含分类(地面、建筑、植被等)、回波次数、强度等信息。
我的经验:如果你做自动驾驶,PCD用得最多;做三维重建,PLY更合适;做测绘项目,LAS是绕不开的。选格式之前,先想清楚下游任务要什么。
1.2 点云存储格式对比
三种格式各有优劣。我整理了一张对比表,方便你快速决策:
| 特性 | PCD | PLY | LAS |
|---|---|---|---|
| 头部信息 | 简洁清晰 | 灵活但复杂 | 标准化,字段固定 |
| 支持字段 | 任意自定义 | 任意自定义 | 预定义(坐标、分类、强度等) |
| 二进制压缩 | 支持 | 支持 | 支持(LAS 1.4有压缩) |
| 有序点云 | 原生支持 | 不支持 | 不支持 |
| 网格支持 | 不支持 | 原生支持 | 不支持 |
| 行业标准 | 机器人/自动驾驶 | 计算机图形学 | 测绘/地理信息 |
为什么会这样?说白了,每种格式都是为特定场景设计的。PCD是PCL团队为了算法研究做的,所以读写效率高、字段灵活。PLY来自图形学社区,所以能存网格。LAS是测绘行业标准,所以字段固定但信息完整。
1.3 点云数据读取与写入
读写点云,看起来简单,但性能差距可以很大。我见过有人用ASCII格式读1000万个点,等了快两分钟——换成二进制,不到两秒。
PCL读写示例
如果你用PCL库,读写PCD是最方便的:
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
// 读取PCD文件
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>("input.pcd", *cloud) == -1) {
PCL_ERROR("读取失败!\n");
return -1;
}
// 处理点云...
// 写入PCD文件(二进制格式)
pcl::io::savePCDFileBinary("output.pcd", *cloud);
避坑指南:我曾经在项目中用savePCDFileASCII保存了一个500MB的点云,结果文件变成了2.5GB。后来排查发现,ASCII格式会把浮点数转成字符串,体积膨胀5倍以上。所以生产环境一定要用二进制格式。
LAS格式读写
LAS格式推荐用LAStools或者liblas。我个人习惯用PDAL库,它支持多种格式的转换:
# PDAL命令行示例:LAS转PCD
pdal translate input.las output.pcd
# 或者用Python
import pdal
import json
pipeline = {
"pipeline": [
"input.las",
{
"type": "writers.pcd",
"filename": "output.pcd",
"compression": "binary"
}
]
}
r = pdal.Pipeline(json.dumps(pipeline))
r.execute()
性能优化要点
读写性能的瓶颈往往不在CPU,而在I/O。这里分享几个我常用的优化技巧:
- 用二进制别用ASCII——体积小、读写快,这是最直接的优化
- 内存映射文件——对于超大点云(比如超过10GB),用mmap可以避免一次性加载到内存
- 分块读取——如果只需要部分点云,用LAS的
spatial query或者PCD的seek跳过不必要的数据 - 多线程写入——写入大文件时,可以分块压缩再合并,能显著提升吞吐量
我的经验:有一次处理一个20GB的LAS文件,单线程读取花了3分钟。改成内存映射+分块读取后,只用了40秒。你想想看,这差距有多大。
1.4 知识体系总览
为了让你对本章内容有个整体印象,我画了一张结构图:
这张图把本章的核心内容串起来了。左边是三种数据结构,中间是它们的对比维度,右边是读写优化的关键手段。你学完这一章,应该能根据项目需求,快速选出合适的格式和读写策略。
好了,点云数据基础就聊到这儿。下一章我们开始讲滤波算法的核心原理——到时候我会结合具体代码,带你看看不同滤波器的性能差异到底有多大。