1. 点云数据基础:什么是点云?点云数据的格式与读取方法
1.1 什么是点云?
点云,说白了就是一堆三维坐标点的集合。每个点都带着 (x, y, z) 三个坐标值,告诉你「我在空间中的这个位置」。我刚开始接触这个领域时,觉得它就像夜空中的星星——无数个光点拼出了整个场景的轮廓。
但点云不只是坐标。很多格式还会给每个点附加属性,比如:
- 强度值(Intensity)——激光雷达返回的信号强弱,能帮你区分不同材质
- 颜色(RGB)——如果是深度相机或融合了图像数据,每个点还能带上颜色
- 法向量(Normal)——描述点所在表面的朝向,做平面拟合时特别有用
- 时间戳——在SLAM里,每个点是什么时候采集的,这很关键
你想想看,一个点云文件里可能存着几百万个这样的点。怎么高效地存、怎么快速地读,就成了第一个要解决的问题。
核心概念:点云 = 三维坐标 + 可选属性。没有属性也能用,但有了属性,很多算法才能跑起来。
1.2 三种主流格式:PCD、PLY、LAS
我在项目中遇到过不少格式混用的情况。不同传感器、不同软件,输出的格式五花八门。但最常用的,就是下面这三种。
| 格式 | 扩展名 | 特点 | 常见场景 |
|---|---|---|---|
| PCD | .pcd | Point Cloud Library 原生格式,支持 ASCII 和二进制 | PCL 处理、学术研究 |
| PLY | .ply | 斯坦福大学开发,支持顶点和面片,可带颜色 | 三维重建、Mesh 生成 |
| LAS | .las / .laz | ASPRS 标准,专为机载激光雷达设计,含分类信息 | 测绘、地理信息系统 |
PCD 格式
PCD 是 PCL 的亲儿子。我个人习惯用 PCD 做中间格式——因为它读写快,而且 PCL 里几乎所有函数都直接支持。它的文件头长这样:
# .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
0.123 0.456 0.789 0.5
...
嗯,这里要注意:WIDTH 和 HEIGHT 如果都大于 1,表示这是「有序点云」,像一张深度图一样有行列结构。如果 HEIGHT 是 1,那就是无序点云,所有点平铺在一起。
PLY 格式
PLY 的厉害之处在于它既能存点,又能存三角面片。做三维重建时,Mesh 转点云、点云转 Mesh,PLY 是首选。它的头部分为 element vertex 和 element face 两部分:
ply
format ascii 1.0
element vertex 1000
property float x
property float y
property float z
property uchar red
property uchar green
property uchar blue
element face 500
property list uchar int vertex_indices
end_header
0.1 0.2 0.3 255 0 0
...
小技巧:如果你只需要点坐标,用 PCD 更省事。如果你需要保留颜色和三角面,PLY 更合适。
LAS 格式
LAS 是测绘界的标准。它不像前两种那样「通用」,但专业度很高。每个点除了坐标,还带有分类标签(比如 2 表示地面、6 表示建筑物)。我做过一个项目,甲方给的数据全是 LAS 格式,里面还带了回波次数和扫描角度——这些信息在做地面滤波时非常有用。
LAS 是二进制格式,没法用文本编辑器直接看。但好在有现成的库可以解析。
1.3 读取方法:Python 与 C++ 实战
光说不练假把式。下面我给出三种格式的读取代码,Python 和 C++ 各一套。
Python 读取示例
Python 这边,我推荐用 open3d 和 laspy。前者通用,后者专攻 LAS。
import open3d as o3d
import numpy as np
# 读取 PCD
pcd = o3d.io.read_point_cloud("example.pcd")
points = np.asarray(pcd.points)
print(f"PCD 点云点数: {points.shape[0]}")
# 读取 PLY(同样用 open3d)
ply = o3d.io.read_point_cloud("example.ply")
colors = np.asarray(ply.colors)
print(f"PLY 点云颜色: {colors.shape}")
# 读取 LAS(需要 laspy)
import laspy
las = laspy.read("example.las")
xyz = np.vstack((las.x, las.y, las.z)).T
classification = las.classification
print(f"LAS 点云点数: {xyz.shape[0]}, 地面点数量: {np.sum(classification == 2)}")
避坑指南:我曾经用 open3d 读一个 2GB 的 LAS 文件,直接内存爆了。后来发现 open3d 的 LAS 读取会把所有点一次性加载到内存。对于大文件,建议用 laspy 的 chunk_iterator 分块读取。
C++ 读取示例(PCL)
C++ 这边,PCL 是绕不开的。它的 io 模块直接支持 PCD 和 PLY,LAS 则需要第三方库或者自己解析。
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
// 读取 PCD
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
if (pcl::io::loadPCDFile<pcl::PointXYZRGB>("example.pcd", *cloud) == -1) {
PCL_ERROR("读取 PCD 失败\n");
return -1;
}
std::cout << "PCD 点数: " << cloud->points.size() << std::endl;
// 读取 PLY
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr ply_cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
if (pcl::io::loadPLYFile("example.ply", *ply_cloud) == -1) {
PCL_ERROR("读取 PLY 失败\n");
return -1;
}
// LAS 读取(使用 liblas 或自己解析二进制)
// 这里给出一个简化的二进制读取思路
std::ifstream file("example.las", std::ios::binary);
// ... 解析 LAS 头,然后按格式读取点记录 ...
个人经验:C++ 里读 LAS 我踩过坑。LAS 1.2 和 1.4 的格式略有不同,特别是点记录格式(Point Format)的字段长度。建议直接用 liblas 或 PDAL,别自己手写解析——除非你想重温二进制文件操作的酸爽。
1.4 知识体系结构图
下面这张图,我把本章的核心知识点串了起来。你可以把它当作一张「点云数据入门地图」:
1.5 本章小结
点云数据入门,说白了就三件事:
- 理解点云是什么——不只是坐标,还有各种属性
- 分清三种格式——PCD 适合 PCL 生态,PLY 适合重建,LAS 适合测绘
- 掌握读取方法——Python 用 open3d + laspy,C++ 用 PCL + PDAL
我记得刚入行时,花了一整天去解析一个 LAS 文件,结果发现格式版本不对。后来学乖了,先用 lasinfo 工具看一眼文件头,再决定怎么读。嗯,这种经验,踩过一次坑就记住了。
一句话总结:点云数据是三维世界的「像素」,格式是它的「容器」,读取方法是你的「钥匙」。容器选对了,钥匙用好了,后面的滤波、分割、SLAM 才能顺利展开。