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
.

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(
74 os.path.join(test_data_path, 'renderoption.json'))
75 vis.run()
76 vis.destroy_window()
Outputs:

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.

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.

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.

79def custom_draw_geometry_with_key_callback(pcd):
80
81 def change_background_to_black(vis):
82 opt = vis.get_render_option()
83 opt.background_color = np.asarray([0, 0, 0])
84 return False
85
86 def load_render_option(vis):
87 vis.get_render_option().load_from_json(
88 os.path.join(test_data_path, 'renderoption.json'))
89 return False
90
91 def capture_depth(vis):
92 depth = vis.capture_depth_float_buffer()
93 plt.imshow(np.asarray(depth))
94 plt.show()
95 return False
96
97 def capture_image(vis):
98 image = vis.capture_screen_float_buffer()
99 plt.imshow(np.asarray(image))
100 plt.show()
101 return False
102
103 key_to_callback = {}
104 key_to_callback[ord("K")] = change_background_to_black
105 key_to_callback[ord("R")] = load_render_option
106 key_to_callback[ord(",")] = capture_depth
107 key_to_callback[ord(".")] = capture_image
108 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.

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

The captured depth sequence:
