JS6969 commited on
Commit
a2ca12e
·
verified ·
1 Parent(s): a5cd0ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -89
app.py CHANGED
@@ -11,13 +11,12 @@
11
 
12
 
13
  import sys, types
14
- try:
15
- import torchvision.transforms.functional_tensor as _ft # noqa: F401
16
- except Exception:
17
- from torchvision.transforms import functional as _F
18
- _mod = types.ModuleType("torchvision.transforms.functional_tensor")
19
- _mod.rgb_to_grayscale = _F.rgb_to_grayscale
20
- sys.modules["torchvision.transforms.functional_tensor"] = _mod
21
 
22
  # ────────────────────────────────────────────────────────
23
  # Standard imports
@@ -73,7 +72,28 @@ def sample_paths(paths: List[Path] | List[str], n: int = 30) -> List[str]:
73
  out.append(str(paths[i]))
74
  seen.add(i)
75
  return out
76
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  # Flag so UI can know if realesrgan is importable
78
  HAVE_REALESRGAN = True
79
 
@@ -856,94 +876,94 @@ def step3_encode(frames_dir_state: str | None, prefix_state: str | None, orig_vi
856
 
857
  # ───────────────── Quick Mode — one click: All frames → Upscale ×4 → MP4 (audio)
858
 
859
- def quick_mode(video: gr.File | None, start_time: str, end_time: str, resize_long: int, prefix_in: str, prog_html: str):
860
- if not video or not video.name:
861
- return None, None, None, "Upload a video.", prog_html
862
- if not (FFMPEG and FFPROBE and HAVE_REALESRGAN):
863
- return None, None, None, "Missing deps (ffmpeg/ffprobe/realesrgan). See requirements.txt.", prog_html
864
 
865
- info = parse_video_info(ffprobe_json(video.name))
866
- in_fps = info.get("fps") or 30.0
867
- prefix = sanitize_prefix(prefix_in) or Path(video.name).stem
868
 
869
- work = Path(tempfile.mkdtemp(prefix="quick_"))
870
- raw_dir = work / "frames_raw"; raw_dir.mkdir(parents=True, exist_ok=True)
871
- up_dir = work / "upscaled"; up_dir.mkdir(parents=True, exist_ok=True)
872
 
873
  # Extract all frames
874
- extract_cmd = build_ffmpeg_extract(
875
- input_path=video.name,
876
- mode="All frames",
877
- every_seconds=1.0,
878
- nth_frame=1,
879
- exact_fps=in_fps,
880
- start_time=(start_time or "").strip(),
881
- end_time=(end_time or "").strip(),
882
- long_side=resize_long,
883
- out_format="jpg",
884
- jpg_quality=3,
885
- png_level=2,
886
- scene_detect=False,
887
- scene_thresh=0.3,
888
- out_pattern=str(raw_dir / f"{prefix}_%05d.jpg"),
889
- )
890
- proc = subprocess.Popen(extract_cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True, bufsize=1)
891
- est = estimate_output_count("All frames", info.get("duration"), in_fps, 1.0, 1, in_fps)
892
- created = 0
893
- while True:
894
- line = proc.stderr.readline()
895
- if not line and proc.poll() is not None:
896
- break
897
- if int(time.time()*10) % 3 == 0:
898
- created = len(list(raw_dir.glob(f"{prefix}_*.jpg")))
899
- pct = min(100.0, (created / est) * 100.0) if est else 0
900
- prog_html = render_progress(pct, f"Phase 1/3: Extracting {created}/{est or '?'}")
901
- proc.wait()
902
-
903
- frames = sorted(raw_dir.glob(f"{prefix}_*.jpg"))
904
- if not frames:
905
- return None, None, None, "No frames extracted in Quick Mode.", prog_html
906
 
907
  # Upscale x4
908
- device = "cuda" if os.environ.get("CUDA_VISIBLE_DEVICES") else "cpu"
909
- upsampler = get_realesrganer("x4plus", 4, 0, (device=="cuda"), device=device)
910
-
911
- total = len(frames)
912
- done = 0
913
- for fp in frames:
914
- img = Image.open(fp).convert("RGB")
915
- output, _ = upsampler.enhance(np.array(img), outscale=4)
916
- Image.fromarray(output).save(up_dir / (Path(fp).stem + ".jpg"), quality=95)
917
- done += 1
918
- pct = (done/total)*100 if total else 0
919
- prog_html = render_progress(pct, f"Phase 2/3: Upscaling {done}/{total}")
920
 
921
  # Encode MP4 with audio
922
- encode_cmd = build_ffmpeg_encode(str(up_dir), prefix, in_fps, "h264", True, video.name)
923
- proc2 = subprocess.Popen(encode_cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True, bufsize=1, cwd=str(up_dir))
924
- while True:
925
- line = proc2.stderr.readline()
926
- if not line and proc2.poll() is not None:
927
- break
928
- if int(time.time()*10) % 5 == 0:
929
- prog_html = render_progress(50.0, "Phase 3/3: Encoding…")
930
- proc2.wait()
931
-
932
- out_file = Path(up_dir) / "output.mp4"
933
- if not out_file.exists():
934
- return None, None, None, "Encoding failed in Quick Mode.", prog_html
935
 
936
  # Intermediates
937
- zip_frames = work / "frames.zip"
938
- with zipfile.ZipFile(zip_frames, "w", zipfile.ZIP_DEFLATED) as zf:
939
- for p in frames:
940
- zf.write(p, p.name)
941
- zip_up = work / "upscaled.zip"
942
- with zipfile.ZipFile(zip_up, "w", zipfile.ZIP_DEFLATED) as zf:
943
- for p in sorted(up_dir.glob("*.jpg"), key=_natural_key):
944
- zf.write(p, p.name)
945
 
946
- return str(out_file), str(zip_frames), str(zip_up), "Quick Mode complete.", render_progress(100.0, "All done")
947
 
948
  # ───────────────── UI
949
 
@@ -952,10 +972,9 @@ def build_ui():
952
  .cf-title { font-size: 1.6rem; font-weight: 800; }
953
  .cmdbox textarea { font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
954
  """) as demo:
955
- gr.Markdown("""
956
- <div class=\"cf-title\">Video → Frames → Upscale → Re-encode</div>
957
- Three-step workflow + Quick Mode. Step 2/3 now accept your own uploaded files as inputs.
958
- """)
959
 
960
  # Shared states (from Step 1)
961
  frames_state = gr.State([]) # list[str]
 
11
 
12
 
13
  import sys, types
14
+ def _rgb_to_grayscale_np(arr: np.ndarray) -> np.ndarray:
15
+ # arr: HxWx3 uint8
16
+ r, g, b = arr[...,0], arr[...,1], arr[...,2]
17
+ gray = (0.2989*r + 0.5870*g + 0.1140*b).astype(arr.dtype)
18
+ return np.stack([gray, gray, gray], axis=-1)
19
+
 
20
 
21
  # ────────────────────────────────────────────────────────
22
  # Standard imports
 
72
  out.append(str(paths[i]))
73
  seen.add(i)
74
  return out
75
+
76
+ import base64
77
+
78
+ def load_logo_base64(path: str) -> str:
79
+ with open(path, "rb") as f:
80
+ return base64.b64encode(f.read()).decode("utf-8")
81
+
82
+ # Preload your Bifröst logo
83
+ LOGO_B64 = load_logo_base64(os.path.join("assets", "bifrost_logo.png"))
84
+
85
+ def render_logo_html(px: int = 96) -> str:
86
+ return f"""
87
+ <div style="display:flex;align-items:center;gap:16px;">
88
+ <img src="data:image/png;base64,{LOGO_B64}" style="height:{px}px;width:auto;" />
89
+ <div>
90
+ <div style="font-size:1.6rem;font-weight:800;">Bifröst Beam</div>
91
+ <div style="opacity:0.8;">Video → Frames → Upscale (Nordic Inspired)</div>
92
+ </div>
93
+ </div>
94
+ <hr>
95
+ """
96
+
97
  # Flag so UI can know if realesrgan is importable
98
  HAVE_REALESRGAN = True
99
 
 
876
 
877
  # ───────────────── Quick Mode — one click: All frames → Upscale ×4 → MP4 (audio)
878
 
879
+ #def quick_mode(video: gr.File | None, start_time: str, end_time: str, resize_long: int, prefix_in: str, prog_html: str):
880
+ # if not video or not video.name:
881
+ # return None, None, None, "Upload a video.", prog_html
882
+ # if not (FFMPEG and FFPROBE and HAVE_REALESRGAN):
883
+ # return None, None, None, "Missing deps (ffmpeg/ffprobe/realesrgan). See requirements.txt.", prog_html
884
 
885
+ # info = parse_video_info(ffprobe_json(video.name))
886
+ # in_fps = info.get("fps") or 30.0
887
+ # prefix = sanitize_prefix(prefix_in) or Path(video.name).stem
888
 
889
+ # work = Path(tempfile.mkdtemp(prefix="quick_"))
890
+ # raw_dir = work / "frames_raw"; raw_dir.mkdir(parents=True, exist_ok=True)
891
+ # up_dir = work / "upscaled"; up_dir.mkdir(parents=True, exist_ok=True)
892
 
893
  # Extract all frames
894
+ # extract_cmd = build_ffmpeg_extract(
895
+ # input_path=video.name,
896
+ # mode="All frames",
897
+ # every_seconds=1.0,
898
+ # nth_frame=1,
899
+ # exact_fps=in_fps,
900
+ # start_time=(start_time or "").strip(),
901
+ # end_time=(end_time or "").strip(),
902
+ # long_side=resize_long,
903
+ # out_format="jpg",
904
+ # jpg_quality=3,
905
+ # png_level=2,
906
+ # scene_detect=False,
907
+ # scene_thresh=0.3,
908
+ # out_pattern=str(raw_dir / f"{prefix}_%05d.jpg"),
909
+ # )
910
+ # proc = subprocess.Popen(extract_cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True, bufsize=1)
911
+ # est = estimate_output_count("All frames", info.get("duration"), in_fps, 1.0, 1, in_fps)
912
+ # created = 0
913
+ # while True:
914
+ # line = proc.stderr.readline()
915
+ # if not line and proc.poll() is not None:
916
+ # break
917
+ # if int(time.time()*10) % 3 == 0:
918
+ # created = len(list(raw_dir.glob(f"{prefix}_*.jpg")))
919
+ # pct = min(100.0, (created / est) * 100.0) if est else 0
920
+ # prog_html = render_progress(pct, f"Phase 1/3: Extracting {created}/{est or '?'}")
921
+ # proc.wait()
922
+
923
+ # frames = sorted(raw_dir.glob(f"{prefix}_*.jpg"))
924
+ # if not frames:
925
+ # return None, None, None, "No frames extracted in Quick Mode.", prog_html
926
 
927
  # Upscale x4
928
+ # device = "cuda" if os.environ.get("CUDA_VISIBLE_DEVICES") else "cpu"
929
+ # upsampler = get_realesrganer("x4plus", 4, 0, (device=="cuda"), device=device)
930
+
931
+ # total = len(frames)
932
+ # done = 0
933
+ # for fp in frames:
934
+ # img = Image.open(fp).convert("RGB")
935
+ # output, _ = upsampler.enhance(np.array(img), outscale=4)
936
+ # Image.fromarray(output).save(up_dir / (Path(fp).stem + ".jpg"), quality=95)
937
+ # done += 1
938
+ # pct = (done/total)*100 if total else 0
939
+ # prog_html = render_progress(pct, f"Phase 2/3: Upscaling {done}/{total}")
940
 
941
  # Encode MP4 with audio
942
+ # encode_cmd = build_ffmpeg_encode(str(up_dir), prefix, in_fps, "h264", True, video.name)
943
+ # proc2 = subprocess.Popen(encode_cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True, bufsize=1, cwd=str(up_dir))
944
+ # while True:
945
+ # line = proc2.stderr.readline()
946
+ # if not line and proc2.poll() is not None:
947
+ # break
948
+ # if int(time.time()*10) % 5 == 0:
949
+ # prog_html = render_progress(50.0, "Phase 3/3: Encoding…")
950
+ # proc2.wait()
951
+
952
+ # out_file = Path(up_dir) / "output.mp4"
953
+ # if not out_file.exists():
954
+ # return None, None, None, "Encoding failed in Quick Mode.", prog_html
955
 
956
  # Intermediates
957
+ # zip_frames = work / "frames.zip"
958
+ # with zipfile.ZipFile(zip_frames, "w", zipfile.ZIP_DEFLATED) as zf:
959
+ # for p in frames:
960
+ # zf.write(p, p.name)
961
+ # zip_up = work / "upscaled.zip"
962
+ # with zipfile.ZipFile(zip_up, "w", zipfile.ZIP_DEFLATED) as zf:
963
+ # for p in sorted(up_dir.glob("*.jpg"), key=_natural_key):
964
+ # zf.write(p, p.name)
965
 
966
+ # return str(out_file), str(zip_frames), str(zip_up), "Quick Mode complete.", render_progress(100.0, "All done")
967
 
968
  # ───────────────── UI
969
 
 
972
  .cf-title { font-size: 1.6rem; font-weight: 800; }
973
  .cmdbox textarea { font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
974
  """) as demo:
975
+ gr.HTML(render_logo_html(96))
976
+ gr.Markdown("Three-step workflow. Video → Frames → Upscale → Re-encode")
977
+
 
978
 
979
  # Shared states (from Step 1)
980
  frames_state = gr.State([]) # list[str]