Raffael-Kultyshev commited on
Commit
7b3b386
·
1 Parent(s): 99d25e7

Revert to working minimal version

Browse files
Files changed (1) hide show
  1. app.py +9 -373
app.py CHANGED
@@ -1,380 +1,16 @@
1
- import json
2
- import os
3
- import html
4
- from pathlib import Path
5
- from typing import Dict, List
6
- from functools import lru_cache
7
-
8
- import gradio as gr
9
- import plotly.graph_objects as go
10
- import plotly.io as pio
11
-
12
- def get_data_dir():
13
- """Get data directory path."""
14
- try:
15
- return Path(__file__).parent / "data"
16
- except:
17
- return Path("data")
18
-
19
- METRIC_LABELS = {
20
- "x_cm": "X (cm)",
21
- "y_cm": "Y (cm)",
22
- "z_cm": "Z (cm)",
23
- "yaw_deg": "Yaw (°)",
24
- "pitch_deg": "Pitch (°)",
25
- "roll_deg": "Roll (°)",
26
- }
27
-
28
- PLOT_GRID = [
29
- ["x_cm", "y_cm", "z_cm"],
30
- ["yaw_deg", "pitch_deg", "roll_deg"],
31
- ]
32
-
33
- PLOT_ORDER = [metric for row in PLOT_GRID for metric in row]
34
-
35
- CUSTOM_CSS = """
36
- :root, .gradio-container, body {
37
- background-color: #050a18 !important;
38
- color: #f8fafc !important;
39
- font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
40
- }
41
- .side-panel {
42
- background: #0f172a;
43
- padding: 20px;
44
- border-radius: 18px;
45
- border: 1px solid #1f2b47;
46
- min-height: 100%;
47
- }
48
- .stats-card ul {
49
- list-style: none;
50
- padding: 0;
51
- margin: 0;
52
- font-size: 0.92rem;
53
- }
54
- .stats-card li {
55
- margin-bottom: 10px;
56
- color: #e2e8f0;
57
- }
58
- .stats-card span {
59
- display: inline-block;
60
- margin-right: 6px;
61
- color: #7dd3fc;
62
- }
63
- .main-panel {
64
- padding-top: 8px;
65
- }
66
- .instruction-card {
67
- background: #0f172a;
68
- padding: 18px 20px;
69
- border-radius: 18px;
70
- border: 1px solid #1f2b47;
71
- }
72
- .instruction-label {
73
- font-size: 0.75rem;
74
- letter-spacing: 0.12em;
75
- text-transform: uppercase;
76
- color: #94a3b8;
77
- margin-bottom: 10px;
78
- }
79
- .instruction-text {
80
- font-size: 1.1rem;
81
- line-height: 1.5;
82
- }
83
- .video-card {
84
- background: #0f172a;
85
- border: 1px solid #1f2b47;
86
- border-radius: 18px;
87
- padding: 18px 20px;
88
- margin-top: 18px;
89
- }
90
- .video-title {
91
- font-size: 0.78rem;
92
- text-transform: uppercase;
93
- letter-spacing: 0.18em;
94
- color: #94a3b8;
95
- margin-bottom: 8px;
96
- }
97
- .video-panel video {
98
- border-radius: 12px;
99
- border: 1px solid #1f2b47;
100
- background: #030712;
101
- }
102
- .download-button button {
103
- border-radius: 999px;
104
- border: 1px solid #334155;
105
- background: #1e293b;
106
- color: #f8fafc;
107
- font-size: 0.85rem;
108
- padding: 8px 24px;
109
- }
110
- .download-button button:hover {
111
- border-color: #67e8f9;
112
- color: #67e8f9;
113
- }
114
- .plots-wrap {
115
- margin-top: 18px;
116
- }
117
- .plots-wrap .gr-row {
118
- gap: 16px;
119
- }
120
- .plot-html {
121
- background: #111a2c;
122
- border-radius: 12px;
123
- padding: 10px;
124
- border: 1px solid #1f2b47;
125
- min-height: 320px;
126
- }
127
- .plot-html iframe {
128
- width: 100%;
129
- height: 300px;
130
- border: none;
131
- }
132
  """
133
-
134
-
135
- @lru_cache(maxsize=1)
136
- def load_data():
137
- """Load all data files."""
138
- data_dir = get_data_dir()
139
- metadata_path = data_dir / "metadata.json"
140
- end_effector_path = data_dir / "end_effector.json"
141
- hands_2d_path = data_dir / "hands_2d.json"
142
-
143
- metadata = {}
144
- end_effector = {}
145
- hands_2d = {}
146
-
147
- if metadata_path.exists():
148
- with open(metadata_path, 'r') as f:
149
- metadata = json.load(f)
150
-
151
- if end_effector_path.exists():
152
- with open(end_effector_path, 'r') as f:
153
- end_effector = json.load(f)
154
-
155
- if hands_2d_path.exists():
156
- with open(hands_2d_path, 'r') as f:
157
- hands_2d = json.load(f)
158
-
159
- return metadata, end_effector, hands_2d
160
-
161
-
162
- def build_state_dataframe(metadata: dict, end_effector: dict, hand: str = "left"):
163
- """Build state dataframe from JSON data."""
164
- fps = metadata.get('fps', 60)
165
-
166
- try:
167
- frame_keys = sorted([int(k) for k in end_effector.keys() if str(k).isdigit()])
168
- except:
169
- frame_keys = []
170
-
171
- timestamps = []
172
- state_data = {
173
- 'wrist_x_cm': [],
174
- 'wrist_y_cm': [],
175
- 'wrist_z_cm': [],
176
- 'wrist_yaw_deg': [],
177
- 'wrist_pitch_deg': [],
178
- 'wrist_roll_deg': [],
179
- }
180
-
181
- for frame_idx in frame_keys:
182
- frame_key = str(frame_idx)
183
- t = frame_idx / fps
184
- timestamps.append(t)
185
-
186
- ee_data = end_effector.get(frame_key, {}) or {}
187
- hand_data = ee_data.get(hand + "_hand")
188
-
189
- if hand_data and isinstance(hand_data, dict):
190
- pose = hand_data.get('pose_6dof')
191
- if pose and len(pose) >= 6:
192
- state_data['wrist_x_cm'].append(pose[0] * 100) # m to cm
193
- state_data['wrist_y_cm'].append(pose[1] * 100)
194
- state_data['wrist_z_cm'].append(pose[2] * 100)
195
- state_data['wrist_roll_deg'].append(pose[3] * 57.3) # rad to deg
196
- state_data['wrist_pitch_deg'].append(pose[4] * 57.3)
197
- state_data['wrist_yaw_deg'].append(pose[5] * 57.3)
198
- else:
199
- for k in state_data:
200
- state_data[k].append(None)
201
- else:
202
- for k in state_data:
203
- state_data[k].append(None)
204
-
205
- return timestamps, state_data
206
-
207
-
208
- def build_plot_fig(timestamps: List[float], state_data: Dict, metric: str) -> go.Figure:
209
- """Build Plotly figure for a metric."""
210
- col_name = f"wrist_{metric}"
211
- if col_name not in state_data:
212
- return go.Figure()
213
-
214
- fig = go.Figure()
215
- fig.add_trace(
216
- go.Scatter(
217
- x=timestamps,
218
- y=state_data[col_name],
219
- mode="lines",
220
- name="Wrist",
221
- )
222
- )
223
- fig.update_layout(
224
- margin=dict(l=20, r=20, t=30, b=20),
225
- height=250,
226
- template="plotly_dark",
227
- legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
228
- xaxis_title="Time (s)",
229
- yaxis_title=METRIC_LABELS[metric],
230
- )
231
- fig.update_xaxes(showgrid=True, gridwidth=0.5, gridcolor="rgba(255,255,255,0.1)")
232
- fig.update_yaxes(showgrid=True, gridwidth=0.5, gridcolor="rgba(255,255,255,0.1)")
233
- return fig
234
-
235
-
236
- def build_plot_html(timestamps: List[float], state_data: Dict, metric: str) -> str:
237
- """Build Plotly HTML for a metric."""
238
- fig = build_plot_fig(timestamps, state_data, metric)
239
- return pio.to_html(fig, include_plotlyjs="cdn", full_html=False)
240
-
241
-
242
- def format_instruction_html(text: str) -> str:
243
- safe_text = html.escape(text)
244
- return (
245
- '<div class="instruction-card">'
246
- '<p class="instruction-label">Language Instruction</p>'
247
- f'<p class="instruction-text">{safe_text}</p>'
248
- "</div>"
249
- )
250
-
251
-
252
- def build_interface():
253
- """Build Gradio interface."""
254
- try:
255
- metadata, end_effector, hands_2d = load_data()
256
- except Exception as e:
257
- print(f"Error loading data: {e}")
258
- metadata, end_effector, hands_2d = {}, {}, {}
259
-
260
- total_frames = len(metadata.get('poses', []))
261
- fps = metadata.get('fps', 60)
262
- hand_detection_rate = len(hands_2d) / max(1, total_frames) * 100 if total_frames > 0 else 0
263
-
264
- left_poses = sum(1 for f in end_effector.values() if f and isinstance(f, dict) and f.get('left_hand'))
265
- right_poses = sum(1 for f in end_effector.values() if f and isinstance(f, dict) and f.get('right_hand'))
266
-
267
- video_path = get_data_dir() / "video.mp4"
268
-
269
- # Build data for left hand
270
- try:
271
- left_timestamps, left_state = build_state_dataframe(metadata, end_effector, "left")
272
- left_figs = {metric: build_plot_html(left_timestamps, left_state, metric) for metric in METRIC_LABELS.keys()}
273
- except Exception as e:
274
- print(f"Error building left hand plots: {e}")
275
- left_figs = {metric: "<p>Plot unavailable</p>" for metric in METRIC_LABELS.keys()}
276
-
277
- # Build data for right hand
278
- try:
279
- right_timestamps, right_state = build_state_dataframe(metadata, end_effector, "right")
280
- right_figs = {metric: build_plot_html(right_timestamps, right_state, metric) for metric in METRIC_LABELS.keys()}
281
- except Exception as e:
282
- print(f"Error building right hand plots: {e}")
283
- right_figs = {metric: "<p>Plot unavailable</p>" for metric in METRIC_LABELS.keys()}
284
-
285
- stats_html = f"""
286
- <div class="stats-card">
287
- <ul>
288
- <li><span>Number of samples/frames:</span> {total_frames:,}</li>
289
- <li><span>Hand detection rate:</span> {hand_detection_rate:.1f}%</li>
290
- <li><span>Left hand poses:</span> {left_poses}</li>
291
- <li><span>Right hand poses:</span> {right_poses}</li>
292
- <li><span>Frames per second:</span> {fps:.1f}</li>
293
- </ul>
294
- </div>
295
  """
296
-
297
- instruction_text = "LiDAR-based egocentric hand tracking for robot training data"
298
-
299
- theme = gr.themes.Soft(
300
- primary_hue="cyan", secondary_hue="blue", neutral_hue="slate"
301
- ).set(
302
- body_background_fill="#0c1424",
303
- body_text_color="#f8fafc",
304
- block_background_fill="#111a2c",
305
- block_title_text_color="#f8fafc",
306
- input_background_fill="#151f33",
307
- border_color_primary="#1f2b47",
308
- shadow_drop="none",
309
- )
310
-
311
- with gr.Blocks(theme=theme, css=CUSTOM_CSS) as demo:
312
- gr.Markdown("# 🤖 Dynamic Intelligence - Human Demo Visualizer")
313
- gr.Markdown(
314
- "Egocentric hand tracking dataset for humanoid robot training. "
315
- "Pipeline: iPhone LiDAR → MediaPipe → 6DoF End-Effector → Robot Training Data"
316
- )
317
-
318
- with gr.Row(equal_height=True):
319
- with gr.Column(scale=1, min_width=260, elem_classes=["side-panel"]):
320
- gr.HTML(stats_html)
321
- with gr.Column(scale=2, min_width=640, elem_classes=["main-panel"]):
322
- instruction_box = gr.HTML(
323
- format_instruction_html(instruction_text),
324
- label="Language Instruction",
325
- )
326
- with gr.Column(elem_classes=["video-card"]):
327
- gr.HTML('<div class="video-title">RGB Video</div>')
328
- video = gr.Video(
329
- height=360,
330
- value=str(video_path) if video_path.exists() else None,
331
- elem_classes=["video-panel"],
332
- show_label=False,
333
- show_download_button=False,
334
- )
335
- download_button = gr.DownloadButton(
336
- label="Download",
337
- value=str(video_path) if video_path.exists() else None,
338
- elem_classes=["download-button"],
339
- )
340
-
341
- plot_outputs_left = []
342
- gr.Markdown("### Left Hand Trajectories", elem_classes=["plots-title"])
343
- with gr.Column(elem_classes=["plots-wrap"]):
344
- for row in PLOT_GRID:
345
- with gr.Row():
346
- for metric in row:
347
- plot = gr.HTML(value=left_figs[metric], elem_classes=["plot-html"])
348
- plot_outputs_left.append(plot)
349
-
350
- plot_outputs_right = []
351
- gr.Markdown("### Right Hand Trajectories", elem_classes=["plots-title"])
352
- with gr.Column(elem_classes=["plots-wrap"]):
353
- for row in PLOT_GRID:
354
- with gr.Row():
355
- for metric in row:
356
- plot = gr.HTML(value=right_figs[metric], elem_classes=["plot-html"])
357
- plot_outputs_right.append(plot)
358
-
359
- return demo
360
-
361
 
362
- def main():
363
- demo = build_interface()
364
- demo.queue().launch(show_api=False)
365
 
 
 
 
366
 
367
  if __name__ == "__main__":
368
- main()
369
 
370
- # For Gradio Spaces
371
- try:
372
- demo = build_interface()
373
- except Exception as e:
374
- print(f"Error building interface: {e}")
375
- import traceback
376
- traceback.print_exc()
377
- # Fallback minimal demo
378
- with gr.Blocks() as demo:
379
- gr.Markdown("# Error loading visualizer")
380
- gr.Markdown(f"Error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Minimal test version
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  """
4
+ import gradio as gr
5
+ from pathlib import Path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ DATA_DIR = Path(__file__).parent / "data"
8
+ video_path = DATA_DIR / "video.mp4"
 
9
 
10
+ with gr.Blocks() as demo:
11
+ gr.Markdown("# Test")
12
+ gr.Video(value=str(video_path) if video_path.exists() else None)
13
 
14
  if __name__ == "__main__":
15
+ demo.launch()
16