Customized visualization

The usage of Open3D convenient visualization functions draw_geometries and draw_geometries_with_custom_animation is straightforward. Everything can be done with the GUI. Press h inside the visualizer window to see helper information. For more details, see /tutorial/visualization/visualization.ipynb.

This tutorial focuses on more advanced functionalities to customize the behavior of the visualizer window. Please refer to examples/python/visualization/customized_visualization.py to try the following examples.

Mimic draw_geometries() with Visualizer class

37    # The following code achieves the same effect as:
38    # o3d.visualization.draw_geometries([pcd])
39    vis = o3d.visualization.Visualizer()
40    vis.create_window()
41    vis.add_geometry(pcd)
42    vis.run()
43    vis.destroy_window()
44

This function produces exactly the same functionality as the convenience function draw_geometries.

../../_images/custom.png

Class Visualizer has a couple of variables such as a ViewControl and a RenderOption. The following function reads a predefined RenderOption stored in a json file.

70    vis = o3d.visualization.Visualizer()
71    vis.create_window()
72    vis.add_geometry(pcd)
73    vis.get_render_option().load_from_json(render_option_path)
74    vis.run()
75    vis.destroy_window()
76

Outputs:

../../_images/normal.png

Change field of view

To change field of view of the camera, it is first necessary to get an instance of the visualizer control. To modify the field of view, use change_field_of_view.

47    vis = o3d.visualization.Visualizer()
48    vis.create_window()
49    vis.add_geometry(pcd)
50    ctr = vis.get_view_control()
51    print("Field of view (before changing) %.2f" % ctr.get_field_of_view())
52    ctr.change_field_of_view(step=fov_step)
53    print("Field of view (after changing) %.2f" % ctr.get_field_of_view())
54    vis.run()
55    vis.destroy_window()
56

The field of view (FoV) can be set to a degree in the range [5,90]. Note that change_field_of_view adds the specified FoV to the current FoV. By default, the visualizer has an FoV of 60 degrees. Calling the following code

custom_draw_geometry_with_custom_fov(pcd, 90.0)

will add the specified 90 degrees to the default 60 degrees. As it exceeds the maximum allowable FoV, the FoV is set to 90 degrees.

../../_images/fov_90.png

The following code

custom_draw_geometry_with_custom_fov(pcd, -90.0)

will set FoV to 5 degrees, because 60 - 90 = -30 is less than 5 degrees.

../../_images/fov_5.png

Callback functions

59
60    def rotate_view(vis):
61        ctr = vis.get_view_control()
62        ctr.rotate(10.0, 0.0)
63        return False
64
65    o3d.visualization.draw_geometries_with_animation_callback([pcd],
66                                                              rotate_view)
67

Function draw_geometries_with_animation_callback registers a Python callback function rotate_view as the idle function of the main loop. It rotates the view along the x-axis whenever the visualizer is idle. This defines an animation behavior.

../../_images/rotate_small.gif
 79
 80    def change_background_to_black(vis):
 81        opt = vis.get_render_option()
 82        opt.background_color = np.asarray([0, 0, 0])
 83        return False
 84
 85    def load_render_option(vis):
 86        vis.get_render_option().load_from_json(render_option_path)
 87        return False
 88
 89    def capture_depth(vis):
 90        depth = vis.capture_depth_float_buffer()
 91        plt.imshow(np.asarray(depth))
 92        plt.show()
 93        return False
 94
 95    def capture_image(vis):
 96        image = vis.capture_screen_float_buffer()
 97        plt.imshow(np.asarray(image))
 98        plt.show()
 99        return False
100
101    key_to_callback = {}
102    key_to_callback[ord("K")] = change_background_to_black
103    key_to_callback[ord("R")] = load_render_option
104    key_to_callback[ord(",")] = capture_depth
105    key_to_callback[ord(".")] = capture_image
106    o3d.visualization.draw_geometries_with_key_callbacks([pcd], key_to_callback)
107
108

Callback functions can also be registered upon key press event. This script registered four keys. For example, pressing k changes the background color to black.

../../_images/key_k.png

Capture images in a customized animation

109    custom_draw_geometry_with_camera_trajectory.index = -1
110    custom_draw_geometry_with_camera_trajectory.trajectory =\
111        o3d.io.read_pinhole_camera_trajectory(camera_trajectory_path)
112    custom_draw_geometry_with_camera_trajectory.vis = o3d.visualization.Visualizer(
113    )
114    image_path = os.path.join(test_data_path, 'image')
115    if not os.path.exists(image_path):
116        os.makedirs(image_path)
117    depth_path = os.path.join(test_data_path, 'depth')
118    if not os.path.exists(depth_path):
119        os.makedirs(depth_path)
120
121    def move_forward(vis):
122        # This function is called within the o3d.visualization.Visualizer::run() loop
123        # The run loop calls the function, then re-render
124        # So the sequence in this function is to:
125        # 1. Capture frame
126        # 2. index++, check ending criteria
127        # 3. Set camera
128        # 4. (Re-render)
129        ctr = vis.get_view_control()
130        glb = custom_draw_geometry_with_camera_trajectory
131        if glb.index >= 0:
132            print("Capture image {:05d}".format(glb.index))
133            depth = vis.capture_depth_float_buffer(False)
134            image = vis.capture_screen_float_buffer(False)
135            plt.imsave(os.path.join(depth_path, '{:05d}.png'.format(glb.index)),
136                       np.asarray(depth),
137                       dpi=1)
138            plt.imsave(os.path.join(image_path, '{:05d}.png'.format(glb.index)),
139                       np.asarray(image),
140                       dpi=1)
141            # vis.capture_depth_image("depth/{:05d}.png".format(glb.index), False)
142            # vis.capture_screen_image("image/{:05d}.png".format(glb.index), False)
143        glb.index = glb.index + 1
144        if glb.index < len(glb.trajectory.parameters):
145            ctr.convert_from_pinhole_camera_parameters(
146                glb.trajectory.parameters[glb.index], allow_arbitrary=True)
147        else:
148            custom_draw_geometry_with_camera_trajectory.vis.\
149                register_animation_callback(None)
150        return False
151
152    vis = custom_draw_geometry_with_camera_trajectory.vis
153    vis.create_window()
154    vis.add_geometry(pcd)
155    vis.get_render_option().load_from_json(render_option_path)
156    vis.register_animation_callback(move_forward)
157    vis.run()
158    vis.destroy_window()
159
160

This function reads a camera trajectory, then defines an animation function move_forward to travel through the camera trajectory. In this animation function, both color image and depth image are captured using Visualizer.capture_depth_float_buffer and Visualizer.capture_screen_float_buffer respectively. The images are saved as png files.

The captured image sequence:

../../_images/image_small.gif

The captured depth sequence:

../../_images/depth_small.gif