当需要快速可视化静态几何形状时,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_events
和update_renderer
来实现。在所有的循环结束之后,调用destroy_window
来关闭窗口
结果如下图所示: