#ifndef POINT_CLOUD_CONVERTER_H #define POINT_CLOUD_CONVERTER_H #include #include #include #include #include "CloudView3DTypes.h" #include "PointCloudXYZRange.h" /** * @brief 点云数据转换器 * 负责加载 txt/dat、pcd、ply 格式的点云文件 */ class PointCloudConverter { public: // 保留原有的双参数回调契约,兼容已有调用方。 using PointCloudLoadCallback = std::function; // 扩展回调携带过滤后的源扫描线布局信息。 using PointCloudLoadIndexedCallback = std::function; using PointCloudLoadFinishedCallback = std::function; PointCloudConverter(); ~PointCloudConverter(); // Set the inclusive XYZ range used by subsequent load operations. // The default-constructed range leaves all points untouched. void setPointCloudRange(const PointCloudXYZRange& range); const PointCloudXYZRange& pointCloudRange() const { return m_pointCloudRange; } /** * @brief 从 txt/dat 文件加载点云(使用 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); /** * Load txt/dat scan data one source line at a time and invoke callbacks * immediately; a filtered source line may produce an empty cloud chunk. */ int loadFromFileByCallback(const std::string& fileName, const PointCloudLoadCallback& lineCallback, const PointCloudLoadFinishedCallback& finishedCallback); // 扩展重载保留源扫描线及点索引。 int loadFromFileByCallback(const std::string& fileName, const PointCloudLoadIndexedCallback& lineCallback, const PointCloudLoadFinishedCallback& finishedCallback); // 显式命名的扩展入口,便于外部代码避免回调重载推导。 int loadFromFileByIndexedCallback( const std::string& fileName, const PointCloudLoadIndexedCallback& lineCallback, const PointCloudLoadFinishedCallback& finishedCallback); /** * @brief 保存点云到 txt 文件(使用 CloudUtils) */ int saveToTxt(const std::string& fileName, const PointCloudXYZ& cloud, int lineNum, int linePtNum); /** * @brief 保存点云到 PLY 文件(ASCII 格式) * @param fileName 输出文件路径 * @param cloud 点云数据 * @return 0=成功, -1=失败 */ int saveToPly(const std::string& fileName, const PointCloudXYZ& cloud); /** * @brief 保存点云到 PCD 文件(ASCII 格式,兼容 PCL) * @param fileName 输出文件路径 * @param cloud 点云数据 * @return 0=成功, -1=失败 */ int saveToPcd(const std::string& fileName, const PointCloudXYZ& cloud); /** * @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: bool validatePointCloudRange(); /** * @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 fields; std::vector fieldSizes; std::vector 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 properties; }; /** * @brief PLY 文件头 */ struct PlyHeader { enum Format { ASCII, BINARY_LE, BINARY_BE }; Format format = ASCII; std::vector 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; PointCloudXYZRange m_pointCloudRange; }; #endif // POINT_CLOUD_CONVERTER_H