Skip to content

Commit b9511cd

Browse files
committed
feat(vis): adding play data easily.
1 parent 835245c commit b9511cd

4 files changed

Lines changed: 303 additions & 0 deletions

File tree

assets/view/default.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"class_name" : "ViewTrajectory",
3+
"interval" : 29,
4+
"is_loop" : false,
5+
"trajectory" :
6+
[
7+
{
8+
"boundingbox_max" : [ 57.366597771035025, 35.139898721460504, 2.6755623623976064 ],
9+
"boundingbox_min" : [ -49.352191246579046, -77.135157738601819, -4.3324021597229345 ],
10+
"field_of_view" : 60.0,
11+
"front" : [ -0.82105186125140217, -0.24149769504190435, 0.51725497042083191 ],
12+
"lookat" : [ -2.3273983466720867, 0.11899999222967703, 0.84795569864897702 ],
13+
"up" : [ 0.50395332303428131, 0.11899522564227219, 0.85549470160664931 ],
14+
"zoom" : 0.13999999999999962
15+
}
16+
],
17+
"version_major" : 1,
18+
"version_minor" : 0
19+
}

scripts/py/data/play_data.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import sys, os
2+
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..' ))
3+
sys.path.append(BASE_DIR)
4+
5+
import numpy as np
6+
from pathlib import Path
7+
from tqdm import tqdm
8+
import open3d as o3d
9+
import fire, time
10+
from scipy.spatial.transform import Rotation as R
11+
12+
from utils import pcdpy3
13+
from utils.o3d_view import MyVisualizer
14+
15+
16+
def xyzqwxyz_to_matrix(xyzqwxyz: list):
17+
"""
18+
input: xyzqwxyz: [x, y, z, qx, qy, qz, qw] a list of 7 elements
19+
"""
20+
rotation = R.from_quat([xyzqwxyz[4], xyzqwxyz[5], xyzqwxyz[6], xyzqwxyz[3]]).as_matrix()
21+
pose = np.eye(4).astype(np.float64)
22+
pose[:3, :3] = rotation
23+
pose[:3, 3] = xyzqwxyz[:3]
24+
return pose
25+
26+
def inv_pose_matrix(pose):
27+
inv_pose = np.eye(4)
28+
inv_pose[:3, :3] = pose[:3, :3].T
29+
inv_pose[:3, 3] = -pose[:3, :3].T.dot(pose[:3, 3])
30+
return inv_pose
31+
32+
class DynamicMapData:
33+
def __init__(self, directory):
34+
super(DynamicMapData, self).__init__()
35+
self.scene_id = directory.split("/")[-1]
36+
self.directory = Path(directory) / "pcd"
37+
self.pcd_files = [os.path.join(self.directory, f) for f in sorted(os.listdir(self.directory)) if f.endswith('.pcd')]
38+
39+
def __len__(self):
40+
return len(self.pcd_files)
41+
42+
def __getitem__(self, index_):
43+
res_dict = {
44+
'scene_id': self.scene_id,
45+
'timestamp': self.pcd_files[index_].split("/")[-1].split(".")[0],
46+
}
47+
pcd_ = pcdpy3.PointCloud.from_path(self.pcd_files[index_])
48+
pcd_.xyzi2np()
49+
pc0 = pcd_.np_data[:,:3]
50+
pose0 = xyzqwxyz_to_matrix(list(pcd_.viewpoint))
51+
inv_pose0 = inv_pose_matrix(pose0)
52+
res_dict['pc'] = pc0 @ inv_pose0[:3, :3].T + inv_pose0[:3, 3]
53+
return res_dict
54+
55+
def vis(
56+
data_dir: str = "/home/kin/data/Dynamic_Papers_assets/Benchmark_data/00",
57+
view_file: str = os.path.abspath(BASE_DIR+"/../../assets/view/default.json"),
58+
point_size: int = 2,
59+
speed: int = 1,
60+
):
61+
o3d_vis = MyVisualizer(view_file=view_file, window_title="DynamicMap Benchmark Data Preview")
62+
opt = o3d_vis.vis.get_render_option()
63+
# opt.background_color = np.asarray([216, 216, 216]) / 255.0
64+
opt.background_color = np.asarray([80/255, 90/255, 110/255])
65+
# opt.background_color = np.asarray([1, 1, 1])
66+
opt.point_size = point_size
67+
dataset = DynamicMapData(data_dir)
68+
for data_id in (pbar := tqdm(range(0, len(dataset)))):
69+
data = dataset[data_id]
70+
now_scene_id = data['scene_id']
71+
pbar.set_description(f"id: {data_id}, scene_id: {now_scene_id}, timestamp: {data['timestamp']}")
72+
73+
pcd = o3d.geometry.PointCloud()
74+
pcd.points = o3d.utility.Vector3dVector(data['pc'][:, :3])
75+
o3d_vis.update([pcd, o3d.geometry.TriangleMesh.create_coordinate_frame(size=1)])
76+
time.sleep(0.05*1/speed)
77+
78+
if __name__ == "__main__":
79+
fire.Fire(vis)
80+
pass

scripts/py/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
tqdm
3+
numpy
4+
fire
5+
open3d

scripts/py/utils/o3d_view.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
'''
2+
# Created: 2023-1-26 16:38
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:
10+
11+
This file is for open3d view control set from view_file, which should be json
12+
1. use normal way to open any geometry and set view by mouse you want
13+
2. `CTRL+C` it will copy the view detail at this moment.
14+
3. `CTRL+V` to json file, you can create new one
15+
4. give the json file path
16+
17+
Check this part: http://www.open3d.org/docs/release/tutorial/visualization/visualization.html#Store-view-point
18+
19+
Test if you want by run this script: by press 'V' on keyboard, will set from json
20+
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)
25+
'''
26+
27+
import open3d as o3d
28+
import json
29+
import os, sys
30+
from typing import List, Callable
31+
from functools import partial
32+
33+
def hex_to_rgb(hex_color):
34+
hex_color = hex_color.lstrip("#")
35+
return tuple(int(hex_color[i:i + 2], 16) / 255.0 for i in (0, 2, 4))
36+
37+
color_map_hex = ['#a6cee3', '#de2d26', '#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99','#b15928',\
38+
'#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd','#ccebc5','#ffed6f']
39+
color_map = [hex_to_rgb(color) for color in color_map_hex]
40+
41+
class ViewControl:
42+
def __init__(self, vctrl: o3d.visualization.ViewControl, view_file=None):
43+
self.vctrl = vctrl
44+
self.params = None
45+
if view_file is not None:
46+
print(f"Init with view_file from: {view_file}")
47+
self.parse_file(view_file)
48+
self.set_param()
49+
else:
50+
print("Init without view_file")
51+
52+
def read_viewTfile(self, view_file):
53+
if view_file is None:
54+
return
55+
self.parse_file(view_file)
56+
self.set_param()
57+
58+
def save_viewTfile(self, view_file):
59+
return
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
65+
if(os.path.exists(view_file)):
66+
with open((view_file)) as user_file:
67+
file_contents = user_file.read()
68+
self.params = json.loads(file_contents)
69+
else:
70+
print(f"\033[91mDidn't find the file, please check it again: {view_file} \033[0m")
71+
print(f"NOTE: If you still have this error, please give the absulote path for view_file")
72+
sys.exit()
73+
74+
def set_param(self):
75+
self.vctrl.change_field_of_view(self.params['trajectory'][0]['field_of_view'])
76+
self.vctrl.set_front(self.params['trajectory'][0]['front'])
77+
self.vctrl.set_lookat(self.params['trajectory'][0]['lookat'])
78+
self.vctrl.set_up(self.params['trajectory'][0]['up'])
79+
self.vctrl.set_zoom(self.params['trajectory'][0]['zoom'])
80+
81+
class MyVisualizer:
82+
def __init__(self, view_file=None, window_title="Default"):
83+
self.params = None
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)
87+
self.view_file = view_file
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+
102+
def show(self, assets: List):
103+
self.vis.clear_geometries()
104+
105+
for asset in assets:
106+
self.vis.add_geometry(asset)
107+
self.o3d_vctrl.read_viewTfile(self.view_file)
108+
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)
146+
147+
if __name__ == "__main__":
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)
172+
sample_ply_data = o3d.data.PLYPointCloud()
173+
pcd = o3d.io.read_point_cloud(sample_ply_data.path)
174+
# 1. define
175+
viz = o3d.visualization.VisualizerWithKeyCallback()
176+
# 2. create
177+
viz.create_window(window_name="TEST ON Change View point through JSON, Press V Please")
178+
# 3. add geometry
179+
viz.add_geometry(pcd)
180+
# 4. get control !!! must step by step
181+
ctr = viz.get_view_control()
182+
183+
o3d_vctrl = ViewControl(ctr)
184+
185+
def set_view(viz):
186+
#Your update routine
187+
o3d_vctrl.read_viewTfile(view_json_file)
188+
viz.update_renderer()
189+
viz.poll_events()
190+
viz.run()
191+
192+
viz.register_key_callback(ord('V'), set_view)
193+
viz.run()
194+
viz.destroy_window()
195+
print("\033[92mAll o3d_view codes run successfully, Close now..\033[0m See you!")
196+
197+
# or:
198+
# viz = MyVisualizer(view_file, window_title="Check Pose")
199+
# viz.show([*pcds, *draw_tfs])

0 commit comments

Comments
 (0)