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