FaceSwapAll22 / app.py
dosesnrolls1's picture
Upload app.py
e2482d4 verified
Raw
History Blame Contribute Delete
10.8 kB
import os
import time
from pathlib import Path
import cv2
import gradio as gr
from optimized_swapper import FaceSwapEngine
ENGINE = FaceSwapEngine()
def _as_index(v, default=1):
try:
return max(1, int(v))
except Exception:
return default
def _file_to_path(item):
if item is None:
return None
if isinstance(item, (str, Path)):
return str(item)
if hasattr(item, "name"):
return item.name
if isinstance(item, dict):
for key in ("path", "name"):
if key in item and item[key]:
return str(item[key])
if isinstance(item, (list, tuple)) and item:
first = item[0]
if isinstance(first, (str, Path)):
return str(first)
if hasattr(first, "name"):
return first.name
return None
def _normalize_video_input(video_value):
if video_value is None:
return None
if isinstance(video_value, (str, Path)):
return str(video_value)
if isinstance(video_value, dict):
if video_value.get("video"):
return str(video_value["video"])
if video_value.get("path"):
return str(video_value["path"])
if isinstance(video_value, (list, tuple)) and video_value:
first = video_value[0]
if isinstance(first, (str, Path)):
return str(first)
if isinstance(first, dict):
return str(first.get("video") or first.get("path") or "") or None
return None
def _eta_text(start_time, progress_value):
elapsed = max(0.001, time.time() - start_time)
if progress_value <= 0:
return f"Elapsed: {elapsed:.1f}s | ETA: estimating..."
remaining = elapsed * (1.0 - progress_value) / progress_value
if remaining >= 60:
return f"Elapsed: {elapsed:.1f}s | ETA: {remaining / 60:.1f} min"
return f"Elapsed: {elapsed:.1f}s | ETA: {remaining:.1f}s"
def _status(progress_value, message, start_time):
pct = int(max(0, min(100, round(progress_value * 100))))
return f"{message}\nProgress: {pct}%\n{_eta_text(start_time, progress_value)}"
def swap_photo(source, source_idx, target, target_idx, det_size, progress=gr.Progress()):
start = time.time()
progress(0.05, desc="Preparing image swap")
if source is None or target is None:
raise gr.Error("Upload both source and target images.")
progress(0.35, desc="Running face detection")
result = ENGINE.swap_image(
source,
_as_index(source_idx),
target,
_as_index(target_idx),
det_size=int(det_size),
)
progress(1.0, desc="Done")
return result, _status(1.0, "Photo swap complete.", start)
def swap_video(source, source_idx, video_path, target_idx, det_size, detection_interval, jpeg_quality, audio, progress=gr.Progress()):
start = time.time()
progress(0.02, desc="Validating inputs")
if source is None:
raise gr.Error("Upload a source image.")
video_path = _normalize_video_input(video_path)
if not video_path:
raise gr.Error("Upload a target video.")
progress(0.10, desc="Preparing source face")
progress(0.20, desc="Starting video processing")
result = ENGINE.swap_video(
source=source,
source_idx=_as_index(source_idx),
video_path=video_path,
target_idx=_as_index(target_idx),
det_size=int(det_size),
detection_interval=max(1, int(detection_interval)),
jpeg_quality=int(jpeg_quality),
preserve_audio=bool(audio),
)
progress(1.0, desc="Video complete")
return result, _status(1.0, "Video swap complete.", start)
def swap_multi_source_single(source_files, target, target_idx, det_size, progress=gr.Progress()):
start = time.time()
if not source_files or target is None:
raise gr.Error("Upload source images and a target image.")
results = []
total = len(source_files)
for i, item in enumerate(source_files, start=1):
progress(((i - 1) / max(total, 1)) * 0.9 + 0.05, desc=f"Processing source {i}/{total}")
path = _file_to_path(item)
if not path:
continue
img = cv2.imread(path)
if img is None:
results.append(f"Error: could not read source image {path}")
continue
try:
result = ENGINE.swap_image(
img,
1,
target,
_as_index(target_idx),
int(det_size),
)
results.append(result)
except Exception as e:
results.append(f"Error: {e}")
if not results:
raise gr.Error("No readable source images were uploaded.")
progress(1.0, desc="Batch complete")
return results, _status(1.0, f"Processed {len(results)} output(s).", start)
def swap_multi_source_multi(source_files, target_files, target_indices, det_size, progress=gr.Progress()):
start = time.time()
if not source_files or not target_files:
raise gr.Error("Upload source and target images.")
indices = [x.strip() for x in str(target_indices).split(",") if x.strip()]
results = []
targets = []
progress(0.05, desc="Loading target images")
for item in target_files:
path = _file_to_path(item)
if not path:
continue
img = cv2.imread(path)
if img is not None:
targets.append(img)
if not targets:
raise gr.Error("No readable target images were uploaded.")
total_jobs = max(1, len(source_files) * len(targets))
done = 0
for src_item in source_files:
src_path = _file_to_path(src_item)
if not src_path:
continue
src = cv2.imread(src_path)
if src is None:
results.append(f"Error: could not read source image {src_path}")
continue
try:
ENGINE.prepare_source(src, 1, int(det_size))
except Exception as e:
results.append(f"Error preparing source {src_path}: {e}")
continue
for j, dst in enumerate(targets):
idx = _as_index(indices[j] if j < len(indices) else 1)
done += 1
progress((done / total_jobs) * 0.95, desc=f"Processing pair {done}/{total_jobs}")
try:
results.append(ENGINE.swap_prepared_source(dst, idx, int(det_size)))
except Exception as e:
results.append(f"Error: {e}")
if not results:
raise gr.Error("No output images were generated.")
progress(1.0, desc="Batch complete")
return results, _status(1.0, f"Processed {len(results)} output(s).", start)
with gr.Blocks(title="Fast Face Swap") as demo:
gr.Markdown(
"# Fast Face Swapping Suite\n"
"CUDA/ONNX Runtime optimized photo and video face swapping with live progress status."
)
with gr.Tab("Single Photo"):
with gr.Row():
with gr.Column():
src = gr.Image(type="numpy", label="Source Image")
src_idx = gr.Number(value=1, precision=0, label="Source Face Index")
with gr.Column():
dst = gr.Image(type="numpy", label="Target Image")
dst_idx = gr.Number(value=1, precision=0, label="Target Face Index")
det_single = gr.Dropdown([256, 320, 384, 512], value=320, label="Detector Size")
btn = gr.Button("Swap", variant="primary")
out = gr.Image(type="numpy", label="Result")
photo_status = gr.Textbox(label="Status / ETA", lines=3, interactive=False)
btn.click(
fn=swap_photo,
inputs=[src, src_idx, dst, dst_idx, det_single],
outputs=[out, photo_status],
show_progress="full",
)
with gr.Tab("Fast Video"):
vsrc = gr.Image(type="numpy", label="Source Image")
vsrc_idx = gr.Number(value=1, precision=0, visible=False, label="Source Face Index")
with gr.Row():
vid = gr.Video(label="Target Video")
vdst_idx = gr.Number(value=1, precision=0, label="Target Face Index")
with gr.Row():
vdet = gr.Dropdown([256, 320, 384, 512], value=320, label="Detector Size")
interval = gr.Slider(
1,
5,
value=1,
step=1,
label="Face detection interval (1 = best tracking accuracy)",
)
quality = gr.Slider(75, 98, value=92, step=1, label="JPEG fallback quality")
audio = gr.Checkbox(value=True, label="Preserve original audio")
vbtn = gr.Button("Swap Video", variant="primary")
vout = gr.Video(label="Output Video")
video_status = gr.Textbox(label="Status / ETA", lines=3, interactive=False)
vbtn.click(
fn=swap_video,
inputs=[vsrc, vsrc_idx, vid, vdst_idx, vdet, interval, quality, audio],
outputs=[vout, video_status],
show_progress="full",
)
with gr.Tab("Multi Source -> Single Target"):
ms = gr.File(file_count="multiple", file_types=["image"], type="filepath", label="Source Images")
md = gr.Image(type="numpy", label="Target Image")
mi = gr.Number(value=1, precision=0, label="Target Face Index")
det_multi_single = gr.Dropdown([256, 320, 384, 512], value=320, label="Detector Size")
mb = gr.Button("Process", variant="primary")
mo = gr.Gallery(label="Results", columns=3)
multi_single_status = gr.Textbox(label="Status / ETA", lines=3, interactive=False)
mb.click(
fn=swap_multi_source_single,
inputs=[ms, md, mi, det_multi_single],
outputs=[mo, multi_single_status],
show_progress="full",
)
with gr.Tab("Multi Source -> Multi Target"):
mss = gr.File(file_count="multiple", file_types=["image"], type="filepath", label="Source Images")
mdd = gr.File(file_count="multiple", file_types=["image"], type="filepath", label="Target Images")
mids = gr.Textbox(value="1", label="Target face indices, comma-separated")
det_multi_multi = gr.Dropdown([256, 320, 384, 512], value=320, label="Detector Size")
mdb = gr.Button("Process", variant="primary")
mdo = gr.Gallery(label="Results", columns=3)
multi_multi_status = gr.Textbox(label="Status / ETA", lines=3, interactive=False)
mdb.click(
fn=swap_multi_source_multi,
inputs=[mss, mdd, mids, det_multi_multi],
outputs=[mdo, multi_multi_status],
show_progress="full",
)
if __name__ == "__main__":
demo.queue(default_concurrency_limit=1, max_size=8).launch(
server_name="0.0.0.0",
server_port=int(os.getenv("PORT", "7860")),
show_error=True,
)