Spaces:
Running
on
Zero
Running
on
Zero
X-GAO
commited on
add dynamic point cloud visulization
Browse files- visualization_pcd.py +166 -0
visualization_pcd.py
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Record3D visualizer
|
2 |
+
"""
|
3 |
+
import os
|
4 |
+
import time
|
5 |
+
from pathlib import Path
|
6 |
+
from decord import VideoReader, cpu
|
7 |
+
|
8 |
+
import numpy as np
|
9 |
+
import tyro
|
10 |
+
import viser
|
11 |
+
import viser.extras
|
12 |
+
import viser.transforms as tf
|
13 |
+
from tqdm.auto import tqdm
|
14 |
+
|
15 |
+
|
16 |
+
def main(
|
17 |
+
data_path: str = "/apdcephfs_cq10/share_1290939/vg_share/reynli/cache/video_demo/video_depth",
|
18 |
+
vid_name: str = "01_dog",
|
19 |
+
downsample_factor: int = 8,
|
20 |
+
max_frames: int = 100,
|
21 |
+
share: bool = False,
|
22 |
+
point_size = 0.01
|
23 |
+
) -> None:
|
24 |
+
|
25 |
+
server = viser.ViserServer()
|
26 |
+
if share:
|
27 |
+
server.request_share_url()
|
28 |
+
|
29 |
+
print("Loading frames!")
|
30 |
+
dis_path = data_path + '/' + vid_name + '.npz'
|
31 |
+
vid_path = data_path + '/' + vid_name + '_rgb.mp4'
|
32 |
+
# vid_path = data_path + '/' + vid_name + '.mp4'
|
33 |
+
|
34 |
+
disp_map = np.load(dis_path)['depth'][:, :, :]
|
35 |
+
T = disp_map.shape[0]
|
36 |
+
H = disp_map.shape[1]
|
37 |
+
W = disp_map.shape[2]
|
38 |
+
|
39 |
+
disp_max = disp_map.max()
|
40 |
+
disp_min = disp_map.min()
|
41 |
+
disp_map = (disp_map - disp_min) / (disp_max - disp_min)
|
42 |
+
|
43 |
+
vr = VideoReader(vid_path, ctx=cpu(0))
|
44 |
+
vid = vr[:].asnumpy()[:, 0:H, 0:W]
|
45 |
+
fps = vr.get_avg_fps()
|
46 |
+
num_frames = min(max_frames, T)
|
47 |
+
|
48 |
+
# Add playback UI.
|
49 |
+
with server.gui.add_folder("Playback"):
|
50 |
+
gui_timestep = server.gui.add_slider(
|
51 |
+
"Timestep",
|
52 |
+
min=0,
|
53 |
+
max=num_frames - 1,
|
54 |
+
step=1,
|
55 |
+
initial_value=0,
|
56 |
+
disabled=True,
|
57 |
+
)
|
58 |
+
gui_next_frame = server.gui.add_button("Next Frame", disabled=True)
|
59 |
+
gui_prev_frame = server.gui.add_button("Prev Frame", disabled=True)
|
60 |
+
gui_playing = server.gui.add_checkbox("Playing", True)
|
61 |
+
gui_framerate = server.gui.add_slider(
|
62 |
+
"FPS", min=1, max=60, step=0.1, initial_value=fps
|
63 |
+
)
|
64 |
+
gui_framerate_options = server.gui.add_button_group(
|
65 |
+
"FPS options", ("10", "20", "30", "60")
|
66 |
+
)
|
67 |
+
|
68 |
+
# Frame step buttons.
|
69 |
+
@gui_next_frame.on_click
|
70 |
+
def _(_) -> None:
|
71 |
+
gui_timestep.value = (gui_timestep.value + 1) % num_frames
|
72 |
+
|
73 |
+
@gui_prev_frame.on_click
|
74 |
+
def _(_) -> None:
|
75 |
+
gui_timestep.value = (gui_timestep.value - 1) % num_frames
|
76 |
+
|
77 |
+
# Disable frame controls when we're playing.
|
78 |
+
@gui_playing.on_update
|
79 |
+
def _(_) -> None:
|
80 |
+
gui_timestep.disabled = gui_playing.value
|
81 |
+
gui_next_frame.disabled = gui_playing.value
|
82 |
+
gui_prev_frame.disabled = gui_playing.value
|
83 |
+
|
84 |
+
# Set the framerate when we click one of the options.
|
85 |
+
@gui_framerate_options.on_click
|
86 |
+
def _(_) -> None:
|
87 |
+
gui_framerate.value = int(gui_framerate_options.value)
|
88 |
+
|
89 |
+
prev_timestep = gui_timestep.value
|
90 |
+
|
91 |
+
# Toggle frame visibility when the timestep slider changes.
|
92 |
+
@gui_timestep.on_update
|
93 |
+
def _(_) -> None:
|
94 |
+
nonlocal prev_timestep
|
95 |
+
current_timestep = gui_timestep.value
|
96 |
+
with server.atomic():
|
97 |
+
frame_nodes[current_timestep].visible = True
|
98 |
+
frame_nodes[prev_timestep].visible = False
|
99 |
+
prev_timestep = current_timestep
|
100 |
+
server.flush() # Optional!
|
101 |
+
|
102 |
+
# Load in frames.
|
103 |
+
server.scene.add_frame(
|
104 |
+
"/frames",
|
105 |
+
wxyz=tf.SO3.exp(np.array([0.0, 0.0, 0.0])).wxyz,
|
106 |
+
position=(0, 0, 0),
|
107 |
+
show_axes=False,
|
108 |
+
)
|
109 |
+
frame_nodes: list[viser.FrameHandle] = []
|
110 |
+
for i in tqdm(range(num_frames)):
|
111 |
+
|
112 |
+
# Add base frame.
|
113 |
+
frame_nodes.append(server.scene.add_frame(f"/frames/t{i}", show_axes=False))
|
114 |
+
|
115 |
+
position_image = np.where(np.zeros([H, W]) == 0)
|
116 |
+
v = np.array(position_image[0])
|
117 |
+
u = np.array(position_image[1])
|
118 |
+
d = disp_map[i, v, u]
|
119 |
+
|
120 |
+
zc = 1.0 / (d + 0.1)
|
121 |
+
# zc = 1.0 / (d + 1e-8)
|
122 |
+
|
123 |
+
xc = zc * (u - (W / 2.0)) / (W/2.)
|
124 |
+
yc = zc * (v - (H / 2.0)) / (H/2.)
|
125 |
+
|
126 |
+
zc -= 4 # disp_max * 0.2
|
127 |
+
|
128 |
+
points = np.stack((xc, yc, zc), axis=1)
|
129 |
+
colors = vid[i, v, u]
|
130 |
+
|
131 |
+
points = points[::downsample_factor]
|
132 |
+
colors = colors[::downsample_factor]
|
133 |
+
|
134 |
+
# Place the point cloud in the frame.
|
135 |
+
server.scene.add_point_cloud(
|
136 |
+
name=f"/frames/t{i}/point_cloud",
|
137 |
+
points=points,
|
138 |
+
colors=colors,
|
139 |
+
point_size=point_size,#0.007,
|
140 |
+
point_shape="rounded",
|
141 |
+
)
|
142 |
+
|
143 |
+
# Hide all but the current frame.
|
144 |
+
for i, frame_node in enumerate(frame_nodes):
|
145 |
+
frame_node.visible = i == gui_timestep.value
|
146 |
+
|
147 |
+
# Playback update loop.
|
148 |
+
prev_timestep = gui_timestep.value
|
149 |
+
while True:
|
150 |
+
if gui_playing.value:
|
151 |
+
gui_timestep.value = (gui_timestep.value + 1) % num_frames
|
152 |
+
|
153 |
+
time.sleep(1.0 / gui_framerate.value)
|
154 |
+
|
155 |
+
|
156 |
+
if __name__ == "__main__":
|
157 |
+
tyro.cli(main(
|
158 |
+
# dir path of saved rgb.mp4 and disp.npz, modify it to your own dir
|
159 |
+
data_path="outputs/results_open_world/",
|
160 |
+
# sample name, modify it to your own sample name
|
161 |
+
vid_name="wukong",
|
162 |
+
# downsample factor of dense pcd
|
163 |
+
downsample_factor=8,
|
164 |
+
# point cloud size
|
165 |
+
point_size=0.007
|
166 |
+
))
|