在三维点云处理的实际开发中,经常需要将内存中存储的三维点云顶点数据持久化到文件中,PLY格式作为点云领域常用的通用存储格式,其二进制版本相比文本版本拥有更小的文件体积和更快的读写速度,更适合存储大规模点云数据。要实现高效的保存,首先需要明确PLY二进制格式的结构规范,再结合C++的文件操作特性完成实现。

PLY二进制格式结构说明
PLY格式文件由文件头和数据体两部分组成,二进制格式的标识在文件头中通过format binary_little_endian 1.0或者format binary_big_endian 1.0指定,分别对应小端和大端字节序。常见的点云顶点属性通常包含x、y、z坐标,部分场景还会包含r、g、b颜色值,文件头需要明确声明这些属性的类型和数量。
一个基础的点云PLY二进制文件头示例如下:
ply format binary_little_endian 1.0 element vertex 1000 property float x property float y property float z end_header
其中element vertex 1000表示点云包含1000个顶点,后续每个顶点的x、y、z坐标都是float类型,数据体部分就是所有顶点的二进制数据按顺序排列。
内存点云数据结构定义
首先定义内存中存储点云顶点的结构体,假设我们的点云只包含三维坐标,每个坐标用float类型存储:
#include <fstream>
#include <vector>
#include <cstdint>
#include <string>
// 定义点云顶点结构体,包含x、y、z三维坐标
struct Point3D {
float x;
float y;
float z;
};
// 定义点云数据类型,用vector存储所有顶点
using PointCloud = std::vector<Point3D>;
高效写入PLY二进制的实现步骤
要实现高效写入,核心思路是减少文件操作的次数,一次性将文件头和数据体写入文件,同时避免不必要的数据拷贝。具体步骤如下:
- 第一步:打开文件,设置为二进制写入模式
- 第二步:按照PLY格式规范写入文件头内容
- 第三步:将内存中的点云数据直接写入文件数据体部分
- 第四步:关闭文件,完成保存
完整实现代码
下面是完整的C++实现代码,支持将内存中的点云顶点数据保存为小端字节序的PLY二进制格式:
/**
* 将内存中的点云顶点数据保存为PLY二进制格式
* @param pointCloud 内存中的点云数据
* @param filePath 输出文件路径
* @return 保存成功返回true,失败返回false
*/
bool savePointCloudToPlyBinary(const PointCloud& pointCloud, const std::string& filePath) {
// 打开文件,二进制写入模式,截断已有内容
std::ofstream outFile(filePath, std::ios::binary | std::ios::trunc);
if (!outFile.is_open()) {
return false;
}
// 写入PLY文件头
outFile << "plyn";
// 使用小端字节序,大多数PC平台都是小端
outFile << "format binary_little_endian 1.0n";
outFile << "element vertex " << pointCloud.size() << "n";
outFile << "property float xn";
outFile << "property float yn";
outFile << "property float zn";
outFile << "end_headern";
// 直接将顶点数据写入文件,避免逐元素拷贝
if (!pointCloud.empty()) {
outFile.write(reinterpret_cast<const char*>(pointCloud.data()),
pointCloud.size() * sizeof(Point3D));
}
// 检查写入是否成功
bool success = outFile.good();
outFile.close();
return success;
}
包含颜色属性的扩展实现
如果点云还包含r、g、b颜色属性,每个颜色通道用unsigned char类型存储,只需要调整结构体定义和文件头即可:
// 包含颜色属性的点云顶点结构体
struct Point3DWithColor {
float x;
float y;
float z;
unsigned char r;
unsigned char g;
unsigned char b;
};
using PointCloudWithColor = std::vector<Point3DWithColor>;
bool savePointCloudWithColorToPlyBinary(const PointCloudWithColor& pointCloud, const std::string& filePath) {
std::ofstream outFile(filePath, std::ios::binary | std::ios::trunc);
if (!outFile.is_open()) {
return false;
}
outFile << "plyn";
outFile << "format binary_little_endian 1.0n";
outFile << "element vertex " << pointCloud.size() << "n";
outFile << "property float xn";
outFile << "property float yn";
outFile << "property float zn";
outFile << "property uchar redn";
outFile << "property uchar greenn";
outFile << "property uchar bluen";
outFile << "end_headern";
if (!pointCloud.empty()) {
outFile.write(reinterpret_cast<const char*>(pointCloud.data()),
pointCloud.size() * sizeof(Point3DWithColor));
}
bool success = outFile.good();
outFile.close();
return success;
}
效率优化技巧
为了进一步提升保存效率,可以采用以下优化方法:
- 使用
write函数一次性写入整块数据,避免循环逐个写入顶点,减少系统调用次数 - 提前计算好文件头的长度,如果需要追加数据可以准确定位,但PLY格式通常一次性写入无需追加
- 如果内存中的点云数据量极大,可以分块写入,避免一次性占用过多内存,但分块时需要注意保持数据顺序和格式正确
- 确认平台的字节序和PLY文件声明的字节序一致,避免额外的字节序转换开销,小端字节序在x86和ARM平台都是主流选择
注意事项
实现过程中需要注意几个常见问题:
- 文件头必须使用文本格式写入,每行以换行符结尾,不能混入二进制数据
- 结构体的内存对齐需要和PLY格式的属性类型大小匹配,上述示例中的float是4字节,unsigned char是1字节,默认对齐下不会出现问题,如果自定义结构体包含不同大小的类型,可以通过
#pragma pack调整对齐方式 - 写入数据体时,必须使用
reinterpret_cast将结构体指针转换为char指针,不能直接用outFile <<输出,否则会被当成文本写入导致格式错误 - 如果需要在大端字节序的平台运行,需要将数据转换为小端字节序再写入,或者修改文件头的格式声明为
binary_big_endian
通过上述方法,就可以在C++中高效地将内存中的三维点云顶点数据保存为PLY二进制格式,满足大规模点云数据的存储需求。
C++PLY_binarypoint_cloudvertex_datafile_IO修改时间:2026-06-09 18:27:40