emirkisa commited on
Commit
3e77b5d
·
verified ·
1 Parent(s): 66c99d0

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +141 -76
app.py CHANGED
@@ -72,6 +72,16 @@ CACHE_DIR = Path(os.environ.get(
72
  ))
73
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
74
 
 
 
 
 
 
 
 
 
 
 
75
  DAVIS_PALETTE = np.array([
76
  [ 0, 0, 0], [128, 0, 0], [ 0, 128, 0], [128, 128, 0],
77
  [ 0, 0, 128], [128, 0, 128], [ 0, 128, 128], [128, 128, 128],
@@ -395,13 +405,21 @@ def get_legend(seq: str) -> str:
395
  return "\n".join(lines)
396
 
397
 
 
 
 
 
 
 
398
  def cache_status_md() -> str:
399
  with _cache_lock:
400
- done = sum(1 for v in _cache_progress.values() if v == "done")
 
401
  total = len(ALL_SEQUENCES)
402
  pct = done / total * 100 if total else 0
403
  bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
404
- return f"`[{bar}]` **{done}/{total}** cached ({pct:.0f}%)"
 
405
 
406
 
407
  # ── Stats plots ────────────────────────────────────────────────────────────────
@@ -688,43 +706,58 @@ def build_ui():
688
  with gr.TabItem("📺 Multi-Video"):
689
  gr.Markdown(
690
  f"Watch **{PAGE_SIZE} sequences at once** in a 3×3 grid. "
691
- "Use Prev/Next to page through all {len(ALL_SEQUENCES)} sequences, "
692
- "or filter first. Videos are encoded once and cached permanently."
693
  )
694
- with gr.Row():
695
- mv_year = gr.Dropdown(["All years","2016 only","2017 only"],
696
- value="All years", label="Year", scale=1)
697
- mv_split = gr.Dropdown(["All splits","Train only","Val only"],
698
- value="All splits", label="Split", scale=1)
699
- mv_obj = gr.Dropdown(["Any # objects","1 object","2 objects","3+ objects"],
700
- value="Any # objects", label="Objects", scale=1)
701
- mv_srch = gr.Textbox(placeholder="Search…", label="Search", scale=2)
702
- with gr.Row():
703
- mv_fps = gr.Slider(1, 30, DEFAULT_FPS, step=1, label="FPS", scale=2)
704
- mv_ov = gr.Checkbox(value=True, label="Burn overlay", scale=1)
705
- mv_a = gr.Slider(0.1, 1.0, DEFAULT_ALPHA, step=0.05,
706
- label="Opacity", scale=2)
707
- mv_load = gr.Button("▶ Load Page", variant="primary", scale=1)
708
-
709
- with gr.Row():
710
- mv_prev = gr.Button("◀ Prev", scale=1)
711
- with gr.Column(scale=3):
712
- mv_page_lbl = gr.Markdown(f"**Page 1 / {total_pages}**")
713
- mv_next = gr.Button("Next ▶", scale=1)
714
 
715
- mv_status = gr.Markdown("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
 
717
- # 9 fixed video slots, 3 rows × 3 cols
718
- mv_vids = []
719
- mv_lbls = []
720
- for row_i in range(3):
721
  with gr.Row():
722
- for col_i in range(3):
723
- with gr.Column():
724
- lbl = gr.Markdown("")
725
- vid = gr.Video(height=260, autoplay=True, label="")
726
- mv_lbls.append(lbl)
727
- mv_vids.append(vid)
 
 
 
 
 
 
 
 
 
 
 
 
728
 
729
  # State: list of sequences currently matching filter, page index
730
  mv_seq_state = gr.State(ALL_SEQUENCES.copy())
@@ -757,36 +790,44 @@ def build_ui():
757
  [mv_seq_state, mv_page_state, mv_page_lbl])
758
 
759
  def _load_page(seqs, page, ov, a, fps):
760
- start = page * PAGE_SIZE
761
- chunk = seqs[start: start + PAGE_SIZE]
762
- tp = max(1, (len(seqs) + PAGE_SIZE - 1) // PAGE_SIZE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
  pg_lbl = f"**Page {page + 1} / {tp}**"
764
- vids = []
765
- labels = []
766
-
767
- def _enc(seq):
768
- p, _ = get_video(seq, ov, a, fps)
769
- return seq, str(p) if p else None
770
-
771
- with ThreadPoolExecutor(max_workers=PAGE_SIZE) as pool:
772
- futs = {pool.submit(_enc, s): i for i, s in enumerate(chunk)}
773
- res = [None] * PAGE_SIZE
774
- lbs = [""] * PAGE_SIZE
775
- for fut in as_completed(futs):
776
- i = futs[fut]
777
- seq, path = fut.result()
778
- res[i] = path
779
- lbs[i] = seq
780
 
781
- # Pad to PAGE_SIZE
782
- while len(res) < PAGE_SIZE:
783
- res.append(None)
784
- lbs.append("—")
 
 
 
 
 
 
 
 
785
 
786
  n_loaded = sum(1 for r in res if r)
787
- status = f"✅ Loaded {n_loaded}/{len(chunk)} videos (page {page+1}/{tp})"
788
 
789
- # Build flat output: [lbl0, vid0, lbl1, vid1, …, status, pg_lbl]
790
  out = []
791
  for lb, r in zip(lbs, res):
792
  out.append(f"**{lb}**" if lb and lb != "—" else "—")
@@ -823,6 +864,27 @@ def build_ui():
823
  mv_next.click(_next, [mv_seq_state, mv_page_state],
824
  [mv_page_state, mv_page_lbl])
825
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
826
  # ──────────────────────────────────────────────────────────────
827
  # Tab 5 · Compare (up to 6 side-by-side)
828
  # ──────────────────────────────────────────────────────────────
@@ -872,23 +934,22 @@ def build_ui():
872
  res = [None] * MAX_COMPARE
873
  lbs = [""] * MAX_COMPARE
874
 
875
- def _enc(i, seq):
 
876
  if seq:
877
- p, _ = get_video(seq, ov, a, fps)
878
- res[i] = str(p) if p else None
 
 
 
879
  lbs[i] = seq
880
 
881
- with ThreadPoolExecutor(max_workers=MAX_COMPARE) as pool:
882
- futs = [pool.submit(_enc, i, s) for i, s in enumerate(slots)]
883
- for f in as_completed(futs):
884
- f.result()
885
-
886
  n_ok = sum(1 for r in res if r)
887
  out = []
888
  for r, l in zip(res, lbs):
889
  out.append(r)
890
  out.append(f"**{l}**" if l else "*empty*")
891
- out.append(f"✅ Loaded {n_ok}/{len([s for s in slots if s])} slots")
892
  return out
893
 
894
  cmp_btn.click(_load_all,
@@ -971,10 +1032,14 @@ demo = build_ui()
971
  start_precache(fps=DEFAULT_FPS, workers=4)
972
 
973
  if __name__ == "__main__":
974
- parser = argparse.ArgumentParser(description="DAVIS Dataset Explorer")
975
- parser.add_argument("--share", action="store_true")
976
- parser.add_argument("--port", type=int, default=7860)
977
- parser.add_argument("--host", default="0.0.0.0")
978
- args = parser.parse_args()
979
- demo.launch(server_name=args.host, server_port=args.port,
980
- share=args.share, theme=gr.themes.Soft())
 
 
 
 
 
72
  ))
73
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
74
 
75
+ def _cleanup_stale_tmp() -> None:
76
+ """Remove any leftover _tmp_* directories left by interrupted encode runs."""
77
+ stale = list(CACHE_DIR.glob("_tmp_*"))
78
+ if stale:
79
+ print(f" Removing {len(stale)} stale tmp dir(s) from previous run…")
80
+ for d in stale:
81
+ shutil.rmtree(d, ignore_errors=True)
82
+
83
+ _cleanup_stale_tmp()
84
+
85
  DAVIS_PALETTE = np.array([
86
  [ 0, 0, 0], [128, 0, 0], [ 0, 128, 0], [128, 128, 0],
87
  [ 0, 0, 128], [128, 0, 128], [ 0, 128, 128], [128, 128, 128],
 
405
  return "\n".join(lines)
406
 
407
 
408
+ def _is_cache_complete() -> bool:
409
+ with _cache_lock:
410
+ return (len(_cache_progress) >= len(ALL_SEQUENCES)
411
+ and all(v == "done" for v in _cache_progress.values()))
412
+
413
+
414
  def cache_status_md() -> str:
415
  with _cache_lock:
416
+ done = sum(1 for v in _cache_progress.values() if v == "done")
417
+ errors = sum(1 for v in _cache_progress.values() if v.startswith("error"))
418
  total = len(ALL_SEQUENCES)
419
  pct = done / total * 100 if total else 0
420
  bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
421
+ err = f" · ⚠️ {errors} errors" if errors else ""
422
+ return f"`[{bar}]` **{done}/{total}** cached ({pct:.0f}%){err}"
423
 
424
 
425
  # ── Stats plots ────────────────────────────────────────────────────────────────
 
706
  with gr.TabItem("📺 Multi-Video"):
707
  gr.Markdown(
708
  f"Watch **{PAGE_SIZE} sequences at once** in a 3×3 grid. "
709
+ f"Page through all {len(ALL_SEQUENCES)} sequences. "
710
+ "Videos are served from the permanent MP4 cache."
711
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
712
 
713
+ # ── Caching-in-progress overlay (hidden once done) ────────
714
+ _initially_done = _is_cache_complete()
715
+ with gr.Column(visible=not _initially_done) as mv_wait_col:
716
+ gr.Markdown("### ⏳ Building MP4 cache — please wait…")
717
+ mv_wait_status = gr.Markdown(cache_status_md())
718
+ gr.Markdown(
719
+ "All 90 sequences are being encoded as MP4s in the background "
720
+ "(raw + overlay variant each). The grid will unlock automatically "
721
+ "when encoding finishes. You can watch other tabs in the meantime."
722
+ )
723
+ mv_refresh_btn = gr.Button("↻ Refresh status", size="sm")
724
+
725
+ # ── Main grid (hidden until cache ready) ──────────────────
726
+ with gr.Column(visible=_initially_done) as mv_grid_col:
727
+ with gr.Row():
728
+ mv_year = gr.Dropdown(["All years","2016 only","2017 only"],
729
+ value="All years", label="Year", scale=1)
730
+ mv_split = gr.Dropdown(["All splits","Train only","Val only"],
731
+ value="All splits", label="Split", scale=1)
732
+ mv_obj = gr.Dropdown(["Any # objects","1 object","2 objects","3+ objects"],
733
+ value="Any # objects", label="Objects", scale=1)
734
+ mv_srch = gr.Textbox(placeholder="Search…", label="Search", scale=2)
735
+ with gr.Row():
736
+ mv_fps = gr.Slider(1, 30, DEFAULT_FPS, step=1, label="FPS", scale=2)
737
+ mv_ov = gr.Checkbox(value=True, label="Burn overlay", scale=1)
738
+ mv_a = gr.Slider(0.1, 1.0, DEFAULT_ALPHA, step=0.05,
739
+ label="Opacity", scale=2)
740
+ mv_load = gr.Button("▶ Load Page", variant="primary", scale=1)
741
 
 
 
 
 
742
  with gr.Row():
743
+ mv_prev = gr.Button("◀ Prev", scale=1)
744
+ with gr.Column(scale=3):
745
+ mv_page_lbl = gr.Markdown(f"**Page 1 / {total_pages}**")
746
+ mv_next = gr.Button("Next ▶", scale=1)
747
+
748
+ mv_status = gr.Markdown("")
749
+
750
+ # 9 fixed video slots, 3 rows × 3 cols
751
+ mv_vids = []
752
+ mv_lbls = []
753
+ for row_i in range(3):
754
+ with gr.Row():
755
+ for col_i in range(3):
756
+ with gr.Column():
757
+ lbl = gr.Markdown("—")
758
+ vid = gr.Video(height=260, autoplay=True, label="")
759
+ mv_lbls.append(lbl)
760
+ mv_vids.append(vid)
761
 
762
  # State: list of sequences currently matching filter, page index
763
  mv_seq_state = gr.State(ALL_SEQUENCES.copy())
 
790
  [mv_seq_state, mv_page_state, mv_page_lbl])
791
 
792
  def _load_page(seqs, page, ov, a, fps):
793
+ # Block if pre-cache not yet finished
794
+ if not _is_cache_complete():
795
+ with _cache_lock:
796
+ done = sum(1 for v in _cache_progress.values() if v == "done")
797
+ total = len(ALL_SEQUENCES)
798
+ pct = int(done / total * 100) if total else 0
799
+ bar = "█" * (pct // 5) + "░" * (20 - pct // 5)
800
+ status = (f"⏳ `[{bar}]` {done}/{total} sequences cached ({pct}%) — "
801
+ "please wait for caching to finish then click Load Page again.")
802
+ out = []
803
+ for _ in range(PAGE_SIZE):
804
+ out.append("—")
805
+ out.append(None)
806
+ out.append(status)
807
+ out.append(f"**Page {page + 1} / ?**")
808
+ return out
809
+
810
+ start = page * PAGE_SIZE
811
+ chunk = seqs[start: start + PAGE_SIZE]
812
+ tp = max(1, (len(seqs) + PAGE_SIZE - 1) // PAGE_SIZE)
813
  pg_lbl = f"**Page {page + 1} / {tp}**"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814
 
815
+ # Sequential encode_sequence() returns in <1 ms when already cached
816
+ res, lbs = [], []
817
+ for s in chunk:
818
+ try:
819
+ p, _ = get_video(s, ov, a, fps)
820
+ res.append(str(p) if p else None)
821
+ except Exception:
822
+ res.append(None)
823
+ lbs.append(s)
824
+
825
+ while len(res) < PAGE_SIZE: # pad
826
+ res.append(None); lbs.append("—")
827
 
828
  n_loaded = sum(1 for r in res if r)
829
+ status = f"✅ {n_loaded}/{len(chunk)} videos loaded (page {page+1}/{tp})"
830
 
 
831
  out = []
832
  for lb, r in zip(lbs, res):
833
  out.append(f"**{lb}**" if lb and lb != "—" else "—")
 
864
  mv_next.click(_next, [mv_seq_state, mv_page_state],
865
  [mv_page_state, mv_page_lbl])
866
 
867
+ # ── Cache progress wiring (timer + manual refresh) ────────
868
+ def _mv_cache_tick():
869
+ """Auto-refresh: hides the wait panel and shows the grid when done."""
870
+ done_now = _is_cache_complete()
871
+ status = cache_status_md()
872
+ return (
873
+ status, # mv_wait_status
874
+ gr.update(visible=not done_now), # mv_wait_col
875
+ gr.update(visible=done_now), # mv_grid_col
876
+ gr.update(active=not done_now), # timer — stop when done
877
+ )
878
+
879
+ mv_refresh_btn.click(_mv_cache_tick,
880
+ outputs=[mv_wait_status, mv_wait_col,
881
+ mv_grid_col, gr.State()])
882
+
883
+ mv_timer = gr.Timer(value=4, active=not _initially_done)
884
+ mv_timer.tick(_mv_cache_tick,
885
+ outputs=[mv_wait_status, mv_wait_col,
886
+ mv_grid_col, mv_timer])
887
+
888
  # ──────────────────────────────────────────────────────────────
889
  # Tab 5 · Compare (up to 6 side-by-side)
890
  # ──────────────────────────────────────────────────────────────
 
934
  res = [None] * MAX_COMPARE
935
  lbs = [""] * MAX_COMPARE
936
 
937
+ # Sequential — fast when pre-cached, safe in all environments
938
+ for i, seq in enumerate(slots):
939
  if seq:
940
+ try:
941
+ p, _ = get_video(seq, ov, a, fps)
942
+ res[i] = str(p) if p else None
943
+ except Exception:
944
+ res[i] = None
945
  lbs[i] = seq
946
 
 
 
 
 
 
947
  n_ok = sum(1 for r in res if r)
948
  out = []
949
  for r, l in zip(res, lbs):
950
  out.append(r)
951
  out.append(f"**{l}**" if l else "*empty*")
952
+ out.append(f"✅ {n_ok}/{len([s for s in slots if s])} slots loaded")
953
  return out
954
 
955
  cmp_btn.click(_load_all,
 
1032
  start_precache(fps=DEFAULT_FPS, workers=4)
1033
 
1034
  if __name__ == "__main__":
1035
+ if IS_HF_SPACE:
1036
+ # HF Spaces manages routing — just launch without binding params.
1037
+ demo.launch(theme=gr.themes.Soft())
1038
+ else:
1039
+ parser = argparse.ArgumentParser(description="DAVIS Dataset Explorer")
1040
+ parser.add_argument("--share", action="store_true")
1041
+ parser.add_argument("--port", type=int, default=7860)
1042
+ parser.add_argument("--host", default="0.0.0.0")
1043
+ args = parser.parse_args()
1044
+ demo.launch(server_name=args.host, server_port=args.port,
1045
+ share=args.share, theme=gr.themes.Soft())