非阻塞可视化
draw_geometries() 是一个很有用的函数,可以快速浏览静态几何体。然而,这个函数会一直阻塞进程,直到可视化窗口被关闭。当几何体被更新、并且需要在不关闭窗口的情况下进行可视化时,这种做法并不理想。本教程介绍了一个自定义渲染循环(rendering loop)的示例。
回顾 draw_geometries
Section titled “回顾 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注意,绑定几何体和渲染场景都是代价较高的操作,因此它们以惰性(lazy)方式执行。有两个标志位分别控制它们。函数 update_geometry 和 update_renderer 用于设置这些标志位。在重新绑定 / 重新渲染之后,这些标志位会再次被清除。
这个渲染循环可以很方便地进行自定义。例如,可以按下面的方式构造一个自定义循环,用来可视化 ICP 配准过程:
vis = Visualizer()vis.create_window()vis.add_geometry(geometry)for i in range(icp_iteration): # do ICP single iteration # transform geometry using ICP vis.update_geometry(geometry) vis.poll_events() vis.update_renderer()实现这一思路的完整脚本如下。
import open3d as o3dimport numpy as np
def prepare_data(): pcd_data = o3d.data.DemoICPPointClouds() source_raw = o3d.io.read_point_cloud(pcd_data.paths[0]) target_raw = o3d.io.read_point_cloud(pcd_data.paths[1]) 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) return source, target
def demo_non_blocking_visualization(): o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)
source, target = prepare_data() 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()
o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Info)
if __name__ == '__main__': demo_non_blocking_visualization()下面几节对这段脚本进行讲解。
准备示例数据
Section titled “准备示例数据”def prepare_data(): pcd_data = o3d.data.DemoICPPointClouds() source_raw = o3d.io.read_point_cloud(pcd_data.paths[0]) target_raw = o3d.io.read_point_cloud(pcd_data.paths[1]) 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) return source, target这一部分读取两个点云并对它们进行降采样。源点云被故意做了一个变换以制造错位(misalignment)。两个点云都被翻转了一下,以便获得更好的可视化效果。
初始化 Visualizer 类
Section titled “初始化 Visualizer 类”def demo_non_blocking_visualization(): o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)
source, target = prepare_data() vis = o3d.visualization.Visualizer() vis.create_window() vis.add_geometry(source) vis.add_geometry(target) threshold = 0.05 icp_iteration = 100 save_image = False这些行创建一个可视化器类的实例,打开可视化窗口,并把两个几何体添加到可视化器中。
变换几何体并进行可视化
Section titled “变换几何体并进行可视化” 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 来渲染一个新帧。for 循环的所有迭代结束后,destroy_window 关闭窗口。
结果如下图所示。