Update app.py
Browse files
app.py
CHANGED
|
@@ -532,6 +532,94 @@ def step1_extract(
|
|
| 532 |
|
| 533 |
# ───────────────── Upscale (Step 2) — supports uploaded images OR frames from Step 1
|
| 534 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 535 |
def save_uploaded_images(files: List[gr.File] | None, prefix: str = "upload") -> Tuple[List[Path], Path]:
|
| 536 |
tmp = Path(tempfile.mkdtemp(prefix="imgup_"))
|
| 537 |
in_dir = tmp / "input"; in_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -874,7 +962,12 @@ def build_ui():
|
|
| 874 |
frames_dir_state = gr.State("") # str
|
| 875 |
prefix_state = gr.State("") # str
|
| 876 |
fps_state = gr.State(30.0) # float
|
| 877 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 878 |
with gr.Tabs():
|
| 879 |
# STEP 1
|
| 880 |
with gr.Tab("Step 1 · Extract Frames"):
|
|
@@ -970,37 +1063,44 @@ def build_ui():
|
|
| 970 |
value="RealESRGAN_x4plus",
|
| 971 |
show_label=True
|
| 972 |
)
|
| 973 |
-
denoise_strength = gr.Slider(
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
)
|
| 977 |
-
outscale = gr.Slider(
|
| 978 |
-
label="Resolution upscale",
|
| 979 |
-
minimum=1, maximum=6, step=1, value=4, show_label=True
|
| 980 |
-
)
|
| 981 |
-
face_enhance = gr.Checkbox(label="Face Enhancement (GFPGAN)", value=False)
|
| 982 |
|
| 983 |
with gr.Row():
|
| 984 |
-
tile = gr.Number(value=
|
| 985 |
precision = gr.Dropdown(["auto", "half", "full"], value="auto", label="Precision (GPU=half, CPU=full)")
|
| 986 |
with gr.Row():
|
| 987 |
-
batch_size = gr.Number(value=
|
| 988 |
max_images = gr.Number(value=0, precision=0, label="Max images to process (0 = all)")
|
| 989 |
-
|
| 990 |
with gr.Row():
|
| 991 |
-
|
|
|
|
| 992 |
|
| 993 |
prog2 = gr.HTML(render_progress(0.0, "Idle"))
|
| 994 |
gallery_up = gr.Gallery(label="Upscaled preview (30 sampled)", columns=6, height=480)
|
| 995 |
zip_up = gr.File(label="Download upscaled ZIP")
|
| 996 |
details2 = gr.Markdown("")
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1002 |
)
|
| 1003 |
|
|
|
|
| 1004 |
# STEP 3 — Re-encode
|
| 1005 |
with gr.Tab("Step 3 · Re-encode Video"):
|
| 1006 |
gr.Markdown("Use frames from Step 1 **or** upload a frames ZIP / images. Optionally provide a video for audio track.")
|
|
|
|
| 532 |
|
| 533 |
# ───────────────── Upscale (Step 2) — supports uploaded images OR frames from Step 1
|
| 534 |
|
| 535 |
+
# Manual-batch Step 2 helpers (resumable, click-to-advance)
|
| 536 |
+
def _ensure_dir(p: Path) -> Path:
|
| 537 |
+
p.mkdir(parents=True, exist_ok=True)
|
| 538 |
+
return p
|
| 539 |
+
|
| 540 |
+
def _save_zip_of_dir(dir_path: Path, zip_path: Path) -> str:
|
| 541 |
+
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 542 |
+
for p in sorted(dir_path.glob("*.*"), key=_natural_key):
|
| 543 |
+
if p.suffix.lower() in [".jpg", ".jpeg", ".png"]:
|
| 544 |
+
zf.write(p, p.name)
|
| 545 |
+
return str(zip_path)
|
| 546 |
+
|
| 547 |
+
def _list_image_paths_from_upload(files: List[gr.File] | None) -> List[str]:
|
| 548 |
+
if not files: return []
|
| 549 |
+
return [str(Path(f.name)) for f in files if Path(f.name).suffix.lower() in [".jpg",".jpeg",".png"]]
|
| 550 |
+
|
| 551 |
+
def _build_gallery_from_dir(dir_path: Path, n: int = 30) -> List[str]:
|
| 552 |
+
paths = sorted(list(dir_path.glob("*.jpg")) + list(dir_path.glob("*.png")), key=_natural_key)
|
| 553 |
+
return sample_paths(paths, n)
|
| 554 |
+
|
| 555 |
+
def step2_prepare_sources(frames_list, uploaded_imgs, max_images):
|
| 556 |
+
src = _list_image_paths_from_upload(uploaded_imgs) or (frames_list or [])
|
| 557 |
+
if not src:
|
| 558 |
+
return [], "", 0, 0, "No images found. Upload files or run Step 1 first.", render_progress(0.0, "Idle")
|
| 559 |
+
try:
|
| 560 |
+
max_images = int(max_images or 0)
|
| 561 |
+
except Exception:
|
| 562 |
+
max_images = 0
|
| 563 |
+
if max_images > 0:
|
| 564 |
+
src = src[:max_images]
|
| 565 |
+
work = Path(tempfile.mkdtemp(prefix="up_manual_"))
|
| 566 |
+
out_dir = _ensure_dir(work / "upscaled")
|
| 567 |
+
total = len(src)
|
| 568 |
+
done_idx = 0
|
| 569 |
+
msg = f"Sources loaded: {total} image(s). Click 'Process Next Batch' to start."
|
| 570 |
+
prog = render_progress(0.0, "Ready")
|
| 571 |
+
return src, str(out_dir), done_idx, total, msg, prog
|
| 572 |
+
|
| 573 |
+
def step2_process_next_batch(
|
| 574 |
+
up_src_paths, up_out_dir, up_done_idx, up_total,
|
| 575 |
+
ui_model_name, outscale, tile, precision, denoise_strength, face_enhance, batch_size,
|
| 576 |
+
):
|
| 577 |
+
if not up_src_paths or not up_out_dir:
|
| 578 |
+
return None, None, "Load sources first.", render_progress(0.0, "Idle"), up_done_idx, up_out_dir
|
| 579 |
+
model_id = map_ui_model_to_internal(ui_model_name)
|
| 580 |
+
scale = clamp_scale_for_model(int(outscale or 4), model_id)
|
| 581 |
+
device = "cuda" if os.environ.get("CUDA_VISIBLE_DEVICES") else "cpu"
|
| 582 |
+
half = (precision == "half") and (device == "cuda")
|
| 583 |
+
tile = int(tile or 256)
|
| 584 |
+
batch_size = max(1, int(batch_size or 8))
|
| 585 |
+
upsampler = get_realesrganer(model_id, scale, tile, half, device=device)
|
| 586 |
+
|
| 587 |
+
start = int(up_done_idx or 0)
|
| 588 |
+
end = min(start + batch_size, int(up_total or 0))
|
| 589 |
+
out_dir = Path(up_out_dir)
|
| 590 |
+
|
| 591 |
+
if start >= up_total:
|
| 592 |
+
gallery = _build_gallery_from_dir(out_dir, 30)
|
| 593 |
+
zip_path = Path(out_dir.parent) / "upscaled.zip"
|
| 594 |
+
zip_file = _save_zip_of_dir(out_dir, zip_path)
|
| 595 |
+
prog = render_progress(100.0, "All images processed")
|
| 596 |
+
details = f"Done. Total upscaled: {len(list(out_dir.glob('*.jpg')))+len(list(out_dir.glob('*.png')))}"
|
| 597 |
+
return gallery, zip_file, details, prog, start, up_out_dir
|
| 598 |
+
|
| 599 |
+
processed_now = 0
|
| 600 |
+
for fp in up_src_paths[start:end]:
|
| 601 |
+
try:
|
| 602 |
+
with Image.open(fp) as im:
|
| 603 |
+
img = im.convert("RGB")
|
| 604 |
+
output, _ = upsampler.enhance(np.array(img), outscale=scale)
|
| 605 |
+
Image.fromarray(output).save(out_dir / (Path(fp).stem + ".jpg"), quality=95)
|
| 606 |
+
except Exception:
|
| 607 |
+
pass
|
| 608 |
+
processed_now += 1
|
| 609 |
+
|
| 610 |
+
next_idx = end
|
| 611 |
+
pct = int(round((next_idx / up_total) * 100)) if up_total else 0
|
| 612 |
+
label = (f"Processed {processed_now} image(s) this batch. "
|
| 613 |
+
f"{next_idx}/{up_total} done (x{scale}, model={ui_model_name}).")
|
| 614 |
+
prog = render_progress(pct, f"Upscaling… {pct}%")
|
| 615 |
+
|
| 616 |
+
gallery = _build_gallery_from_dir(out_dir, 30)
|
| 617 |
+
zip_path = Path(out_dir.parent) / "upscaled.zip"
|
| 618 |
+
zip_file = _save_zip_of_dir(out_dir, zip_path)
|
| 619 |
+
return gallery, zip_file, label, prog, next_idx, up_out_dir
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
|
| 623 |
def save_uploaded_images(files: List[gr.File] | None, prefix: str = "upload") -> Tuple[List[Path], Path]:
|
| 624 |
tmp = Path(tempfile.mkdtemp(prefix="imgup_"))
|
| 625 |
in_dir = tmp / "input"; in_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 962 |
frames_dir_state = gr.State("") # str
|
| 963 |
prefix_state = gr.State("") # str
|
| 964 |
fps_state = gr.State(30.0) # float
|
| 965 |
+
# Shared Step 2 states (manual batching)
|
| 966 |
+
up_src_paths_state = gr.State([]) # list[str] absolute paths to process
|
| 967 |
+
up_out_dir_state = gr.State("") # str: output dir path
|
| 968 |
+
up_done_idx_state = gr.State(0) # int: next index to start from
|
| 969 |
+
up_total_state = gr.State(0) # int: total images
|
| 970 |
+
|
| 971 |
with gr.Tabs():
|
| 972 |
# STEP 1
|
| 973 |
with gr.Tab("Step 1 · Extract Frames"):
|
|
|
|
| 1063 |
value="RealESRGAN_x4plus",
|
| 1064 |
show_label=True
|
| 1065 |
)
|
| 1066 |
+
denoise_strength = gr.Slider(0, 1, value=0.5, step=0.1, label="Denoise (only general-x4v3)")
|
| 1067 |
+
outscale = gr.Slider(1, 6, value=4, step=1, label="Resolution upscale")
|
| 1068 |
+
face_enhance = gr.Checkbox(value=False, label="Face Enhancement (GFPGAN)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1069 |
|
| 1070 |
with gr.Row():
|
| 1071 |
+
tile = gr.Number(value=256, label="Tile size (try 128 if OOM; 0=auto)")
|
| 1072 |
precision = gr.Dropdown(["auto", "half", "full"], value="auto", label="Precision (GPU=half, CPU=full)")
|
| 1073 |
with gr.Row():
|
| 1074 |
+
batch_size = gr.Number(value=12, precision=0, label="Batch size per click")
|
| 1075 |
max_images = gr.Number(value=0, precision=0, label="Max images to process (0 = all)")
|
| 1076 |
+
|
| 1077 |
with gr.Row():
|
| 1078 |
+
btn_prepare = gr.Button("Step 2: Load / Reset Sources", variant="secondary")
|
| 1079 |
+
btn_next = gr.Button("Process Next Batch", variant="primary")
|
| 1080 |
|
| 1081 |
prog2 = gr.HTML(render_progress(0.0, "Idle"))
|
| 1082 |
gallery_up = gr.Gallery(label="Upscaled preview (30 sampled)", columns=6, height=480)
|
| 1083 |
zip_up = gr.File(label="Download upscaled ZIP")
|
| 1084 |
details2 = gr.Markdown("")
|
| 1085 |
+
|
| 1086 |
+
# 1) load/reset sources
|
| 1087 |
+
btn_prepare.click(
|
| 1088 |
+
step2_prepare_sources,
|
| 1089 |
+
inputs=[frames_state, imgs_override, max_images],
|
| 1090 |
+
outputs=[up_src_paths_state, up_out_dir_state, up_done_idx_state, up_total_state, details2, prog2]
|
| 1091 |
+
)
|
| 1092 |
+
|
| 1093 |
+
# 2) process one batch per click
|
| 1094 |
+
btn_next.click(
|
| 1095 |
+
step2_process_next_batch,
|
| 1096 |
+
inputs=[
|
| 1097 |
+
up_src_paths_state, up_out_dir_state, up_done_idx_state, up_total_state,
|
| 1098 |
+
ui_model_name, outscale, tile, precision, denoise_strength, face_enhance, batch_size
|
| 1099 |
+
],
|
| 1100 |
+
outputs=[gallery_up, zip_up, details2, prog2, up_done_idx_state, up_out_dir_state]
|
| 1101 |
)
|
| 1102 |
|
| 1103 |
+
|
| 1104 |
# STEP 3 — Re-encode
|
| 1105 |
with gr.Tab("Step 3 · Re-encode Video"):
|
| 1106 |
gr.Markdown("Use frames from Step 1 **or** upload a frames ZIP / images. Optionally provide a video for audio track.")
|