Open3d之非阻塞可视化

当需要快速可视化静态几何形状时,draw_geometries()是一个十分有用的函数。然而这个函数会锁定一个进程直到可视化的窗口被关闭。当需要在不关闭窗口的情况下更新几何图形并可视化的话,这并不是一个最优的选择。本教程介绍了一个自定义渲染循环的教程。

回顾draw_geometries

draw_geometries()具有以下渲染循环(C++ 的实现请看Visualizer::Run()):

while(true):
    if (geometry has changed):
        re-bind geometry to shaders
    if (view parameters have changed):
        re-render the scene
    if (any user mouse/keyboard input):
        respond to it and set flags for re-rendering

需要注意的是,绑定几何体和渲染都是很费资源的操作,因此它们以懒惰的方式执行的。它们分别通过两个独立的标志控制。update_geometry()update_renderer()函数可以将这两个标志设为打开。在重新绑定或者渲染之后这俩标志会被清空。

自定义的渲染循环很容易实现,比如下面的例子就可以自定义循环来可视化ICP配准的过程。

vis = Visualizer()
vis.create_window()
for i in range(icp_iteration):
    # do ICP single iteration
    # transform geometry using ICP
    vis.update_geometry(geometry)
    vis.poll_events()
    vis.update_renderer()

完整的脚本如下:

# -*- coding:utf-8 -*-
import numpy as np
import open3d as o3d

# 设置Open3D调试级别
o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)
# 加载点云
source_raw = o3d.io.read_point_cloud("../../test_data/ICP/cloud_bin_0.pcd")
target_raw = o3d.io.read_point_cloud("../../test_data/ICP/cloud_bin_1.pcd")
# 体素下采样
source = source_raw.voxel_down_sample(voxel_size=0.02)
target = target_raw.voxel_down_sample(voxel_size=0.02)
# 原始点云空间转换
trans = [[0.862, 0.011, -0.507, 0.0], [-0.139, 0.967, -0.215, 0.7],
         [0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]]
source.transform(trans)
# 翻转
flip_transform = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]
source.transform(flip_transform)
target.transform(flip_transform)
# 可视化
vis = o3d.visualization.Visualizer()
vis.create_window()
vis.add_geometry(source)
vis.add_geometry(target)
threshold = 0.05
icp_iteration = 100
save_image = False

for i in range(icp_iteration):
    # 配准
    reg_p2l = o3d.pipelines.registration.registration_icp(
        source, target, threshold, np.identity(4),
        o3d.pipelines.registration.TransformationEstimationPointToPlane(),
        o3d.pipelines.registration.ICPConvergenceCriteria(max_iteration=1))
    source.transform(reg_p2l.transformation)
    vis.update_geometry(source)
    vis.poll_events()
    vis.update_renderer()
    if save_image:
        vis.capture_screen_image("temp_%04d.jpg" % i)
vis.destroy_window()

下面是完整代码的解释。

预处理实验数据

 加载点云
source_raw = o3d.io.read_point_cloud("../../test_data/ICP/cloud_bin_0.pcd")
target_raw = o3d.io.read_point_cloud("../../test_data/ICP/cloud_bin_1.pcd")
# 体素下采样
source = source_raw.voxel_down_sample(voxel_size=0.02)
target = target_raw.voxel_down_sample(voxel_size=0.02)
# 原始点云空间转换
trans = [[0.862, 0.011, -0.507, 0.0], [-0.139, 0.967, -0.215, 0.7],
         [0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]]
source.transform(trans)
# 翻转
flip_transform = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]
source.transform(flip_transform)
target.transform(flip_transform)

这部分读取两点云并对其进行下采样。源点云被有意地转换为不对齐。两个点云都翻转以获得更好的可视化效果。

初始化可视化类

   vis = o3d.visualization.Visualizer()
   vis.create_window()
   vis.add_geometry(source)
   vis.add_geometry(target)

这几行是创建了可视化器实例,打开了可视化窗口,并向可视化器添加了两个几何图形。

转换几何体并可视化它

for i in range(icp_iteration):
    # 配准
    reg_p2l = o3d.pipelines.registration.registration_icp(
        source, target, threshold, np.identity(4),
        o3d.pipelines.registration.TransformationEstimationPointToPlane(),
        o3d.pipelines.registration.ICPConvergenceCriteria(max_iteration=1))
    source.transform(reg_p2l.transformation)
    vis.update_geometry(source)
    vis.poll_events()
    vis.update_renderer()
    if save_image:
        vis.capture_screen_image("temp_%04d.jpg" % i)
vis.destroy_window()

这个脚本在每一次迭代中调用了registration_icp函数。需要注意的是它通过ICPConvergenceCriteria(max_iteration=1)标志来显示的只执行一次ICP配准。这是一种从ICP迭代中检索轻微更新姿态的技巧。在ICP之后,相应地变换源几何。

最后一点是这个脚本的核心。update_geometry通知 vis相关的几何已经被更新。最后,可视化器渲染新的一帧通过调用poll_eventsupdate_renderer来实现。在所有的循环结束之后,调用destroy_window来关闭窗口

结果如下图所示:

你可能感兴趣的:(open3d)