使用新的highgui 3D可视化功能(附代码示例)

245 阅读5分钟

最近我通过GSoC计划对highgui模块做出了贡献,增加了viz3d命名空间。那么,新的功能是什么,如何使用它们?

简介

三维视觉一直是计算机视觉学科的一个非常重要的部分。如今,随着机器人、自动驾驶汽车、虚拟和增强现实等技术的快速发展,它比以往任何时候都更加重要。因此,能够方便地使用3D算法和快速建立解决方案的原型是一个非常重要和理想的功能,而OpenCV,作为一个方便的原型解决方案,应该提供必要的工具。

三维可视化模块(称为viz)已经是OpenCV的一部分,但不幸的是,它依赖于相当大的3D-party VTK库,在一些平台上获得/构建该库可能很困难。因此,我们决定创建一个轻量级的替代解决方案,只需使用OpenGL和本地GUI工具包(例如Windows上的Win UI或Linux上的GTK+)来显示一些常见的3D对象,如点云、网格、典型形状、相机轨迹等。这是本项目的目标。已经实现的功能包括渲染简单的基元,如立方体和球体,显示三角形网格、点云和RGB-D图像。

如何构建它

所有的新功能都在cv::viz3d命名空间中实现。每个函数的第一个参数都是发生动作的窗口的名称。与以前的3D可视化实现不同,这个新的轻量级API是现有模块 "highgui "的一部分,它被放置在opencv_contrib的一个专门的 "viz "模块中。它已经在Windows上进行了测试,只要你安装了GTK+2和GTKGLExt,应该也能在大多数Linux发行版上工作。 在Ubuntu上,用新的Viz3D功能构建OpenCV需要以下软件包:

  • libgtkglext1-dev
  • libgtk2.0-dev

OpenCV应使用以下CMake选项进行配置:

  • WITH_GTK_2_X=ON (仅在Linux上)
  • WITH_OPENGL=ON

你可以通过'example_cpp_viz3d'样本看到这些功能的作用。要构建它,你应该在CMake中配置BUILD_EXAMPLES=ON。

特性

所有的新特性都在cv::viz3d命名空间中实现。每个函数的第一个参数都是发生动作的窗口的名称。作用于对象的函数总是接受第二个参数,指定对象的名称。例如,如果我们想在坐标(5,5,5)处显示一个红色的立方体,我们可以写:

cv::Vec3f red = { 1.0f, 0.0f, 0.0 };
cv::Vec3f green = { 0.0f, 1.0f, 0.0 };
cv::Vec3f blue = { 0.0f, 0.0f, 1.0 };
/*                 window       object     box size              box color */
cv::viz3d::showBox("my window", "cube 1”, { 1.0f, 1.0f, 1.0f }, red);
/*                           window       object     new position */
cv::viz3d::setObjectPosition("my window", "cube 1", { 5.0f, 0.0f, 5.0f });
/* There's also a setObjectRotation */

如果你想显示一个有阴影或线框的立方体,你可以将渲染模式设置为RENDER_SHADING或RENDER_WIREFRAME(默认是RI如果你想显示一个有阴影或线框的立方体,你可以将渲染模式设置为RENDER_SHADING或RENDER_WIREFRAME(默认是RENDER_SIMPLE):

cv::viz3d::showBox("my window", "cube 2", { 1.0f, 1.0f, 1.0f }, red, cv::viz3d::RENDER_SHADING);
cv::viz3d::showBox("my window", "cube 3", { 1.0f, 1.0f, 1.0f }, red, cv::viz3d::RENDER_WIREFRAME);
cv::viz3d::setObjectPosition("my window", "cube 2", { 0.0f,  0.0f, 5.0f });
cv::viz3d::setObjectPosition("my window", "cube 3", { -5.0f, 0.0f, 5.0f });

当然,我们并不局限于盒子,我们还可以显示球体:

...
cv::viz3d::showSphere("my window", "sphere 1", 1.0f, green);
cv::viz3d::showSphere("my window", "sphere 2", 1.0f, green, cv::viz3d::RENDER_SHADING);
cv::viz3d::showSphere("my window", "sphere 3", 1.0f, green, cv::viz3d::RENDER_WIREFRAME);
cv::viz3d::setObjectPosition("my window", "sphere 1", { 5.0f,  0.0f, -5.0f });
cv::viz3d::setObjectPosition("my window", "sphere 2", { 0.0f,  0.0f, -5.0f });
cv::viz3d::setObjectPosition("my window", "sphere 3", { -5.0f, 0.0f, -5.0f });

和平面:

...
cv::viz3d::showPlane("my window", "plane 1", { 1.0f, 1.0f }, blue);
cv::viz3d::showPlane("my window", "plane 2", { 1.0f, 1.0f }, blue, cv::viz3d::RENDER_SHADING);
cv::viz3d::showPlane("my window", "plane 3", { 1.0f, 1.0f }, blue, cv::viz3d::RENDER_WIREFRAME);
cv::viz3d::setObjectPosition("my window", "plane 1", { 5.0f,  0.0f, 0.0f });
cv::viz3d::setObjectPosition("my window", "plane 2", { 0.0f,  0.0f, 0.0f });
cv::viz3d::setObjectPosition("my window", "plane 3", { -5.0f, 0.0f, 0.0f });

点云可以用showPoints或showRGBD来显示。第一个函数的参数是cv::Mat Points clouPoints云可以用showPoints或showRGBD来显示。第一个函数以一个包含点云的cv::Mat作为参数。它必须是二维的,有6列,每行代表一个点。前3行包含点的位置,后3行包含点的颜色。下面是代码和使用showPoints显示的蜜蜂点云。

cv::viz3d::showPoints(“my window”, “bee”,  bee_mat);

第二个函数,showRGBD,以一个cv::Mat为参数,其中包含一个RGB-D图像(4通道)和一个相机的固有矩阵,并将图像显示为点云。在这个例子中,我使用了setGridVisible来显示一个带有坐标系的网格,而不是一个十字线。我还没能实现标签,但我正在努力:

cv::Matx33f intrinsic = {
  529.5f, 0.0f, 365.0f,
  0.0f, 529.5f, 265.0f,
  0.0f, 0.0f, 1.0f,
};

cv::viz3d::showRGBD(“rgbd”,  “points”, rgbd_mat, intrinsic, 0.1f);
cv::viz3d::setGridVisible(“rgbd”, true);

还有showLines,它的工作原理与showPoints相同,但使每一对连续的点成为一条线。 showMesh遵循同样的逻辑,但用三角形代替了点。在showMesh中,你可以选择使用指数来定义三角形,或者将顶点3乘3分组。你也可以选择为输入的cv::Mat添加法线和颜色,这样显示的网格就会有阴影。

最后,可以使用setPerspective、setSun和setSky来配置摄像机、照明和环境,并且可以使用destroyObject来销毁物体。

障碍物

我遇到的一个主要问题是,尽管窗口系统已经支持OpenGL渲染,但它是非常基本的,是以旧的OpenGL为基础的。例如,虽然系统暴露了一个setOpenGlDrawCallback函数,但却没有setOpenGlFreeCallback来释放我需要分配的用于渲染的对象。我最终用新的函数扩展了窗口系统,比如setOpenGlFreeCallback和getOpenGlUserData。

这些新功能允许我在已有的系统上建立viz3d功能。当一个viz3d函数在一个窗口上被首次调用时,一个处理三维视图和窗口上所有对象的内部对象被分配,并使用setOpenGlDrawCallback进行设置,随后被新的OpenGL free callback释放。

现在我面临一个不同的问题:OpenCV的OpenGL包装器没有实现现代功能(例如,没有顶点阵列和着色器)。所以我也需要在OpenCV核心模块上扩展OpenGL包装器。我添加了Attribute、VertexArray、Shader和Program对象,并为一些没有暴露的功能添加了新的函数。

总结

这个项目所有的主要计划功能都已经实现,现在唯一缺少的功能是在网格的轴上显示坐标标签。这些功能使得在OpenCV上实现3D数据的可视化比以往更容易。Google Summer of Code是我对开源开发的介绍,我非常喜欢它。这是我第一次为像OpenCV这样大的代码库做贡献,与世界各地的人一起工作是一个很好的经历。