点云ply读写时候遇到几个细节的问题,代码放在下面,做好总结。
一般ply格式点云的一个点的结构都可以直接使用notepad++打开ply文件查看头部:
可以看出每个点都包含float类型的xyz和uchar类型的rgb,ply文件的头部对读写都很重要。
通过下面生成新的ply文件进行演示:
#pragma pack(push, 1) // 设置内存对齐方式为 1 字节,点云一般都是内存紧密排列的
//自定义点格式
struct SelDefPoint
{
float x, y, z;
unsigned char intensity;
};
//待解析的点云格式
struct CCPoint
{
float x, y, z;
unsigned char red, green, blue;
};
#pragma pack(pop) // 恢复默认的内存对齐方式
//ply_file 原始ply文件路径;newPlyPath 新生成的ply文件路径
bool NewPlyFile(string ply_file, string newPlyPath) {
std::ifstream inputFile(ply_file, std::ios::binary);//一定记得要求读写格式为二进制,否则头部末尾后所有内容读取失败
if (!inputFile.is_open()) {
std::cerr << "Failed to open PLY file: " << ply_file << std::endl;
return 1;
}
// 读取 PLY 头部信息
std::string line;
while (std::getline(inputFile, line)) {
if (line == "end_header") {
break; // 头部信息结束才能真正开始读取点数据
}
}
// 读取点云数据
std::vector<CCPoint> points;
CCPoint point;
while (inputFile.read(reinterpret_cast<char*>(&point), sizeof(CCPoint))) {
points.push_back(point);
}
inputFile.close();
std::ofstream outFile(newPlyPath, std::ios::binary);
if (!outFile.is_open()) {
std::cerr << "Failed to open output file" << std::endl;
return 1;
}
//定义新ply的头部
char plyHeader[] =
"ply\n"
"format binary_little_endian 1.0\n"
"element vertex %d\n"
"property float x\n"
"property float y\n"
"property float z\n"
"property uchar intensity\n"
"end_header\n";
// 替换 %d 为实际的顶点数量
int numVertices = points.size();
char header[1024]; // 根据ply头大小适当调整
std::sprintf(header, plyHeader, numVertices);//需要写入顶点数目
// 将PLY头部信息以二进制形式写入文件,写完后就可以直接在notepad++中打开验证了
outFile.write(header, strlen(header));
std::vector<SelDefPoint> selDefPoints;
//使用新的点格式拷贝所需要的数据,在这里,使用原始数据的red作为新数据的intensity
for (auto pt:points)
{
SelDefPoint tmp;
tmp.x = pt.x;
tmp.y = pt.y;
tmp.z = pt.z;
tmp.intensity = pt.red;
selDefPoints.push_back(tmp);
}
for (int i=0;i<selDefPoints.size();i++) {
//使用.write的方法写入新的ply文件中,而不是<<重定向
outFile.write(reinterpret_cast<const char*>(&selDefPoints[i]), sizeof(SelDefPoint));
}
outFile.close();
return 0;
}
总结
- 确定ply头格式中的property类型和数目,根据它创建对应的结构体
- 要使用%d填入顶点的个数
- 确定是否需要紧密排列结构体的内存,否则容易造成读写出错
- 注意结构体属性类型是否和ply头的声明类型一致,否则也会读写出错
- 使用write进行读写而不是重定向符号