4. 点云读写与格式转换:常见点云格式(PCD、PLY、LAS、TXT、BIN),Open3D读写操作,格式转换实战(BIN转PCD,PCD转PLY)

点云读写,说白了就是跟各种文件格式打交道。我刚开始做这行的时候,光是被格式问题就折腾了好几个通宵。你想想看,传感器采集回来的数据,可能是二进制,可能是文本,还有各种行业标准格式。不会读写,后面所有算法都是空中楼阁。

这一章,我们就来彻底搞定它。

4.1 常见点云格式一览

先看看市面上常见的几种格式。我按使用场景给你捋一捋。

格式 后缀 特点 典型场景
PCD .pcd Point Cloud Library 原生格式,支持头文件描述 PCL 生态、学术研究
PLY .ply 斯坦福大学开发,支持顶点和面片 三维重建、网格处理
LAS .las / .laz ASPRS 标准,含分类、强度等信息 测绘、LiDAR 点云
TXT .txt / .xyz 纯文本,每行 x y z,可带 r g b 调试、简单数据交换
BIN .bin 二进制裸数据,无头文件 KITTI 数据集、工业传感器

嗯,这里要注意:BIN 格式没有标准头文件。我在项目中遇到过,不同厂家给的 .bin 文件,数据排列方式可能完全不同。有的按 xyzxyz 排,有的按 xyzrgb 排,还有的带时间戳。所以读 BIN 之前,一定要先搞清楚数据协议。

4.2 Open3D 读写操作

Open3D 是我个人最常用的点云处理库。它把读写操作封装得特别干净,几行代码就能搞定。

4.2.1 读取点云

import open3d as o3d

# 读取 PCD 文件
pcd = o3d.io.read_point_cloud("example.pcd")
print(f"点云点数: {len(pcd.points)}")
print(f"点云类型: {pcd}")

# 读取 PLY 文件
pcd_ply = o3d.io.read_point_cloud("mesh.ply")
print(f"PLY 点云点数: {len(pcd_ply.points)}")

你看,就这么简单。Open3D 会自动根据文件后缀判断格式。不过,LAS 格式需要额外安装 laspy,Open3D 原生不支持直接读 LAS。

小技巧: 我习惯在读文件前先检查一下文件是否存在。不然读到一半报错,挺烦人的。

4.2.2 写入点云

# 写入 PCD 文件
o3d.io.write_point_cloud("output.pcd", pcd)

# 写入 PLY 文件(带颜色)
o3d.io.write_point_cloud("output_color.ply", pcd, write_ascii=False)

# 写入 ASCII 格式的 PCD
o3d.io.write_point_cloud("output_ascii.pcd", pcd, write_ascii=True)

这里有个坑:write_ascii 参数。默认是 False,写入二进制格式。二进制文件小,读写快。但如果你要手动查看数据内容,就得用 ASCII 模式。我曾经为了调试一个 bug,把二进制文件转成 ASCII,才发现是某个点的 z 坐标异常大。

4.2.3 读取 TXT 文件

Open3D 没有直接读 .txt 的函数。我一般用 NumPy 先读,再转成点云。

import numpy as np

# 假设 TXT 文件每行是 x y z
data = np.loadtxt("points.txt")
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(data)

# 如果带颜色,每行是 x y z r g b
data_rgb = np.loadtxt("points_rgb.txt")
pcd.points = o3d.utility.Vector3dVector(data_rgb[:, :3])
pcd.colors = o3d.utility.Vector3dVector(data_rgb[:, 3:6] / 255.0)
注意: 颜色值范围是 0-1。如果 TXT 里存的是 0-255,记得除以 255。我刚开始就忘了这茬,点云显示出来一片惨白。

4.3 格式转换实战

格式转换是日常工作中最频繁的操作。下面我挑两个最典型的场景,手把手带你走一遍。

4.3.1 BIN 转 PCD

KITTI 数据集用的就是 .bin 格式。每个点占 4 个 float,分别是 x, y, z, reflectance。注意,这里没有颜色。

import numpy as np
import open3d as o3d

def bin_to_pcd(bin_path, pcd_path):
    # 读取二进制文件
    raw_data = np.fromfile(bin_path, dtype=np.float32)
    
    # 每 4 个 float 为一个点 (x, y, z, reflectance)
    raw_data = raw_data.reshape(-1, 4)
    
    # 提取 xyz
    points = raw_data[:, :3]
    
    # 创建点云对象
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(points)
    
    # 保存为 PCD
    o3d.io.write_point_cloud(pcd_path, pcd)
    print(f"转换完成: {bin_path} -> {pcd_path}")

# 使用示例
bin_to_pcd("000000.bin", "000000.pcd")

为什么用 np.fromfile?因为它是按二进制直接读,效率最高。我测试过,读一个 100MB 的 .bin 文件,不到 0.1 秒。

避坑指南: 我曾经遇到过一个 .bin 文件,里面混了 NaN 值。读出来之后,点云显示有个大洞。后来我加了一步过滤:points = points[~np.isnan(points).any(axis=1)]

4.3.2 PCD 转 PLY

这个就简单了。Open3D 直接支持。

import open3d as o3d

def pcd_to_ply(pcd_path, ply_path):
    # 读取 PCD
    pcd = o3d.io.read_point_cloud(pcd_path)
    
    # 保存为 PLY(二进制格式,文件更小)
    o3d.io.write_point_cloud(ply_path, pcd, write_ascii=False)
    print(f"转换完成: {pcd_path} -> {ply_path}")

# 使用示例
pcd_to_ply("scan.pcd", "scan.ply")

如果你需要保留颜色,确保 PCD 文件里已经包含了 RGB 信息。Open3D 在转换时会自动保留。

4.3.3 批量转换

实际项目中,经常要处理成百上千个文件。我写了个批量转换脚本,分享给你。

import os
import glob
import open3d as o3d

def batch_convert(input_dir, output_dir, input_ext, output_ext):
    # 创建输出目录
    os.makedirs(output_dir, exist_ok=True)
    
    # 查找所有输入文件
    pattern = os.path.join(input_dir, f"*.{input_ext}")
    files = glob.glob(pattern)
    
    for i, file_path in enumerate(files):
        # 生成输出文件名
        base_name = os.path.basename(file_path)
        name_without_ext = os.path.splitext(base_name)[0]
        output_path = os.path.join(output_dir, f"{name_without_ext}.{output_ext}")
        
        # 读取并转换
        pcd = o3d.io.read_point_cloud(file_path)
        o3d.io.write_point_cloud(output_path, pcd)
        
        # 打印进度
        if (i + 1) % 10 == 0:
            print(f"已转换 {i + 1}/{len(files)} 个文件")

# 使用示例:将目录下所有 PCD 转为 PLY
batch_convert("pcd_files/", "ply_files/", "pcd", "ply")

嗯,这里要注意:批量转换时,最好加个进度提示。不然等半天不知道进度,心里没底。

4.4 知识体系总览

为了让你更直观地理解本章的知识结构,我画了一张图。

点云读写与格式转换知识体系 点云格式 PCD PLY LAS TXT BIN Open3D 读取 Open3D 写入 NumPy 辅助 BIN → PCD PCD → PLY 批量转换脚本

从这张图可以看出来,整个知识体系分三层:格式认知 → 读写操作 → 实战转换。每一层都依赖上一层。你先把格式搞明白,再学读写,最后做转换,就水到渠成了。

我的建议: 刚开始学的时候,别急着搞批量转换。先拿一个文件,手动走一遍流程。确认每一步都正确了,再上脚本。这样出了问题,你也能快速定位。

专注资料整理