Spaces:
				
			
			
	
			
			
		Running
		
			on 
			
			Zero
	
	
	
			
			
	
	
	
	
		
		
		Running
		
			on 
			
			Zero
	| """Record3D visualizer | |
| """ | |
| import time | |
| from decord import VideoReader, cpu | |
| import numpy as np | |
| import tyro | |
| import viser | |
| import viser.extras | |
| import viser.transforms as tf | |
| from tqdm.auto import tqdm | |
| def main( | |
| data_path: str, | |
| vid_name: str, | |
| downsample_factor: int = 8, | |
| max_frames: int = 100, | |
| share: bool = False, | |
| point_size=0.01, | |
| ) -> None: | |
| server = viser.ViserServer() | |
| if share: | |
| server.request_share_url() | |
| print("Loading frames!") | |
| dis_path = data_path + "/" + vid_name + ".npz" | |
| vid_path = data_path + "/" + vid_name + "_input.mp4" | |
| disp_map = np.load(dis_path)["depth"][:, :, :] | |
| T = disp_map.shape[0] | |
| H = disp_map.shape[1] | |
| W = disp_map.shape[2] | |
| disp_max = disp_map.max() | |
| disp_min = disp_map.min() | |
| disp_map = (disp_map - disp_min) / (disp_max - disp_min) | |
| vr = VideoReader(vid_path, ctx=cpu(0)) | |
| vid = vr[:].asnumpy()[:, 0:H, 0:W] | |
| fps = vr.get_avg_fps() | |
| num_frames = min(max_frames, T) | |
| # Add playback UI. | |
| with server.gui.add_folder("Playback"): | |
| gui_timestep = server.gui.add_slider( | |
| "Timestep", | |
| min=0, | |
| max=num_frames - 1, | |
| step=1, | |
| initial_value=0, | |
| disabled=True, | |
| ) | |
| gui_next_frame = server.gui.add_button("Next Frame", disabled=True) | |
| gui_prev_frame = server.gui.add_button("Prev Frame", disabled=True) | |
| gui_playing = server.gui.add_checkbox("Playing", True) | |
| gui_framerate = server.gui.add_slider( | |
| "FPS", min=1, max=60, step=0.1, initial_value=fps | |
| ) | |
| gui_framerate_options = server.gui.add_button_group( | |
| "FPS options", ("10", "20", "30", "60") | |
| ) | |
| # Frame step buttons. | |
| def _(_) -> None: | |
| gui_timestep.value = (gui_timestep.value + 1) % num_frames | |
| def _(_) -> None: | |
| gui_timestep.value = (gui_timestep.value - 1) % num_frames | |
| # Disable frame controls when we're playing. | |
| def _(_) -> None: | |
| gui_timestep.disabled = gui_playing.value | |
| gui_next_frame.disabled = gui_playing.value | |
| gui_prev_frame.disabled = gui_playing.value | |
| # Set the framerate when we click one of the options. | |
| def _(_) -> None: | |
| gui_framerate.value = int(gui_framerate_options.value) | |
| prev_timestep = gui_timestep.value | |
| # Toggle frame visibility when the timestep slider changes. | |
| def _(_) -> None: | |
| nonlocal prev_timestep | |
| current_timestep = gui_timestep.value | |
| with server.atomic(): | |
| frame_nodes[current_timestep].visible = True | |
| frame_nodes[prev_timestep].visible = False | |
| prev_timestep = current_timestep | |
| server.flush() # Optional! | |
| # Load in frames. | |
| server.scene.add_frame( | |
| "/frames", | |
| wxyz=tf.SO3.exp(np.array([0.0, 0.0, 0.0])).wxyz, | |
| position=(0, 0, 0), | |
| show_axes=False, | |
| ) | |
| frame_nodes: list[viser.FrameHandle] = [] | |
| for i in tqdm(range(num_frames)): | |
| # Add base frame. | |
| frame_nodes.append(server.scene.add_frame(f"/frames/t{i}", show_axes=False)) | |
| position_image = np.where(np.zeros([H, W]) == 0) | |
| v = np.array(position_image[0]) | |
| u = np.array(position_image[1]) | |
| d = disp_map[i, v, u] | |
| zc = 1.0 / (d + 0.1) | |
| # zc = 1.0 / (d + 1e-8) | |
| xc = zc * (u - (W / 2.0)) / (W / 2.0) | |
| yc = zc * (v - (H / 2.0)) / (H / 2.0) | |
| zc -= 4 # disp_max * 0.2 | |
| points = np.stack((xc, yc, zc), axis=1) | |
| colors = vid[i, v, u] | |
| points = points[::downsample_factor] | |
| colors = colors[::downsample_factor] | |
| # Place the point cloud in the frame. | |
| server.scene.add_point_cloud( | |
| name=f"/frames/t{i}/point_cloud", | |
| points=points, | |
| colors=colors, | |
| point_size=point_size, # 0.007, | |
| point_shape="rounded", | |
| ) | |
| # Hide all but the current frame. | |
| for i, frame_node in enumerate(frame_nodes): | |
| frame_node.visible = i == gui_timestep.value | |
| # Playback update loop. | |
| prev_timestep = gui_timestep.value | |
| while True: | |
| if gui_playing.value: | |
| gui_timestep.value = (gui_timestep.value + 1) % num_frames | |
| time.sleep(1.0 / gui_framerate.value) | |
| if __name__ == "__main__": | |
| tyro.cli( | |
| main( | |
| # dir path of saved rgb.mp4 and disp.npz, modify it to your own dir | |
| data_path="./demo_output", | |
| # sample name, modify it to your own sample name | |
| vid_name="example_01", | |
| # downsample factor of dense pcd | |
| downsample_factor=8, | |
| # point cloud size | |
| point_size=0.007, | |
| ) | |
| ) | |
