Commit Β·
bb0f4d9
1
Parent(s): 82a1045
Replace bottom Examples + top stats card with one demo button
Browse files- Remove the 'Run summary' tile card at the top of the output
column; the four output panels (vis video, charts, canvas,
raw JSON accordion) are enough.
- Remove the bottom 'Demo video' card with three example rows;
sat awkwardly below the layout.
- Add a single 'Load demo video' button right under the upload
widget in the Input card. One click fills every control with
a curated preset (sbs Β· combined Β· log Β· GOP=dynamic Β· 1024
patches) and loads the bundled demo clip.
- Button is hidden when examples/demo_codec_heatmap.mp4 is
missing so the UI degrades cleanly on stripped checkouts.
- process() is back to its 4-output signature.
app.py
CHANGED
|
@@ -40,6 +40,26 @@ import numpy as np
|
|
| 40 |
|
| 41 |
PATCH_CHOICES = [14, 16, 28]
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
def smart_resize(frame: np.ndarray, max_pixels: int, factor: int) -> np.ndarray:
|
| 45 |
"""Resize so h,w are multiples of `factor` and h*w <= max_pixels."""
|
|
@@ -452,41 +472,6 @@ def pack_canvas(
|
|
| 452 |
return canvas, n
|
| 453 |
|
| 454 |
|
| 455 |
-
def render_stats_html(
|
| 456 |
-
*, total_selected: int, n_groups: int, gop_label: str,
|
| 457 |
-
n_sampled: int, total_frames: int, canvas_resolution: str,
|
| 458 |
-
saliency_signal: str, score_log_scale: bool, elapsed_sec: float,
|
| 459 |
-
grid_label: str, n_uniform: int, codec: str = "",
|
| 460 |
-
) -> str:
|
| 461 |
-
"""Format the run's headline numbers as a grid of brand-colored tiles."""
|
| 462 |
-
sig = saliency_signal + (" Β· log" if score_log_scale else "")
|
| 463 |
-
gop_disp = (
|
| 464 |
-
gop_label if gop_label in ("global", "dynamic") else f"GOP={gop_label}"
|
| 465 |
-
)
|
| 466 |
-
cells = [
|
| 467 |
-
("selected patches", f"{total_selected:,}"),
|
| 468 |
-
("groups", f"{n_groups} Β· {gop_disp}"),
|
| 469 |
-
("sampled frames", f"{n_sampled} / {total_frames}"),
|
| 470 |
-
("canvas", canvas_resolution),
|
| 471 |
-
("uniform baseline", f"{n_uniform} frame{'s' if n_uniform != 1 else ''}"),
|
| 472 |
-
("saliency", sig),
|
| 473 |
-
("patch grid / frame", grid_label),
|
| 474 |
-
("elapsed", f"{elapsed_sec:.2f}s"),
|
| 475 |
-
]
|
| 476 |
-
if codec:
|
| 477 |
-
cells.insert(2, ("input codec", codec))
|
| 478 |
-
parts = ['<div class="ovc-stats">']
|
| 479 |
-
for label, value in cells:
|
| 480 |
-
parts.append(
|
| 481 |
-
f'<div class="ovc-stat">'
|
| 482 |
-
f'<div class="value">{value}</div>'
|
| 483 |
-
f'<div class="label">{label}</div>'
|
| 484 |
-
f'</div>'
|
| 485 |
-
)
|
| 486 |
-
parts.append("</div>")
|
| 487 |
-
return "".join(parts)
|
| 488 |
-
|
| 489 |
-
|
| 490 |
def make_charts(
|
| 491 |
grids: List[np.ndarray],
|
| 492 |
masks: List[np.ndarray],
|
|
@@ -609,7 +594,7 @@ def process(
|
|
| 609 |
progress=gr.Progress(track_tqdm=False),
|
| 610 |
):
|
| 611 |
if not video_path:
|
| 612 |
-
return None, None, "Please upload a video.", None
|
| 613 |
|
| 614 |
t0 = time.time()
|
| 615 |
progress(0.05, desc="Reading metadata")
|
|
@@ -619,7 +604,7 @@ def process(
|
|
| 619 |
return None, None, json.dumps(
|
| 620 |
{"error": "Could not read frame count.", "metadata": meta},
|
| 621 |
indent=2, ensure_ascii=False,
|
| 622 |
-
), None
|
| 623 |
|
| 624 |
progress(0.10, desc="Sampling frames")
|
| 625 |
fps = float(meta.get("fps") or 0.0)
|
|
@@ -649,7 +634,7 @@ def process(
|
|
| 649 |
return None, None, json.dumps(
|
| 650 |
{"error": "Failed to decode frames.", "metadata": meta},
|
| 651 |
indent=2, ensure_ascii=False,
|
| 652 |
-
), None
|
| 653 |
|
| 654 |
progress(0.25, desc="smart_resize")
|
| 655 |
resized = [smart_resize(f, int(max_pixels), int(patch_size)) for f in raw]
|
|
@@ -764,27 +749,11 @@ def process(
|
|
| 764 |
groups=groups, gop_label=gop_resolved,
|
| 765 |
)
|
| 766 |
|
| 767 |
-
stats_html = render_stats_html(
|
| 768 |
-
total_selected=int(actual_selected),
|
| 769 |
-
n_groups=len(groups),
|
| 770 |
-
gop_label=gop_resolved,
|
| 771 |
-
n_sampled=len(fids),
|
| 772 |
-
total_frames=int(total),
|
| 773 |
-
canvas_resolution=f"{canvas.shape[1]}Γ{canvas.shape[0]}",
|
| 774 |
-
saliency_signal=saliency_signal,
|
| 775 |
-
score_log_scale=bool(score_log_scale),
|
| 776 |
-
elapsed_sec=round(time.time() - t0, 2),
|
| 777 |
-
grid_label=f"{hb}Γ{wb}",
|
| 778 |
-
n_uniform=int(n_uniform),
|
| 779 |
-
codec=str(meta.get("codec") or ""),
|
| 780 |
-
)
|
| 781 |
-
|
| 782 |
progress(1.0, desc="Done")
|
| 783 |
return (
|
| 784 |
vis_path, canvas_path,
|
| 785 |
json.dumps(info, indent=2, ensure_ascii=False),
|
| 786 |
chart_fig,
|
| 787 |
-
stats_html,
|
| 788 |
)
|
| 789 |
|
| 790 |
|
|
@@ -1203,20 +1172,6 @@ FLOW_HTML = """
|
|
| 1203 |
</div>
|
| 1204 |
"""
|
| 1205 |
|
| 1206 |
-
EMPTY_STATS = (
|
| 1207 |
-
'<div class="ovc-stats">'
|
| 1208 |
-
'<div class="ovc-stat" style="grid-column: 1 / -1; text-align:center; '
|
| 1209 |
-
'background: linear-gradient(135deg, rgba(79,70,229,0.04), rgba(6,182,212,0.03));">'
|
| 1210 |
-
'<div class="value" style="font-size:1.05rem; font-weight:600; '
|
| 1211 |
-
'-webkit-background-clip: initial; background-clip: initial; color: #64748b;">'
|
| 1212 |
-
'Hit <span style="background: var(--ovc-grad); -webkit-background-clip:text; '
|
| 1213 |
-
'background-clip:text; color:transparent;">Run pipeline</span>, '
|
| 1214 |
-
'or pick a row in <i>Demo video</i> below.'
|
| 1215 |
-
'</div>'
|
| 1216 |
-
'<div class="label" style="margin-top:6px;">key numbers will land here</div>'
|
| 1217 |
-
'</div>'
|
| 1218 |
-
'</div>'
|
| 1219 |
-
)
|
| 1220 |
|
| 1221 |
|
| 1222 |
with gr.Blocks(**_BLOCK_KW) as demo:
|
|
@@ -1229,6 +1184,11 @@ with gr.Blocks(**_BLOCK_KW) as demo:
|
|
| 1229 |
with gr.Group(elem_classes="ovc-card"):
|
| 1230 |
gr.Markdown("### Input")
|
| 1231 |
video_in = gr.Video(label="Video", sources=["upload"], height=240)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1232 |
|
| 1233 |
with gr.Group(elem_classes="ovc-card"):
|
| 1234 |
gr.Markdown("### Pipeline")
|
|
@@ -1321,10 +1281,6 @@ with gr.Blocks(**_BLOCK_KW) as demo:
|
|
| 1321 |
|
| 1322 |
# βββ Outputs (wide column) βββββββββββββββββββββββββββββββββββββββ
|
| 1323 |
with gr.Column(scale=6, min_width=420):
|
| 1324 |
-
with gr.Group(elem_classes="ovc-card ovc-card-primary"):
|
| 1325 |
-
gr.Markdown("### Run summary")
|
| 1326 |
-
stats_out = gr.HTML(value=EMPTY_STATS)
|
| 1327 |
-
|
| 1328 |
with gr.Group(elem_classes="ovc-card ovc-card-primary"):
|
| 1329 |
gr.Markdown("### Patch selection visualization")
|
| 1330 |
vis_out = gr.Video(
|
|
@@ -1363,46 +1319,6 @@ with gr.Blocks(**_BLOCK_KW) as demo:
|
|
| 1363 |
label="", language="json", lines=18,
|
| 1364 |
)
|
| 1365 |
|
| 1366 |
-
DEMO_VIDEO = os.path.join(
|
| 1367 |
-
os.path.dirname(os.path.abspath(__file__)),
|
| 1368 |
-
"examples", "demo_codec_heatmap.mp4",
|
| 1369 |
-
)
|
| 1370 |
-
if os.path.exists(DEMO_VIDEO):
|
| 1371 |
-
with gr.Group(elem_classes="ovc-card"):
|
| 1372 |
-
gr.Markdown("### Demo video")
|
| 1373 |
-
gr.Markdown(
|
| 1374 |
-
"<small>Click any row below to load the bundled clip with a "
|
| 1375 |
-
"preset configuration. Or upload your own video at the top.</small>"
|
| 1376 |
-
)
|
| 1377 |
-
gr.Examples(
|
| 1378 |
-
examples=[
|
| 1379 |
-
[
|
| 1380 |
-
DEMO_VIDEO, 16, 14, 1024, 150000,
|
| 1381 |
-
"selection", 0.55, 0.0, 0.0,
|
| 1382 |
-
"gradient", False, 99.0, 0.55, "global",
|
| 1383 |
-
],
|
| 1384 |
-
[
|
| 1385 |
-
DEMO_VIDEO, 16, 14, 1024, 150000,
|
| 1386 |
-
"heatmap", 0.55, 0.0, 0.0,
|
| 1387 |
-
"combined", True, 96.0, 0.55, "dynamic",
|
| 1388 |
-
],
|
| 1389 |
-
[
|
| 1390 |
-
DEMO_VIDEO, 24, 14, 2048, 150000,
|
| 1391 |
-
"sbs", 0.55, 0.0, 0.0,
|
| 1392 |
-
"combined", True, 95.0, 0.55, "8",
|
| 1393 |
-
],
|
| 1394 |
-
],
|
| 1395 |
-
inputs=[
|
| 1396 |
-
video_in, sample_frames, patch_size, top_k, max_pixels,
|
| 1397 |
-
viz_mode, heatmap_alpha, start_sec, end_sec,
|
| 1398 |
-
saliency_signal, score_log_scale, bitcost_pct,
|
| 1399 |
-
fade_strength, gop,
|
| 1400 |
-
],
|
| 1401 |
-
label="",
|
| 1402 |
-
cache_examples=False,
|
| 1403 |
-
examples_per_page=5,
|
| 1404 |
-
)
|
| 1405 |
-
|
| 1406 |
gr.HTML(
|
| 1407 |
'<div id="ovc-footer">'
|
| 1408 |
'<b>OneVision Encoder</b> Β· codec-style patch saliency demo Β· '
|
|
@@ -1432,7 +1348,18 @@ with gr.Blocks(**_BLOCK_KW) as demo:
|
|
| 1432 |
saliency_signal, score_log_scale, bitcost_pct, fade_strength,
|
| 1433 |
gop,
|
| 1434 |
],
|
| 1435 |
-
outputs=[vis_out, canvas_out, info_out, chart_out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1436 |
)
|
| 1437 |
|
| 1438 |
|
|
|
|
| 40 |
|
| 41 |
PATCH_CHOICES = [14, 16, 28]
|
| 42 |
|
| 43 |
+
DEMO_VIDEO_PATH = os.path.join(
|
| 44 |
+
os.path.dirname(os.path.abspath(__file__)),
|
| 45 |
+
"examples", "demo_codec_heatmap.mp4",
|
| 46 |
+
)
|
| 47 |
+
DEMO_PRESET = (
|
| 48 |
+
DEMO_VIDEO_PATH, # video_in
|
| 49 |
+
16, # sample_frames
|
| 50 |
+
14, # patch_size
|
| 51 |
+
1024, # total_patches
|
| 52 |
+
150000, # max_pixels
|
| 53 |
+
"sbs", # viz_mode
|
| 54 |
+
0.55, # heatmap_alpha
|
| 55 |
+
0.0, 0.0, # start_sec, end_sec
|
| 56 |
+
"combined", # saliency_signal
|
| 57 |
+
True, # score_log_scale
|
| 58 |
+
96.0, # bitcost_pct
|
| 59 |
+
0.55, # fade_strength
|
| 60 |
+
"dynamic", # gop
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
|
| 64 |
def smart_resize(frame: np.ndarray, max_pixels: int, factor: int) -> np.ndarray:
|
| 65 |
"""Resize so h,w are multiples of `factor` and h*w <= max_pixels."""
|
|
|
|
| 472 |
return canvas, n
|
| 473 |
|
| 474 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
def make_charts(
|
| 476 |
grids: List[np.ndarray],
|
| 477 |
masks: List[np.ndarray],
|
|
|
|
| 594 |
progress=gr.Progress(track_tqdm=False),
|
| 595 |
):
|
| 596 |
if not video_path:
|
| 597 |
+
return None, None, "Please upload a video.", None
|
| 598 |
|
| 599 |
t0 = time.time()
|
| 600 |
progress(0.05, desc="Reading metadata")
|
|
|
|
| 604 |
return None, None, json.dumps(
|
| 605 |
{"error": "Could not read frame count.", "metadata": meta},
|
| 606 |
indent=2, ensure_ascii=False,
|
| 607 |
+
), None
|
| 608 |
|
| 609 |
progress(0.10, desc="Sampling frames")
|
| 610 |
fps = float(meta.get("fps") or 0.0)
|
|
|
|
| 634 |
return None, None, json.dumps(
|
| 635 |
{"error": "Failed to decode frames.", "metadata": meta},
|
| 636 |
indent=2, ensure_ascii=False,
|
| 637 |
+
), None
|
| 638 |
|
| 639 |
progress(0.25, desc="smart_resize")
|
| 640 |
resized = [smart_resize(f, int(max_pixels), int(patch_size)) for f in raw]
|
|
|
|
| 749 |
groups=groups, gop_label=gop_resolved,
|
| 750 |
)
|
| 751 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 752 |
progress(1.0, desc="Done")
|
| 753 |
return (
|
| 754 |
vis_path, canvas_path,
|
| 755 |
json.dumps(info, indent=2, ensure_ascii=False),
|
| 756 |
chart_fig,
|
|
|
|
| 757 |
)
|
| 758 |
|
| 759 |
|
|
|
|
| 1172 |
</div>
|
| 1173 |
"""
|
| 1174 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1175 |
|
| 1176 |
|
| 1177 |
with gr.Blocks(**_BLOCK_KW) as demo:
|
|
|
|
| 1184 |
with gr.Group(elem_classes="ovc-card"):
|
| 1185 |
gr.Markdown("### Input")
|
| 1186 |
video_in = gr.Video(label="Video", sources=["upload"], height=240)
|
| 1187 |
+
with gr.Row(elem_classes="ovc-preset"):
|
| 1188 |
+
btn_demo = gr.Button(
|
| 1189 |
+
"Load demo video", size="sm",
|
| 1190 |
+
visible=os.path.exists(DEMO_VIDEO_PATH),
|
| 1191 |
+
)
|
| 1192 |
|
| 1193 |
with gr.Group(elem_classes="ovc-card"):
|
| 1194 |
gr.Markdown("### Pipeline")
|
|
|
|
| 1281 |
|
| 1282 |
# βββ Outputs (wide column) βββββββββββββββββββββββββββββββββββββββ
|
| 1283 |
with gr.Column(scale=6, min_width=420):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1284 |
with gr.Group(elem_classes="ovc-card ovc-card-primary"):
|
| 1285 |
gr.Markdown("### Patch selection visualization")
|
| 1286 |
vis_out = gr.Video(
|
|
|
|
| 1319 |
label="", language="json", lines=18,
|
| 1320 |
)
|
| 1321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1322 |
gr.HTML(
|
| 1323 |
'<div id="ovc-footer">'
|
| 1324 |
'<b>OneVision Encoder</b> Β· codec-style patch saliency demo Β· '
|
|
|
|
| 1348 |
saliency_signal, score_log_scale, bitcost_pct, fade_strength,
|
| 1349 |
gop,
|
| 1350 |
],
|
| 1351 |
+
outputs=[vis_out, canvas_out, info_out, chart_out],
|
| 1352 |
+
)
|
| 1353 |
+
|
| 1354 |
+
btn_demo.click(
|
| 1355 |
+
lambda: DEMO_PRESET,
|
| 1356 |
+
inputs=None,
|
| 1357 |
+
outputs=[
|
| 1358 |
+
video_in, sample_frames, patch_size, top_k, max_pixels,
|
| 1359 |
+
viz_mode, heatmap_alpha, start_sec, end_sec,
|
| 1360 |
+
saliency_signal, score_log_scale, bitcost_pct, fade_strength,
|
| 1361 |
+
gop,
|
| 1362 |
+
],
|
| 1363 |
)
|
| 1364 |
|
| 1365 |
|