Open3D:如何将2D深度图转为3D曲面及选用更优数据结构?
深度图转3D曲面可视化(Open3D C++)
需求与已尝试方案
需求:希望在Open3D中实现深度图的3D可视化,效果如示例图右侧所示。
已尝试方案:将预处理得到的单通道深度图转换为PointCloud并完成可视化,代码如下:
// @image_8uc1 is single channel depth image. std::shared_ptr<geometry::PointCloud> DepthMap2PointCloud(const cv::Mat& image_8uc1, const bool normalized = false) { std::shared_ptr<geometry::PointCloud> cloud(new geometry::PointCloud); for (int y = 0; y < image_8uc1.rows; y++) { const uchar* ptr = image_8uc1.ptr<uchar>(y); for (int x = 0; x < image_8uc1.cols; x++) { if (normalized) { const double _x = ((float)x / (float)image_8uc1.cols); const double _y = ((float)y / (float)image_8uc1.rows); const double _z = (float)ptr[x] / 255.0; Eigen::Vector3d p(_x, _y, _z); cloud->points_.push_back(p); } else { Eigen::Vector3d p(x, y, ptr[x]); cloud->points_.push_back(p); } } } return cloud; } // ... auto cloud = DepthMap2PointCloud(map); visualization::Visualizer visualizer; visualizer.CreateVisualizerWindow("Open3D", 1600, 900); visualizer.AddGeometry(cloud); visualizer.Run(); visualizer.DestroyVisualizerWindow();
可视化结果如图所示。
问题
- 如何利用深度图中所有相邻点(x,y)间距一致的结构特性创建曲面?
- 在Open3D中,除
open3d::geometry::PointCloud外,是否有更有效的数据结构解决该问题?
解决方案
针对问题1:利用深度图结构创建曲面
深度图的像素点是规则网格排列的,相邻点(x,y)间距一致,这种结构天然适合构建三角网格(TriangleMesh),具体步骤:
- 先按原有逻辑生成所有顶点的3D坐标;
- 遍历所有非边缘的像素点,为每一组相邻的四个像素((x,y)、(x+1,y)、(x,y+1)、(x+1,y+1))创建两个三角面:
- 第一个三角面:顶点索引为
y*width + x、y*width + x+1、(y+1)*width + x - 第二个三角面:顶点索引为
y*width + x+1、(y+1)*width + x+1、(y+1)*width + x
- 第一个三角面:顶点索引为
- 完成遍历后即可得到连续的曲面网格。
针对问题2:更高效的数据结构
Open3D中**open3d::geometry::TriangleMesh**是更适配的结构,原因如下:
- 它直接存储顶点和面的拓扑关系,可视化时渲染为连续曲面,而非离散点;
- 相比点云,三角网格的渲染效率更高,GPU可批量处理面绘制;
- 针对深度图的规则网格,生成三角网格的逻辑简单高效,无需额外点云重建算法(如泊松重建)。
如果需要保留深度图纹理,还可以结合open3d::geometry::Image为三角网格添加纹理坐标,实现带纹理的3D曲面可视化。
示例代码(深度图转TriangleMesh)
#include <open3d/geometry/TriangleMesh.h> std::shared_ptr<open3d::geometry::TriangleMesh> DepthMap2TriangleMesh(const cv::Mat& image_8uc1, bool normalized = false) { auto mesh = std::make_shared<open3d::geometry::TriangleMesh>(); int width = image_8uc1.cols; int height = image_8uc1.rows; // 生成顶点 for (int y = 0; y < height; y++) { const uchar* ptr = image_8uc1.ptr<uchar>(y); for (int x = 0; x < width; x++) { double _x, _y, _z; if (normalized) { _x = static_cast<float>(x) / width; _y = static_cast<float>(y) / height; _z = static_cast<float>(ptr[x]) / 255.0; } else { _x = x; _y = y; _z = ptr[x]; } mesh->vertices_.push_back(Eigen::Vector3d(_x, _y, _z)); } } // 生成三角面 for (int y = 0; y < height - 1; y++) { for (int x = 0; x < width - 1; x++) { int idx0 = y * width + x; int idx1 = y * width + x + 1; int idx2 = (y + 1) * width + x; int idx3 = (y + 1) * width + x + 1; // 添加两个三角形 mesh->triangles_.push_back(Eigen::Vector3i(idx0, idx1, idx2)); mesh->triangles_.push_back(Eigen::Vector3i(idx1, idx3, idx2)); } } // 计算法向量(可选,提升曲面光照效果) mesh->ComputeVertexNormals(); return mesh; } // 可视化调用 auto mesh = DepthMap2TriangleMesh(map); open3d::visualization::Visualizer visualizer; visualizer.CreateVisualizerWindow("Open3D Mesh", 1600, 900); visualizer.AddGeometry(mesh); visualizer.Run(); visualizer.DestroyVisualizerWindow();
内容的提问来源于stack exchange,提问作者wsys
相关产品推荐
相关产品推荐

