Update app.py
Browse files
app.py
CHANGED
|
@@ -46,6 +46,34 @@ from basicsr.utils.download_util import load_file_from_url
|
|
| 46 |
from realesrgan import RealESRGANer
|
| 47 |
from realesrgan.archs.srvgg_arch import SRVGGNetCompact
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
# Flag so UI can know if realesrgan is importable
|
| 50 |
HAVE_REALESRGAN = True
|
| 51 |
|
|
@@ -158,13 +186,22 @@ def clamp_scale_for_model(outscale: int, model_id: str) -> int:
|
|
| 158 |
|
| 159 |
|
| 160 |
def sample_paths(paths: List[Path] | List[str], n: int = 30) -> List[str]:
|
| 161 |
-
"""
|
| 162 |
if not paths:
|
| 163 |
return []
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
|
| 170 |
def sanitize_prefix(txt: str) -> str:
|
|
@@ -281,6 +318,8 @@ def build_ffmpeg_extract(
|
|
| 281 |
cmd += ["-frame_pts", "1", out_pattern]
|
| 282 |
return cmd
|
| 283 |
|
|
|
|
|
|
|
| 284 |
|
| 285 |
def realesrgan(img, model_name, denoise_strength, face_enhance, outscale):
|
| 286 |
if img is None:
|
|
@@ -480,8 +519,7 @@ def step1_extract(
|
|
| 480 |
last_html = render_progress(0.0, f"Extracting… {created} created")
|
| 481 |
ret = proc.wait()
|
| 482 |
|
| 483 |
-
frames = sorted(raw_dir.glob(f"{prefix}_*.{out_format}"))
|
| 484 |
-
if ret != 0 or not frames:
|
| 485 |
try:
|
| 486 |
err = proc.stderr.read() if proc.stderr else ""
|
| 487 |
except Exception:
|
|
@@ -516,14 +554,12 @@ def save_uploaded_images(files: List[gr.File] | None, prefix: str = "upload") ->
|
|
| 516 |
|
| 517 |
def step2_upscale(
|
| 518 |
frames_list: List[str] | None,
|
| 519 |
-
|
| 520 |
-
|
| 521 |
tile: int,
|
| 522 |
precision: str,
|
| 523 |
prog_html: str,
|
| 524 |
uploaded_imgs: List[gr.File] | None,
|
| 525 |
-
denoise_strength: float = 0.5, # NEW: accepted but currently unused
|
| 526 |
-
face_enhance: bool = False, # NEW: accepted but currently unused (needs GFPGAN)
|
| 527 |
):
|
| 528 |
|
| 529 |
# decide source: uploaded images take priority, else frames from step 1
|
|
@@ -534,7 +570,8 @@ def step2_upscale(
|
|
| 534 |
src_paths = frames_list or []
|
| 535 |
|
| 536 |
if not src_paths:
|
| 537 |
-
|
|
|
|
| 538 |
|
| 539 |
# Map demo model -> internal, clamp scale to supported values
|
| 540 |
model_id = map_ui_model_to_internal(ui_model_name)
|
|
@@ -550,6 +587,8 @@ def step2_upscale(
|
|
| 550 |
total = len(src_paths)
|
| 551 |
done = 0
|
| 552 |
up_paths: List[Path] = []
|
|
|
|
|
|
|
| 553 |
for fp in src_paths:
|
| 554 |
try:
|
| 555 |
img = Image.open(fp).convert("RGB")
|
|
@@ -561,22 +600,29 @@ def step2_upscale(
|
|
| 561 |
except Exception:
|
| 562 |
pass
|
| 563 |
done += 1
|
| 564 |
-
|
| 565 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 566 |
|
| 567 |
if not up_paths:
|
| 568 |
-
|
|
|
|
| 569 |
|
|
|
|
| 570 |
gallery = sample_paths(up_paths, 30)
|
| 571 |
zip_path = work / "upscaled.zip"
|
| 572 |
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 573 |
for p in up_paths:
|
| 574 |
zf.write(p, p.name)
|
| 575 |
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
return gallery, str(zip_path), f"Upscaled: {len(up_paths)}", render_progress(100.0, "Upscaling complete")
|
| 580 |
|
| 581 |
# ───────────────── Encode (Step 3) — supports uploaded frames/ZIP & optional audio source
|
| 582 |
|
|
@@ -752,7 +798,7 @@ def quick_mode(video: gr.File | None, start_time: str, end_time: str, resize_lon
|
|
| 752 |
zf.write(p, p.name)
|
| 753 |
zip_up = work / "upscaled.zip"
|
| 754 |
with zipfile.ZipFile(zip_up, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 755 |
-
for p in sorted(up_dir.glob("*.jpg")):
|
| 756 |
zf.write(p, p.name)
|
| 757 |
|
| 758 |
return str(out_file), str(zip_frames), str(zip_up), "Quick Mode complete.", render_progress(100.0, "All done")
|
|
|
|
| 46 |
from realesrgan import RealESRGANer
|
| 47 |
from realesrgan.archs.srvgg_arch import SRVGGNetCompact
|
| 48 |
|
| 49 |
+
_num = re.compile(r'(\d+)')
|
| 50 |
+
|
| 51 |
+
def _natural_key(p: Path | str):
|
| 52 |
+
s = str(p)
|
| 53 |
+
return [int(t) if t.isdigit() else t.lower() for t in _num.split(s)]
|
| 54 |
+
|
| 55 |
+
def sample_paths(paths: List[Path] | List[str], n: int = 30) -> List[str]:
|
| 56 |
+
"""Evenly sample up to n items across the entire list, in order."""
|
| 57 |
+
if not paths:
|
| 58 |
+
return []
|
| 59 |
+
# Ensure stable numeric ordering first (00001, 00002, ... 01000)
|
| 60 |
+
paths = sorted(paths, key=_natural_key)
|
| 61 |
+
total = len(paths)
|
| 62 |
+
n = max(1, min(n, total))
|
| 63 |
+
if n == total:
|
| 64 |
+
return [str(p) for p in paths]
|
| 65 |
+
# Even spacing (no duplicates), covering start→end
|
| 66 |
+
step = (total - 1) / (n - 1)
|
| 67 |
+
idxs = [round(i * step) for i in range(n)]
|
| 68 |
+
# De-dupe in case of edge rounding on tiny sets
|
| 69 |
+
out = []
|
| 70 |
+
seen = set()
|
| 71 |
+
for i in idxs:
|
| 72 |
+
if i not in seen:
|
| 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 |
|
|
|
|
| 186 |
|
| 187 |
|
| 188 |
def sample_paths(paths: List[Path] | List[str], n: int = 30) -> List[str]:
|
| 189 |
+
"""Evenly sample up to n items across the entire list (deterministic), in natural numeric order."""
|
| 190 |
if not paths:
|
| 191 |
return []
|
| 192 |
+
paths = sorted(paths, key=_natural_key) # ensure numeric order first
|
| 193 |
+
total = len(paths)
|
| 194 |
+
n = max(1, min(n, total))
|
| 195 |
+
if n == total:
|
| 196 |
+
return [str(p) for p in paths]
|
| 197 |
+
step = (total - 1) / (n - 1) # cover both ends
|
| 198 |
+
idxs = [round(i * step) for i in range(n)]
|
| 199 |
+
out, seen = [], set()
|
| 200 |
+
for i in idxs:
|
| 201 |
+
if i not in seen:
|
| 202 |
+
out.append(str(paths[int(i)]))
|
| 203 |
+
seen.add(int(i))
|
| 204 |
+
return out
|
| 205 |
|
| 206 |
|
| 207 |
def sanitize_prefix(txt: str) -> str:
|
|
|
|
| 318 |
cmd += ["-frame_pts", "1", out_pattern]
|
| 319 |
return cmd
|
| 320 |
|
| 321 |
+
frames = sorted(raw_dir.glob(f"{prefix}_*.{out_format}"), key=_natural_key)
|
| 322 |
+
gallery = sample_paths(frames, 30)
|
| 323 |
|
| 324 |
def realesrgan(img, model_name, denoise_strength, face_enhance, outscale):
|
| 325 |
if img is None:
|
|
|
|
| 519 |
last_html = render_progress(0.0, f"Extracting… {created} created")
|
| 520 |
ret = proc.wait()
|
| 521 |
|
| 522 |
+
frames = sorted(raw_dir.glob(f"{prefix}_*.{out_format}"), key=_natural_key)
|
|
|
|
| 523 |
try:
|
| 524 |
err = proc.stderr.read() if proc.stderr else ""
|
| 525 |
except Exception:
|
|
|
|
| 554 |
|
| 555 |
def step2_upscale(
|
| 556 |
frames_list: List[str] | None,
|
| 557 |
+
model_name: str,
|
| 558 |
+
scale: int,
|
| 559 |
tile: int,
|
| 560 |
precision: str,
|
| 561 |
prog_html: str,
|
| 562 |
uploaded_imgs: List[gr.File] | None,
|
|
|
|
|
|
|
| 563 |
):
|
| 564 |
|
| 565 |
# decide source: uploaded images take priority, else frames from step 1
|
|
|
|
| 570 |
src_paths = frames_list or []
|
| 571 |
|
| 572 |
if not src_paths:
|
| 573 |
+
yield None, None, "No images provided. Upload files or run Step 1 first.", prog_html
|
| 574 |
+
return
|
| 575 |
|
| 576 |
# Map demo model -> internal, clamp scale to supported values
|
| 577 |
model_id = map_ui_model_to_internal(ui_model_name)
|
|
|
|
| 587 |
total = len(src_paths)
|
| 588 |
done = 0
|
| 589 |
up_paths: List[Path] = []
|
| 590 |
+
|
| 591 |
+
last_pct = -1
|
| 592 |
for fp in src_paths:
|
| 593 |
try:
|
| 594 |
img = Image.open(fp).convert("RGB")
|
|
|
|
| 600 |
except Exception:
|
| 601 |
pass
|
| 602 |
done += 1
|
| 603 |
+
if total:
|
| 604 |
+
pct = int(round((done / total) * 100))
|
| 605 |
+
remaining = max(0, total - done)
|
| 606 |
+
if pct != last_pct:
|
| 607 |
+
label = f"Upscaling… {pct}% · {remaining}/{total} remaining"
|
| 608 |
+
prog_html = render_progress(pct, label)
|
| 609 |
+
# stream progress (gallery/zip stay None until the end)
|
| 610 |
+
yield None, None, label, prog_html
|
| 611 |
+
last_pct = pct
|
| 612 |
|
| 613 |
if not up_paths:
|
| 614 |
+
yield None, None, "Upscaling produced no outputs.", prog_html
|
| 615 |
+
return
|
| 616 |
|
| 617 |
+
# Final outputs
|
| 618 |
gallery = sample_paths(up_paths, 30)
|
| 619 |
zip_path = work / "upscaled.zip"
|
| 620 |
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 621 |
for p in up_paths:
|
| 622 |
zf.write(p, p.name)
|
| 623 |
|
| 624 |
+
final_label = f"Upscaled: {len(up_paths)}"
|
| 625 |
+
yield gallery, str(zip_path), final_label, render_progress(100.0, "Upscaling complete")
|
|
|
|
|
|
|
| 626 |
|
| 627 |
# ───────────────── Encode (Step 3) — supports uploaded frames/ZIP & optional audio source
|
| 628 |
|
|
|
|
| 798 |
zf.write(p, p.name)
|
| 799 |
zip_up = work / "upscaled.zip"
|
| 800 |
with zipfile.ZipFile(zip_up, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 801 |
+
for p in sorted(up_dir.glob("*.jpg"), key=_natural_key):
|
| 802 |
zf.write(p, p.name)
|
| 803 |
|
| 804 |
return str(out_file), str(zip_frames), str(zip_up), "Quick Mode complete.", render_progress(100.0, "All done")
|