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. Details see Visualization.

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

Mimic draw_geometries() with Visualizer class

13def custom_draw_geometry(pcd):
14    # The following code achieves the same effect as:
15    # o3d.visualization.draw_geometries([pcd])
16    vis = o3d.visualization.Visualizer()
17    vis.create_window()
18    vis.add_geometry(pcd)
19    vis.run()
20    vis.destroy_window()

This function produces exactly the same functionality of the convenient 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.

46def custom_draw_geometry_load_option(pcd):
47    vis = o3d.visualization.Visualizer()
48    vis.create_window()
49    vis.add_geometry(pcd)
50    vis.get_render_option().load_from_json("../../TestData/renderoption.json")
51    vis.run()
52    vis.destroy_window()

Outputs:

../../_images/normal.png

Change field of view

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

23def custom_draw_geometry_with_custom_fov(pcd, fov_step):
24    vis = o3d.visualization.Visualizer()
25    vis.create_window()
26    vis.add_geometry(pcd)
27    ctr = vis.get_view_control()
28    print("Field of view (before changing) %.2f" % ctr.get_field_of_view())
29    ctr.change_field_of_view(step=fov_step)
30    print("Field of view (after changing) %.2f" % ctr.get_field_of_view())
31    vis.run()
32    vis.destroy_window()

The field of view can be set as [5,90] degree. Note that change_field_of_view adds specified FoV on the current FoV. By default, visualizer has 60 degrees of FoV. 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 maximum allowable FoV, this will set FoV as 90 degrees.

../../_images/fov_90.png

The following code

custom_draw_geometry_with_custom_fov(pcd, -90.0)

will make FoV as 5 degrees, because 60 - 90 = -30 is smaller than 5 degrees.

../../_images/fov_5.png

Use callback functions

35def custom_draw_geometry_with_rotation(pcd):
36
37    def rotate_view(vis):
38        ctr = vis.get_view_control()
39        ctr.rotate(10.0, 0.0)
40        return False
41
42    o3d.visualization.draw_geometries_with_animation_callback([pcd],
43                                                              rotate_view)

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
55def custom_draw_geometry_with_key_callback(pcd):
56
57    def change_background_to_black(vis):
58        opt = vis.get_render_option()
59        opt.background_color = np.asarray([0, 0, 0])
60        return False
61
62    def load_render_option(vis):
63        vis.get_render_option().load_from_json(
64            "../../TestData/renderoption.json")
65        return False
66
67    def capture_depth(vis):
68        depth = vis.capture_depth_float_buffer()
69        plt.imshow(np.asarray(depth))
70        plt.show()
71        return False
72
73    def capture_image(vis):
74        image = vis.capture_screen_float_buffer()
75        plt.imshow(np.asarray(image))
76        plt.show()
77        return False
78
79    key_to_callback = {}
80    key_to_callback[ord("K")] = change_background_to_black
81    key_to_callback[ord("R")] = load_render_option
82    key_to_callback[ord(",")] = capture_depth
83    key_to_callback[ord(".")] = capture_image
84    o3d.visualization.draw_geometries_with_key_callbacks([pcd], key_to_callback)

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

 87def custom_draw_geometry_with_camera_trajectory(pcd):
 88    custom_draw_geometry_with_camera_trajectory.index = -1
 89    custom_draw_geometry_with_camera_trajectory.trajectory =\
 90            o3d.io.read_pinhole_camera_trajectory(
 91                    "../../TestData/camera_trajectory.json")
 92    custom_draw_geometry_with_camera_trajectory.vis = o3d.visualization.Visualizer(
 93    )
 94    if not os.path.exists("../../TestData/image/"):
 95        os.makedirs("../../TestData/image/")
 96    if not os.path.exists("../../TestData/depth/"):
 97        os.makedirs("../../TestData/depth/")
 98
 99    def move_forward(vis):
100        # This function is called within the o3d.visualization.Visualizer::run() loop
101        # The run loop calls the function, then re-render
102        # So the sequence in this function is to:
103        # 1. Capture frame
104        # 2. index++, check ending criteria
105        # 3. Set camera
106        # 4. (Re-render)
107        ctr = vis.get_view_control()
108        glb = custom_draw_geometry_with_camera_trajectory
109        if glb.index >= 0:
110            print("Capture image {:05d}".format(glb.index))
111            depth = vis.capture_depth_float_buffer(False)
112            image = vis.capture_screen_float_buffer(False)
113            plt.imsave("../../TestData/depth/{:05d}.png".format(glb.index),\
114                    np.asarray(depth), dpi = 1)
115            plt.imsave("../../TestData/image/{:05d}.png".format(glb.index),\
116                    np.asarray(image), dpi = 1)
117            #vis.capture_depth_image("depth/{:05d}.png".format(glb.index), False)
118            #vis.capture_screen_image("image/{:05d}.png".format(glb.index), False)
119        glb.index = glb.index + 1
120        if glb.index < len(glb.trajectory.parameters):
121            ctr.convert_from_pinhole_camera_parameters(
122                glb.trajectory.parameters[glb.index])
123        else:
124            custom_draw_geometry_with_camera_trajectory.vis.\
125                    register_animation_callback(None)
126        return False
127
128    vis = custom_draw_geometry_with_camera_trajectory.vis
129    vis.create_window()
130    vis.add_geometry(pcd)
131    vis.get_render_option().load_from_json("../../TestData/renderoption.json")
132    vis.register_animation_callback(move_forward)
133    vis.run()
134    vis.destroy_window()

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. They are saved in files.

The captured image sequence:

../../_images/image_small.gif

The captured depth sequence:

../../_images/depth_small.gif