abreza commited on
Commit
247f6df
1 Parent(s): 13c73c9

support simulation json

Browse files
Files changed (1) hide show
  1. app.py +205 -10
app.py CHANGED
@@ -1,25 +1,220 @@
 
 
 
 
1
  import spaces
2
  import gradio as gr
3
  from gradio_rerun import Rerun
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
 
6
  @spaces.GPU
7
- def generate():
8
- pass
 
 
 
 
 
 
 
9
 
 
 
 
10
 
11
- def display_rrd(file):
12
- return None if file is None else file.name
 
 
13
 
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  with gr.Blocks() as demo:
 
 
 
 
 
16
  with gr.Row():
17
- file_input = gr.File(label="Upload RRD File")
18
- with gr.Row():
19
- viewer = Rerun(
20
- streaming=False,
 
 
 
 
21
  )
22
 
23
- file_input.change(display_rrd, inputs=[file_input], outputs=[viewer])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- demo.queue().launch(share=False)
 
 
1
+ import json
2
+ import numpy as np
3
+ import rerun as rr
4
+ from rerun.components import Material
5
  import spaces
6
  import gradio as gr
7
  from gradio_rerun import Rerun
8
+ from scipy.spatial.transform import Rotation
9
+ import tempfile
10
+ import os
11
+ from typing import Optional, Dict, Any, List, Tuple
12
+
13
+
14
+ def vector3_to_numpy(vec):
15
+ """Convert Vector3 dictionary to numpy array"""
16
+ return np.array([vec['x'], vec['y'], vec['z']])
17
+
18
+
19
+ def euler_to_quaternion(euler):
20
+ """Convert Euler angles dictionary to quaternion"""
21
+ return Rotation.from_euler('xyz', [euler['x'], euler['y'], euler['z']]).as_quat()
22
+
23
+
24
+ def create_subject_mesh(subject):
25
+ """Create a simple cube mesh for a subject"""
26
+ position = vector3_to_numpy(subject['position'])
27
+ size = vector3_to_numpy(subject['size'])
28
+
29
+ # Create cube vertices
30
+ vertices = np.array([
31
+ [-0.5, -0.5, -0.5], [0.5, -0.5, -0.5], [0.5, 0.5, -0.5], [-0.5, 0.5, -0.5],
32
+ [-0.5, -0.5, 0.5], [0.5, -0.5, 0.5], [0.5, 0.5, 0.5], [-0.5, 0.5, 0.5]
33
+ ]) * size.reshape(1, 3) + position.reshape(1, 3)
34
+
35
+ # Create cube faces
36
+ faces = np.array([
37
+ [0, 1, 2], [0, 2, 3], # front
38
+ [1, 5, 6], [1, 6, 2], # right
39
+ [5, 4, 7], [5, 7, 6], # back
40
+ [4, 0, 3], [4, 3, 7], # left
41
+ [3, 2, 6], [3, 6, 7], # top
42
+ [4, 5, 1], [4, 1, 0] # bottom
43
+ ])
44
+
45
+ return vertices, faces
46
+
47
+
48
+ def log_simulation(simulation_data: Dict[str, Any]) -> None:
49
+ """Log single simulation data to Rerun"""
50
+ rr.init("camera_simulation")
51
+
52
+ subjects = simulation_data['subjects']
53
+ camera_frames = simulation_data['cameraFrames']
54
+ instructions = simulation_data['instructions']
55
+
56
+ # Log simulation metadata
57
+ rr.log("metadata/instructions", rr.TextDocument(
58
+ "\n".join([
59
+ f"Instruction {i+1}:\n" +
60
+ f" Movement: {inst['cameraMovement']}\n" +
61
+ f" Easing: {inst['movementEasing']}\n" +
62
+ f" Frames: {inst['frameCount']}\n" +
63
+ f" Camera Angle: {inst.get('initialCameraAngle', 'N/A')}\n" +
64
+ f" Shot Type: {inst.get('initialShotType', 'N/A')}\n" +
65
+ f" Subject Index: {inst.get('subjectIndex', 'N/A')}"
66
+ for i, inst in enumerate(instructions)
67
+ ])
68
+ ), timeless=True)
69
+
70
+ # Set up world coordinate system
71
+ rr.log("world", rr.ViewCoordinates.RIGHT_HAND_Y_UP, timeless=True)
72
+
73
+ # Log subjects (as simple cubes)
74
+ for idx, subject in enumerate(subjects):
75
+ vertices, faces = create_subject_mesh(subject)
76
+ subject_color = [0.8, 0.2, 0.2, 1.0] if idx == simulation_data.get(
77
+ 'selectedSubject') else [0.8, 0.8, 0.8, 1.0]
78
+
79
+ rr.log(
80
+ f"world/subject_{idx}",
81
+ rr.Mesh3D(
82
+ vertex_positions=vertices,
83
+ indices=faces,
84
+ mesh_material=Material(albedo_factor=subject_color)
85
+ ),
86
+ timeless=True
87
+ )
88
+
89
+ # Log subject class
90
+ rr.log(f"world/subject_{idx}/class",
91
+ rr.TextDocument(subject['objectClass']),
92
+ timeless=True)
93
+
94
+ # Log camera trajectory
95
+ camera_positions = np.array(
96
+ [vector3_to_numpy(frame['position']) for frame in camera_frames])
97
+ rr.log(
98
+ "world/camera_trajectory",
99
+ rr.Points3D(camera_positions),
100
+ timeless=True
101
+ )
102
+
103
+ # Log camera positions over time
104
+ for frame_idx, camera_frame in enumerate(camera_frames):
105
+ rr.set_time_sequence("frame", frame_idx)
106
+
107
+ position = vector3_to_numpy(camera_frame['position'])
108
+ rotation_q = euler_to_quaternion(camera_frame['angle'])
109
+
110
+ # Log camera transform
111
+ rr.log(
112
+ "world/camera",
113
+ rr.Transform3D(
114
+ translation=position,
115
+ rotation=rr.Quaternion(xyzw=rotation_q)
116
+ )
117
+ )
118
+
119
+ # Log camera frustum
120
+ rr.log(
121
+ "world/camera/view",
122
+ rr.Pinhole(
123
+ focal_length=camera_frame['focalLength'],
124
+ width=1920,
125
+ height=1080
126
+ )
127
+ )
128
+
129
+
130
+ def load_simulation_data(file) -> Tuple[Optional[List[Dict[str, Any]]], Optional[List[str]]]:
131
+ """Load simulation data from JSON file and return simulations with their descriptions"""
132
+ if file is None:
133
+ return None, None
134
+
135
+ try:
136
+ json_data = json.load(open(file.name))
137
+ simulations = json_data['simulations']
138
+
139
+ # Create descriptions for each simulation
140
+ descriptions = [
141
+ f"Simulation {i}: {len(sim['subjects'])} subjects, {len(sim['instructions'])} instructions"
142
+ for i, sim in enumerate(simulations)
143
+ ]
144
+
145
+ return simulations, descriptions
146
+ except Exception as e:
147
+ print(f"Error loading simulation data: {str(e)}")
148
+ return None, None
149
 
150
 
151
  @spaces.GPU
152
+ def visualize_simulation(file, simulation_index: int) -> Optional[str]:
153
+ """Process selected simulation and create Rerun visualization"""
154
+ if file is None:
155
+ return None
156
+
157
+ try:
158
+ simulations, _ = load_simulation_data(file)
159
+ if simulations is None or simulation_index >= len(simulations):
160
+ return None
161
 
162
+ # Create temporary file for RRD
163
+ temp_dir = tempfile.mkdtemp()
164
+ rrd_path = os.path.join(temp_dir, "simulation.rrd")
165
 
166
+ # Log selected simulation
167
+ simulation = simulations[simulation_index]
168
+ log_simulation(simulation)
169
+ rr.save(rrd_path)
170
 
171
+ return rrd_path
172
 
173
+ except Exception as e:
174
+ print(f"Error processing simulation: {str(e)}")
175
+ return None
176
+
177
+
178
+ def update_simulation_dropdown(file):
179
+ """Update simulation dropdown when file is uploaded"""
180
+ _, descriptions = load_simulation_data(file)
181
+ return gr.Dropdown(choices=descriptions if descriptions else [], value=None)
182
+
183
+
184
+ # Create Gradio interface
185
  with gr.Blocks() as demo:
186
+ gr.Markdown("""
187
+ # Camera Simulation Visualizer
188
+ Upload a JSON file containing camera simulation data and select a simulation to visualize.
189
+ """)
190
+
191
  with gr.Row():
192
+ file_input = gr.File(
193
+ label="Upload Simulation JSON",
194
+ file_types=[".json"]
195
+ )
196
+ simulation_dropdown = gr.Dropdown(
197
+ label="Select Simulation",
198
+ choices=[],
199
+ type="index"
200
  )
201
 
202
+ with gr.Row():
203
+ viewer = Rerun(streaming=False)
204
+
205
+ # Update dropdown when file is uploaded
206
+ file_input.change(
207
+ update_simulation_dropdown,
208
+ inputs=[file_input],
209
+ outputs=[simulation_dropdown]
210
+ )
211
+
212
+ # Visualize selected simulation
213
+ simulation_dropdown.change(
214
+ visualize_simulation,
215
+ inputs=[file_input, simulation_dropdown],
216
+ outputs=[viewer]
217
+ )
218
 
219
+ if __name__ == "__main__":
220
+ demo.queue().launch(share=False)