第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 知识体系总览

为了让你对本章内容有个整体印象,我画了一张结构图:

点云数据基础 数据结构 PCD:PCL原生格式 PLY:网格+点云 LAS:测绘标准 存储格式对比 头部结构差异 字段支持范围 压缩与体积 读写与优化 二进制 vs ASCII 内存映射mmap 分块与多线程 选对格式 + 优化读写 = 滤波性能提升50%以上

这张图把本章的核心内容串起来了。左边是三种数据结构,中间是它们的对比维度,右边是读写优化的关键手段。你学完这一章,应该能根据项目需求,快速选出合适的格式和读写策略。

好了,点云数据基础就聊到这儿。下一章我们开始讲滤波算法的核心原理——到时候我会结合具体代码,带你看看不同滤波器的性能差异到底有多大。


可可精品资料库