PCDViewer 支持图形文件

张开发
2026/5/2 3:21:58 15 分钟阅读

分享文章

PCDViewer 支持图形文件
PCDViewer最早在2022年开始支持了图形文件的显示初衷是支持自动驾驶中高精地图数据的显示在5.3.0中优化了对图形文件的支持基于Json定义了一种简单的图形文件格式以便开发者和用户进行图形文件的创建和显示5.4.0中进一步优化了图形文件格式文件格式更加简简洁增加了属性支持且增强了扩展性。图形文件的核心原则PCDViewer 5.4.0 基于图元Geomtry和节点Node组织场景数据通过节点构成树形结构各节点可以有自己的名称Name和位姿Transformation以实现丰富的场景表示。图元设计了五种图元各图元有包括颜色、大小、绘制方式等属性具体如下图元Geomtry属性Attributes点Pointsuchar color[4];int point_size{ 1 };线段Linesuchar color[4];int line_width{ 1 };bool dash{ false };三角形Trianglesuchar color[4];FaceMode mode;//包括点模型、线框模型、填充模型多线段Polylineuchar color[4];int line_width{ 1 };bool dash{ false };多边形Polygonuchar color[4];FaceMode mode;//包括点模型、线框模型、填充模型节点通过节点构成树形结构组织场景struct Transformation { Eigen::Quaterniond R; Eigen::Vector3d t; }; struct GraphicsNode { std::string name; Transformation T; //pose in its parent nodes coordination system. std::vectorGeometryBasePtr geomtries; std::vectorstd::shared_ptrGraphicsNode children; };示例效果图形文件相关的代码Graphics.h#ifndef PCDVIEWER_GEOMETRY_H #define PCDVIEWER_GEOMETRY_H #include memory #include string #include vector #include Eigen/Geometry namespace pcd_viewer_ns { struct Point3 { union { struct { float x, y, z; }; float data[3]; }; float operator[] (size_t i) { return data[i]; } const float operator[] (size_t i) const { return data[i]; } Point3() {} Point3(float x_, float y_, float z_) { data[0] x_; data[1] y_; data[2] z_; } Point3(const Eigen::Vector3d p) { data[0] p[0]; data[1] p[1]; data[2] p[2]; } }; enum class GeometryType { Points, Lines, Triangles, Polyline, Polygon }; enum class PolygonMode { Point, Line, Fill }; struct FaceMode { PolygonMode frontMode{ PolygonMode::Fill}; PolygonMode backMode{ PolygonMode::Fill }; }; struct GeometryBase { GeometryBase(GeometryType type_): type(type_) {} std::string name; std::vectorPoint3 points; const GeometryType type; bool alpha_enable{ false }; //r, g, b, a std::uint8_t color[4]{ 255, 255, 255, 255 }; void setColor(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a 255) { color[0] r; color[1] g; color[2] b; color[3] a; } GeometryType getType() const { return type; } }; struct Points: GeometryBase { int point_size{ 1 }; Points(): GeometryBase(GeometryType::Points) {} }; struct Lines : GeometryBase { int line_width{ 1 }; bool dash{ false }; Lines() : GeometryBase(GeometryType::Lines) {} }; struct Polyline : GeometryBase { int line_width{ 1 }; bool dash{ false }; Polyline(): GeometryBase(GeometryType::Polyline) {} }; struct Triangles : GeometryBase { FaceMode mode; Triangles() : GeometryBase(GeometryType::Triangles) {} }; struct Polygon : GeometryBase { FaceMode mode; Polygon() : GeometryBase(GeometryType::Polygon) {} }; using GeometryBasePtr std::shared_ptrGeometryBase; struct Transformation { Eigen::Quaterniond R{ 1, 0, 0, 0 }; Eigen::Vector3d t{ 0, 0, 0 }; Transformation() {} Transformation(const Eigen::Quaterniond q_, const Eigen::Vector3d t_) { R q_; t t_; } }; struct GraphicsNode { std::string name; Transformation T; //transformation (pose) in its parent nodes coordination system. std::vectorGeometryBasePtr geomtries; std::vectorstd::shared_ptrGraphicsNode children; }; using GraphicsNodePtr std::shared_ptrGraphicsNode; using GraphicsData GraphicsNodePtr; } #endif // !PCDVIEWER_GEOMETRY_HGraphicsIO.h#ifndef PCDVIEWER_GRAPHICS_IO_H #define PCDVIEWER_GRAPHICS_IO_H #include Graphics.h namespace pcd_viewer_ns { bool read_graphics_data(const std::string graphics_json_file, GraphicsData data); void save_graphics_data(const GraphicsData data, const std::string graphics_json_file); } #endif // !PCDVIEWER_GEOMETRY_HGraphicsIO.cpp#include GraphicsIO.h #include nlohmann/json.hpp #include fstream #include iostream using json nlohmann::json; namespace pcd_viewer_ns { // --- 辅助函数保存 Geometry 数据到 JSON 对象 --- json geometry_to_json(const GeometryBasePtr geom) { json j; // 1. 保存公共属性 j[name] geom-name; j[type] static_castint(geom-type); // 保存枚举值 j[alpha_enable] geom-alpha_enable; // 保存颜色 j[color] { geom-color[0], geom-color[1], geom-color[2], geom-color[3] }; // 保存点集 auto j_points j[points]; for (const auto p : geom-points) { json j_point; j_point[x] p.x; j_point[y] p.y; j_point[z] p.z; j_points.push_back(j_point); } // 2. 根据类型保存特有属性 switch (geom-type) { case GeometryType::Points: { auto p static_castPoints*(geom.get()); j[point_size] p-point_size; break; } case GeometryType::Lines: { auto l static_castLines*(geom.get()); j[line_width] l-line_width; j[dash] l-dash; break; } case GeometryType::Polyline: { auto pl static_castPolyline*(geom.get()); j[line_width] pl-line_width; j[dash] pl-dash; break; } case GeometryType::Triangles: { auto t static_castTriangles*(geom.get()); j[front_mode] static_castint(t-mode.frontMode); j[back_mode] static_castint(t-mode.backMode); break; } case GeometryType::Polygon: { auto poly static_castPolygon*(geom.get()); j[front_mode] static_castint(poly-mode.frontMode); j[back_mode] static_castint(poly-mode.backMode); break; } } return j; } // 假设 JsonNode 是 nlohmann::json 的别名或类型 using JsonNode nlohmann::json; void write_transformation(JsonNode node, const Transformation T) { const auto p T.t; const auto q T.R; // 1. 写入平移 [x, y, z] node[translation] { p.x(), p.y(), p.z() }; node[rotation] { q.x(), q.y(), q.z(), q.w() }; } /** * 从 JSON 节点读取 Transformation */ Transformation read_transformation(const JsonNode node) { Eigen::Vector3d p(0, 0, 0); Eigen::Quaterniond q(1, 0, 0, 0); // 1. 读取平移 // 假设 translation 是一个包含3个数字的数组 if (node.contains(translation) node[translation].is_array()) { auto t node[translation]; if (t.size() 3) { p[0] t[0].getdouble(); p[1] t[1].getdouble(); p[2] t[2].getdouble(); } } // 2. 读取旋转 // 假设 rotation 是一个包含4个数字的数组 [qx, qy, qz, qw] if (node.contains(rotation) node[rotation].is_array()) { auto r node[rotation]; if (r.size() 4) { double qx r[0].getdouble(); double qy r[1].getdouble(); double qz r[2].getdouble(); double qw r[3].getdouble(); // Eigen::Quaterniond 的构造函数参数顺序是 (w, x, y, z) q Eigen::Quaterniond(qw, qx, qy, qz); // 可选归一化四元数防止数值误差导致缩放 q.normalize(); } } return Transformation(q, p); } // --- 辅助函数从 JSON 创建 Geometry 指针 --- GeometryBasePtr json_to_geometry(const json j) { GeometryBasePtr ptr nullptr; GeometryType type static_castGeometryType(j.at(type).getint()); // 1. 根据类型创建对象 switch (type) { case GeometryType::Points: ptr std::make_sharedPoints(); break; case GeometryType::Lines: ptr std::make_sharedLines(); break; case GeometryType::Polyline: ptr std::make_sharedPolyline(); break; case GeometryType::Triangles: ptr std::make_sharedTriangles(); break; case GeometryType::Polygon: ptr std::make_sharedPolygon(); break; default: return nullptr; } // 2. 恢复公共属性 ptr-name j.value(name, ); { const auto j_points j[points]; ptr-points.reserve(j_points.size()); for (const auto j_point : j_points) { ptr-points.emplace_back(j_point[x], j_point[y], j_point[z]); } } ptr-alpha_enable j.value(alpha_enable, false); auto color_arr j.at(color).getstd::vectorstd::uint8_t(); if (color_arr.size() 4) { ptr-setColor(color_arr[0], color_arr[1], color_arr[2], color_arr[3]); } // 3. 恢复特有属性 if (type GeometryType::Points) { auto p static_castPoints*(ptr.get()); p-point_size j.value(point_size, 1); } else if (type GeometryType::Lines) { auto lines static_castLines*(ptr.get()); lines-line_width j.value(line_width, 1); lines-dash j.value(dash, false); } else if (type GeometryType::Polyline) { auto poly static_castPolyline*(ptr.get()); poly-line_width j.value(line_width, 1); poly-dash j.value(dash, false); } else if (type GeometryType::Triangles || type GeometryType::Polygon) { FaceMode mode; mode.frontMode static_castPolygonMode(j.value(front_mode, 2)); // 2 is Fill mode.backMode static_castPolygonMode(j.value(back_mode, 2)); if (type GeometryType::Triangles) { auto tris static_castTriangles*(ptr.get()); tris-mode mode; } else { auto plg static_castPolygon*(ptr.get()); plg-mode mode; } } return ptr; } // --- 核心函数保存数据 --- void save_graphics_data(const GraphicsData data, const std::string graphics_json_file) { if (!data) return; // 递归函数将节点树转换为 JSON std::functionjson(const GraphicsNodePtr) node_to_json [](const GraphicsNodePtr node) - json { json j; j[name] node-name; // 保存几何体列表 json geom_array json::array(); for (const auto geom : node-geomtries) { geom_array.push_back(geometry_to_json(geom)); } j[geometries] geom_array; auto j_transformation j[transformation]; write_transformation(j_transformation, node-T); // 递归保存子节点 (嵌套结构的关键) json children_array json::array(); for (const auto child : node-children) { children_array.push_back(node_to_json(child)); } j[children] children_array; return j; }; json final_json node_to_json(data); // 写入文件 std::ofstream o(graphics_json_file); if (o.is_open()) { o final_json.dump(4); // 4 表示缩进空格数方便阅读 o.close(); } else { std::cerr Error: Could not open file for writing: graphics_json_file std::endl; } } // --- 核心函数读取数据 --- bool read_graphics_data(const std::string graphics_json_file, GraphicsData data) { std::ifstream i(graphics_json_file); if (!i.is_open()) { std::cerr Error: Could not open file: graphics_json_file std::endl; return false; } try { json j; i j; // 解析文件流 // 递归函数将 JSON 转换为节点树 std::functionGraphicsNodePtr(const json) json_to_node [](const json node_j) - GraphicsNodePtr { auto node std::make_sharedGraphicsNode(); node-name node_j.value(name, unnamed); // 恢复几何体 if (node_j.contains(geometries) node_j[geometries].is_array()) { for (const auto geom_j : node_j[geometries]) { auto geom_ptr json_to_geometry(geom_j); if (geom_ptr) { node-geomtries.push_back(geom_ptr); } } } if (node_j.contains(transformation)) { node-T read_transformation(node_j[transformation]); } // 递归恢复子节点 if (node_j.contains(children) node_j[children].is_array()) { for (const auto child_j : node_j[children]) { node-children.push_back(json_to_node(child_j)); } } return node; }; data json_to_node(j); return true; } catch (const std::exception e) { std::cerr JSON Parse Error: e.what() std::endl; return false; } } }

更多文章