11'''
22# Created: 2023-1-26 16:38
3- # Copyright (C) 2022-now, RPL, KTH Royal Institute of Technology
4- # Author: Kin ZHANG (https://kin-zhang.github.io/)
5-
6- # This work is licensed under the terms of the MIT license.
7- # For a copy, see <https://opensource.org/licenses/MIT>.
3+ # Updated: 2024-04-15 12:06
4+ # Copyright (C) 2023-now, RPL, KTH Royal Institute of Technology
5+ # Author: Qingwen ZHANG (https://kin-zhang.github.io/)
6+ #
7+ # code gits: https://gist.github.com/Kin-Zhang/77e8aa77a998f1a4f7495357843f24ef
8+ #
9+ # Description as follows:
810
911This file is for open3d view control set from view_file, which should be json
10121. use normal way to open any geometry and set view by mouse you want
11132. `CTRL+C` it will copy the view detail at this moment.
12143. `CTRL+V` to json file, you can create new one
13154. give the json file path
14- Check this part: http://www.open3d.org/docs/release/tutorial/visualization/visualization.html#Store-view-point
1516
16- Test if you want by run this script
17+ Check this part: http://www.open3d.org/docs/release/tutorial/visualization/visualization.html#Store-view-point
1718
18- Then press 'V' on keyboard, will set from json
19+ Test if you want by run this script: by press 'V' on keyboard, will set from json
1920
21+ # CHANGELOG:
22+ # 2024-04-15 12:06(Qingwen): show a example json text. add hex_to_rgb, color_map_hex, color_map (for color points if needed)
23+ # 2024-01-27 0:41(Qingwen): update MyVisualizer class, reference from kiss-icp
24+ [python/kiss-icp/tools/visualizer.py](https://github.com/PRBonn/kiss-icp/blob/main/python/kiss_icp/tools/visualizer.py)
2025'''
2126
2227import open3d as o3d
2328import json
2429import os , sys
25- from typing import List
26- BASE_DIR = os .path .abspath (os .path .join ( os .path .dirname ( __file__ ), '..' ))
27- sys .path .append (BASE_DIR )
30+ from typing import List , Callable
31+ from functools import partial
2832
2933def hex_to_rgb (hex_color ):
3034 hex_color = hex_color .lstrip ("#" )
3135 return tuple (int (hex_color [i :i + 2 ], 16 ) / 255.0 for i in (0 , 2 , 4 ))
3236
33- color_map_hex = ['#a6cee3' ,'#1f78b4' ,'#b2df8a' ,'#33a02c' ,'#fb9a99' ,'#e31a1c' ,'#fdbf6f' ,'#ff7f00' ,'#cab2d6' ,'#6a3d9a' ,'#ffff99' ,'#b15928' ,\
37+ color_map_hex = ['#a6cee3' , '#de2d26' , '#1f78b4' ,'#b2df8a' ,'#33a02c' ,'#fb9a99' ,'#e31a1c' ,'#fdbf6f' ,'#ff7f00' ,'#cab2d6' ,'#6a3d9a' ,'#ffff99' ,'#b15928' ,\
3438 '#8dd3c7' ,'#ffffb3' ,'#bebada' ,'#fb8072' ,'#80b1d3' ,'#fdb462' ,'#b3de69' ,'#fccde5' ,'#d9d9d9' ,'#bc80bd' ,'#ccebc5' ,'#ffed6f' ]
3539color_map = [hex_to_rgb (color ) for color in color_map_hex ]
3640
@@ -40,16 +44,24 @@ def __init__(self, vctrl: o3d.visualization.ViewControl, view_file=None):
4044 self .params = None
4145 if view_file is not None :
4246 print (f"Init with view_file from: { view_file } " )
43- self .parase_file (view_file )
47+ self .parse_file (view_file )
4448 self .set_param ()
4549 else :
4650 print ("Init without view_file" )
51+
4752 def read_viewTfile (self , view_file ):
48- self .parase_file (view_file )
53+ if view_file is None :
54+ return
55+ self .parse_file (view_file )
4956 self .set_param ()
57+
5058 def save_viewTfile (self , view_file ):
5159 return
52- def parase_file (self , view_file ):
60+
61+ def parse_file (self , view_file ):
62+ if view_file is None :
63+ print (f"\033 [91mNo specific view file. Skip to setup viewpoint in open3d. \033 [0m" )
64+ return
5365 if (os .path .exists (view_file )):
5466 with open ((view_file )) as user_file :
5567 file_contents = user_file .read ()
@@ -69,26 +81,94 @@ def set_param(self):
6981class MyVisualizer :
7082 def __init__ (self , view_file = None , window_title = "Default" ):
7183 self .params = None
72- self .viz = o3d .visualization .VisualizerWithKeyCallback ()
73- self .viz .create_window (window_name = window_title )
74- self .o3d_vctrl = ViewControl (self .viz .get_view_control (), view_file = view_file )
84+ self .vis = o3d .visualization .VisualizerWithKeyCallback ()
85+ self .vis .create_window (window_name = window_title )
86+ self .o3d_vctrl = ViewControl (self .vis .get_view_control (), view_file = view_file )
7587 self .view_file = view_file
76-
88+
89+ self .block_vis = True
90+ self .play_crun = False
91+ self .reset_bounding_box = True
92+ print (
93+ f"\n { window_title .capitalize ()} initialized. Press:\n "
94+ "\t [SPACE] to pause/start\n "
95+ "\t [ESC] to exit\n "
96+ "\t [N] to step\n "
97+ )
98+ self ._register_key_callback (["Ā" , "Q" , "\x1b " ], self ._quit )
99+ self ._register_key_callback ([" " ], self ._start_stop )
100+ self ._register_key_callback (["N" ], self ._next_frame )
101+
77102 def show (self , assets : List ):
78- self .viz .clear_geometries ()
103+ self .vis .clear_geometries ()
79104
80105 for asset in assets :
81- self .viz .add_geometry (asset )
82- if self .view_file is not None :
106+ self .vis .add_geometry (asset )
83107 self .o3d_vctrl .read_viewTfile (self .view_file )
84108
85- self .viz .update_renderer ()
86- self .viz .poll_events ()
87- self .viz .run ()
88- self .viz .destroy_window ()
109+ self .vis .update_renderer ()
110+ self .vis .poll_events ()
111+ self .vis .run ()
112+ self .vis .destroy_window ()
113+
114+ def update (self , assets : List , clear : bool = True ):
115+ if clear :
116+ self .vis .clear_geometries ()
117+
118+ for asset in assets :
119+ self .vis .add_geometry (asset , reset_bounding_box = False )
120+ self .vis .update_geometry (asset )
121+
122+ if self .reset_bounding_box :
123+ self .vis .reset_view_point (True )
124+ if self .view_file is not None :
125+ self .o3d_vctrl .read_viewTfile (self .view_file )
126+ self .reset_bounding_box = False
127+
128+ self .vis .update_renderer ()
129+ while self .block_vis :
130+ self .vis .poll_events ()
131+ if self .play_crun :
132+ break
133+ self .block_vis = not self .block_vis
134+
135+ def _register_key_callback (self , keys : List , callback : Callable ):
136+ for key in keys :
137+ self .vis .register_key_callback (ord (str (key )), partial (callback ))
138+ def _next_frame (self , vis ):
139+ self .block_vis = not self .block_vis
140+ def _start_stop (self , vis ):
141+ self .play_crun = not self .play_crun
142+ def _quit (self , vis ):
143+ print ("Destroying Visualizer. Thanks for using ^v^." )
144+ vis .destroy_window ()
145+ os ._exit (0 )
89146
90147if __name__ == "__main__" :
91- view_json_file = f"{ BASE_DIR } /data/o3d_view/default_test.json"
148+ json_content = """{
149+ "class_name" : "ViewTrajectory",
150+ "interval" : 29,
151+ "is_loop" : false,
152+ "trajectory" :
153+ [
154+ {
155+ "boundingbox_max" : [ 3.9660897254943848, 2.427476167678833, 2.55859375 ],
156+ "boundingbox_min" : [ 0.55859375, 0.83203125, 0.56663715839385986 ],
157+ "field_of_view" : 60.0,
158+ "front" : [ 0.27236083595988803, -0.25567329763523589, -0.92760484038816615 ],
159+ "lookat" : [ 2.4114965637897101, 1.8070288935660688, 1.5662280268112718 ],
160+ "up" : [ -0.072779625398507866, -0.96676294585190281, 0.24509698622097265 ],
161+ "zoom" : 0.47999999999999976
162+ }
163+ ],
164+ "version_major" : 1,
165+ "version_minor" : 0
166+ }
167+ """
168+ # write to json file
169+ view_json_file = "view.json"
170+ with open (view_json_file , 'w' ) as f :
171+ f .write (json_content )
92172 sample_ply_data = o3d .data .PLYPointCloud ()
93173 pcd = o3d .io .read_point_cloud (sample_ply_data .path )
94174 # 1. define
0 commit comments