FeilongTang commited on
Commit
b5599e7
Β·
1 Parent(s): e3d4cd8

Chart: cumulative patch count over time, codec vs uniform

Browse files

Replaces the two side-by-side bar panels with a single overlaid step
chart so the contrast is immediate.

X = time (s)
Y = cumulative count of selected patches
Indigo (solid) = codec saliency, rises in steps at each sampled
frame by however many patches that frame contributed.
Cyan (dashed) = uniform-sampling baseline at the same budget,
stays flat between its evenly-spaced uniform frames
and jumps by grid_size on each.
Dotted gray line at y = total patches budget for reference; the
codec curve hits it (== budget), the uniform curve typically falls
short because budget rarely divides evenly into grid_size.
Light dashed verticals show the GOP group boundaries.

Section header retitled 'Cumulative patches over time' with a short
legend in the subtitle explaining the two curves.

Files changed (1) hide show
  1. app.py +81 -70
app.py CHANGED
@@ -581,19 +581,16 @@ def make_charts(
581
  groups: List[Tuple[int, int]] = None,
582
  gop_label: str = "global",
583
  ):
584
- """Two side-by-side panels comparing codec selection vs uniform sampling.
 
 
585
 
586
- Both panels share x = time (seconds), y = number of patches selected at
587
- that timestamp. Same total patch budget on both sides β€” only the
588
- *allocation* differs.
589
-
590
- Left (codec): patches go where saliency wants them β€” often spiky
591
- and concentrated on a few moments.
592
- Right (uniform): the same budget is spent on full frames sampled
593
- evenly in time; every uniform frame contributes the
594
- full grid's worth of patches.
595
- """
596
- fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9.2, 3.2), constrained_layout=True)
597
 
598
  fps_safe = float(fps) if fps and fps > 0 else 25.0
599
  if grids:
@@ -605,70 +602,85 @@ def make_charts(
605
  (max(frame_ids) / fps_safe) if frame_ids else 1.0
606
  )
607
 
608
- # ─── Left: codec selection over time ─────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
609
  times = [fid / fps_safe for fid in frame_ids]
610
  counts = [int(m.sum()) for m in masks]
611
- if not times:
612
- times, counts = [0.0], [0]
613
- bar_w = max(duration / max(1, len(times)) * 0.55, 0.04)
614
- ax1.bar(
615
- times, counts, width=bar_w,
616
- color="#4f46e5", alpha=0.88,
617
- edgecolor="#312e81", linewidth=0.4,
 
618
  )
619
- total_selected = sum(counts)
620
- n_groups = len(groups) if groups else 1
621
- gop_str = (
622
- gop_label if gop_label in ("global", "dynamic") else f"GOP={gop_label}"
 
 
 
 
 
 
623
  )
624
- ax1.set_title(
625
- f"Codec selection Β· {saliency_signal} Β· {gop_str} "
626
- f"({n_groups} groups) Β· {total_selected} patches",
627
- fontsize=10, color="#1e293b",
 
628
  )
629
- ax1.set_xlabel("time (s)", fontsize=9)
630
- ax1.set_ylabel("# patches selected", fontsize=9)
631
- ax1.set_xlim(-duration * 0.02, duration * 1.02)
632
- ax1.tick_params(axis="both", labelsize=8)
633
- ax1.grid(True, alpha=0.25, linestyle="--", axis="y")
634
- ax1.spines[["top", "right"]].set_visible(False)
635
-
636
- # Group boundary lines (skip if there's just one big group).
 
 
 
637
  if groups and len(groups) > 1 and times:
638
  for (_, end_idx) in groups[:-1]:
639
  if end_idx + 1 < len(times):
640
  bx = (times[end_idx] + times[end_idx + 1]) / 2.0
641
  else:
642
- bx = times[end_idx] + bar_w
643
- ax1.axvline(
644
- bx, color="#94a3b8", linestyle=(0, (4, 3)),
645
- alpha=0.55, linewidth=0.9,
646
  )
647
 
648
- # ─── Right: uniform-sampling baseline at the same budget ────────────
649
- n_uniform = max(1, int(total_patches_budget // max(1, grid_size)))
650
- uniform_times = (
651
- [duration * 0.5] if n_uniform == 1
652
- else list(np.linspace(0.0, duration, n_uniform))
653
- )
654
- uniform_counts = [grid_size] * n_uniform
655
- bar_w_u = max(duration / max(1, n_uniform) * 0.55, 0.04)
656
- ax2.bar(
657
- uniform_times, uniform_counts, width=bar_w_u,
658
- color="#06b6d4", alpha=0.88,
659
- edgecolor="#0e7490", linewidth=0.4,
660
- )
661
- ax2.set_title(
662
- f"Uniform baseline Β· {n_uniform} frames Γ— {grid_size} patches "
663
- f"= {n_uniform * grid_size}",
664
- fontsize=10, color="#1e293b",
665
  )
666
- ax2.set_xlabel("time (s)", fontsize=9)
667
- ax2.set_ylabel("# patches per uniform frame", fontsize=9)
668
- ax2.set_xlim(-duration * 0.02, duration * 1.02)
669
- ax2.tick_params(axis="both", labelsize=8)
670
- ax2.grid(True, alpha=0.25, linestyle="--", axis="y")
671
- ax2.spines[["top", "right"]].set_visible(False)
 
 
 
672
 
673
  fig.patch.set_facecolor("white")
674
  return fig
@@ -1371,14 +1383,13 @@ with gr.Blocks(**_BLOCK_KW) as demo:
1371
  )
1372
 
1373
  with gr.Group(elem_classes="ovc-card ovc-card-primary"):
1374
- gr.Markdown("### Codec selection vs uniform baseline")
1375
  gr.Markdown(
1376
- "<small>Same total patch budget on both sides. "
1377
- "<b>Left</b>: where the codec saliency actually placed "
1378
- "patches (often spiky, concentrated on important moments). "
1379
- "<b>Right</b>: how a naive uniform frame sampler would "
1380
- "spend the same budget β€” spread evenly in time, every "
1381
- "patch in the chosen frames kept.</small>"
1382
  )
1383
  chart_out = gr.Plot(label="", show_label=False)
1384
 
 
581
  groups: List[Tuple[int, int]] = None,
582
  gop_label: str = "global",
583
  ):
584
+ """One overlaid step chart: cumulative patches selected vs time, for
585
+ the codec saliency curve and a uniform-sampling baseline at the same
586
+ total budget.
587
 
588
+ X = time (s)
589
+ Y = cumulative count of selected patches
590
+ Both curves end near the budget (codec: == total selected; uniform:
591
+ n_uniform_frames Γ— grid_size, ≀ budget). The codec curve rises in
592
+ bursts where saliency is high; uniform rises in equal steps."""
593
+ fig, ax = plt.subplots(figsize=(9.2, 3.6), constrained_layout=True)
 
 
 
 
 
594
 
595
  fps_safe = float(fps) if fps and fps > 0 else 25.0
596
  if grids:
 
602
  (max(frame_ids) / fps_safe) if frame_ids else 1.0
603
  )
604
 
605
+ # ─── Build step curves ──────────────────────────────────────────────
606
+ def _step(xs, cum):
607
+ """Return (xx, yy) for a left-continuous step plot through (xs, cum)."""
608
+ if not xs:
609
+ return [0.0, duration], [0.0, 0.0]
610
+ xx, yy = [0.0], [0.0]
611
+ prev = 0.0
612
+ for x, c in zip(xs, cum):
613
+ xx.extend([x, x]); yy.extend([prev, c])
614
+ prev = c
615
+ xx.append(duration); yy.append(prev)
616
+ return xx, yy
617
+
618
  times = [fid / fps_safe for fid in frame_ids]
619
  counts = [int(m.sum()) for m in masks]
620
+ codec_cum = list(np.cumsum(counts)) if counts else []
621
+ codec_total = int(codec_cum[-1]) if codec_cum else 0
622
+ xx_c, yy_c = _step(times, codec_cum)
623
+
624
+ n_uniform = max(1, int(total_patches_budget // max(1, grid_size)))
625
+ uni_times = (
626
+ [duration * 0.5] if n_uniform == 1
627
+ else list(np.linspace(0.0, duration, n_uniform))
628
  )
629
+ uni_cum = list(np.cumsum([grid_size] * n_uniform))
630
+ uni_total = int(uni_cum[-1]) if uni_cum else 0
631
+ xx_u, yy_u = _step(uni_times, uni_cum)
632
+
633
+ # ─── Plot ───────────────────────────────────────────────────────────
634
+ ax.fill_between(xx_c, yy_c, step=None, alpha=0.12, color="#4f46e5")
635
+ ax.plot(
636
+ xx_c, yy_c,
637
+ color="#4f46e5", linewidth=2.2,
638
+ label=f"Codec Β· {saliency_signal} ({codec_total:,} patches)",
639
  )
640
+ ax.fill_between(xx_u, yy_u, step=None, alpha=0.10, color="#06b6d4")
641
+ ax.plot(
642
+ xx_u, yy_u,
643
+ color="#06b6d4", linewidth=2.2, linestyle="--",
644
+ label=f"Uniform baseline ({uni_total:,} patches)",
645
  )
646
+
647
+ # Budget reference line
648
+ budget = int(total_patches_budget)
649
+ ax.axhline(budget, color="#94a3b8", linestyle=":", linewidth=1.1, alpha=0.85)
650
+ ax.text(
651
+ duration * 0.995, budget * 1.015,
652
+ f"budget {budget:,}", color="#475569",
653
+ fontsize=8.5, va="bottom", ha="right",
654
+ )
655
+
656
+ # Group boundaries
657
  if groups and len(groups) > 1 and times:
658
  for (_, end_idx) in groups[:-1]:
659
  if end_idx + 1 < len(times):
660
  bx = (times[end_idx] + times[end_idx + 1]) / 2.0
661
  else:
662
+ bx = times[end_idx]
663
+ ax.axvline(
664
+ bx, color="#cbd5e1", linestyle=(0, (3, 3)),
665
+ alpha=0.55, linewidth=0.8,
666
  )
667
 
668
+ n_groups = len(groups) if groups else 1
669
+ gop_str = gop_label if gop_label in ("global", "dynamic") else f"GOP={gop_label}"
670
+ ax.set_title(
671
+ f"Cumulative patches selected over time Β· {saliency_signal} Β· "
672
+ f"{gop_str} ({n_groups} groups)",
673
+ fontsize=11, color="#1e293b",
 
 
 
 
 
 
 
 
 
 
 
674
  )
675
+ ax.set_xlabel("time (s)", fontsize=9.5)
676
+ ax.set_ylabel("# patches selected (cumulative)", fontsize=9.5)
677
+ ax.set_xlim(-duration * 0.02, duration * 1.02)
678
+ ymax = max(budget, codec_total, uni_total) * 1.08 + 1
679
+ ax.set_ylim(0, ymax)
680
+ ax.tick_params(axis="both", labelsize=8.5)
681
+ ax.grid(True, alpha=0.25, linestyle="--", axis="y")
682
+ ax.spines[["top", "right"]].set_visible(False)
683
+ ax.legend(loc="upper left", fontsize=9, frameon=False)
684
 
685
  fig.patch.set_facecolor("white")
686
  return fig
 
1383
  )
1384
 
1385
  with gr.Group(elem_classes="ovc-card ovc-card-primary"):
1386
+ gr.Markdown("### Cumulative patches over time")
1387
  gr.Markdown(
1388
+ "<small>Same total patch budget. <b>Indigo</b>: codec "
1389
+ "saliency cumulative count β€” rises in bursts on high-"
1390
+ "energy moments. <b>Cyan (dashed)</b>: uniform sampling "
1391
+ "baseline β€” equal-height steps at evenly-spaced times. "
1392
+ "Both end near the dotted <b>budget</b> reference line.</small>"
 
1393
  )
1394
  chart_out = gr.Plot(label="", show_label=False)
1395