Utils/CloudView/Inc/PointCloudConverter.h
杰仔 7e887aaeee feat(点云加载): 添加PLY文件格式支持并更新版本号至1.1.4
实现PLY文件加载功能,支持ASCII和二进制格式(大端/小端)
扩展文件对话框过滤器包含PLY格式
统一PLY和PCD文件的颜色处理逻辑
2026-04-10 20:13:04 +08:00

199 lines
6.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef POINT_CLOUD_CONVERTER_H
#define POINT_CLOUD_CONVERTER_H
#include <string>
#include <vector>
#include <memory>
/**
* @brief 简单的 3D 点结构
*/
struct Point3D
{
float x, y, z;
Point3D() : x(0), y(0), z(0) {}
Point3D(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
};
/**
* @brief 带颜色的 3D 点结构
*/
struct Point3DRGB
{
float x, y, z;
uint8_t r, g, b;
float pointSize; // 自定义点大小0 表示使用全局默认值(来自 RGBA 中 A > 1 的值)
Point3DRGB() : x(0), y(0), z(0), r(255), g(255), b(255), pointSize(0) {}
Point3DRGB(float _x, float _y, float _z, uint8_t _r = 255, uint8_t _g = 255, uint8_t _b = 255, float _ps = 0)
: x(_x), y(_y), z(_z), r(_r), g(_g), b(_b), pointSize(_ps) {}
};
/**
* @brief 简单的点云容器
*/
template<typename PointT>
class SimplePointCloud
{
public:
std::vector<PointT> points;
std::vector<int> lineIndices; // 每个点所属的线索引
void clear() { points.clear(); lineIndices.clear(); }
size_t size() const { return points.size(); }
bool empty() const { return points.empty(); }
void reserve(size_t n) { points.reserve(n); lineIndices.reserve(n); }
void push_back(const PointT& pt) { points.push_back(pt); }
void push_back(const PointT& pt, int lineIdx) {
points.push_back(pt);
lineIndices.push_back(lineIdx);
}
};
using PointCloudXYZ = SimplePointCloud<Point3D>;
using PointCloudXYZRGB = SimplePointCloud<Point3DRGB>;
/**
* @brief 点云数据转换器
* 负责加载 txt 和 pcd 格式的点云文件
*/
class PointCloudConverter
{
public:
PointCloudConverter();
~PointCloudConverter();
/**
* @brief 从 txt 文件加载点云(使用 CloudUtils
*/
int loadFromTxt(const std::string& fileName, PointCloudXYZ& cloud);
int loadFromTxt(const std::string& fileName, PointCloudXYZRGB& cloud);
/**
* @brief 从 pcd 文件加载点云(简单 ASCII/Binary 解析)
*/
int loadFromPcd(const std::string& fileName, PointCloudXYZ& cloud);
int loadFromPcd(const std::string& fileName, PointCloudXYZRGB& cloud);
/**
* @brief 从 ply 文件加载点云(支持 ASCII / Binary Little-Endian / Binary Big-Endian
*/
int loadFromPly(const std::string& fileName, PointCloudXYZ& cloud);
int loadFromPly(const std::string& fileName, PointCloudXYZRGB& cloud);
/**
* @brief 根据文件扩展名自动选择加载方式
*/
int loadFromFile(const std::string& fileName, PointCloudXYZ& cloud);
int loadFromFile(const std::string& fileName, PointCloudXYZRGB& cloud);
/**
* @brief 保存点云到 txt 文件(使用 CloudUtils
*/
int saveToTxt(const std::string& fileName, const PointCloudXYZ& cloud, int lineNum, int linePtNum);
/**
* @brief 旋转点云(行列转置 + 交换XY
* @param cloud 输入点云
* @param rotatedCloud 输出旋转后的点云
* @param lineNum 原始线数
* @param linePtNum 原始每线点数
* @param newLineNum 输出新的线数
* @param newLinePtNum 输出新的每线点数
*/
int rotateCloud(const PointCloudXYZ& cloud, PointCloudXYZ& rotatedCloud,
int lineNum, int linePtNum, int& newLineNum, int& newLinePtNum);
/**
* @brief 获取最后的错误信息
*/
std::string getLastError() const { return m_lastError; }
/**
* @brief 获取加载的点云数量
*/
size_t getLoadedPointCount() const { return m_loadedPointCount; }
/**
* @brief 获取加载的线数量
*/
int getLoadedLineCount() const { return m_loadedLineCount; }
/**
* @brief 上次 loadFromTxt(PointCloudXYZRGB&) 加载的文件是否包含颜色数据
*/
bool lastLoadHadColor() const { return m_lastLoadHadColor; }
private:
/**
* @brief 获取文件扩展名(小写)
*/
std::string getFileExtension(const std::string& fileName);
/**
* @brief 解析 PCD 文件头
*/
struct PcdHeader
{
int width = 0;
int height = 1;
int points = 0;
bool isBinary = false;
bool hasRgb = false;
std::vector<std::string> fields;
std::vector<int> fieldSizes;
std::vector<char> fieldTypes;
int pointSize = 0;
};
bool parsePcdHeader(std::ifstream& file, PcdHeader& header);
/**
* @brief PLY 属性描述
*/
struct PlyProperty
{
std::string name; // 属性名 (x, y, z, red, green, blue, alpha, nx, ny, nz ...)
std::string type; // 数据类型 (float, double, uchar, int, short ...)
int byteSize = 0; // 字节数
bool isList = false; // 是否是 list 类型(如 face 的 vertex_indices
std::string listCountType; // list 的计数类型
std::string listValueType; // list 的值类型
};
/**
* @brief PLY 元素描述
*/
struct PlyElement
{
std::string name; // 元素名 (vertex, face ...)
int count = 0; // 元素数量
std::vector<PlyProperty> properties;
};
/**
* @brief PLY 文件头
*/
struct PlyHeader
{
enum Format { ASCII, BINARY_LE, BINARY_BE };
Format format = ASCII;
std::vector<PlyElement> elements;
int vertexElementIndex = -1; // vertex 元素在 elements 中的索引
};
bool parsePlyHeader(std::ifstream& file, PlyHeader& header);
int getPlyTypeByteSize(const std::string& type);
/**
* @brief 计算 PLY 元素中一个非 list 记录的总字节数
*/
int calcPlyElementStride(const PlyElement& element);
std::string m_lastError;
size_t m_loadedPointCount;
int m_loadedLineCount;
bool m_lastLoadHadColor;
};
#endif // POINT_CLOUD_CONVERTER_H