Fswap2 / app.py
dosesnrolls1's picture
Update app.py
67812a9 verified
Raw
History Blame Contribute Delete
6.48 kB
import os
import torch
print(f"Is CUDA available: {torch.cuda.is_available()}")
# True
print(f"CUDA device: {torch.cuda.get_device_name(torch.cuda.current_device())}")
# Tesla T4import os
import shutil
import subprocess
import tempfile
from pathlib import Path
import cv2
import gradio as gr
import onnxruntime as ort
from huggingface_hub import hf_hub_download
from insightface.app import FaceAnalysis
from insightface.model_zoo import get_model
import tensorflow as tf
print(tf.config.list_physical_devices('GPU'))
# [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
APP_DIR = Path(__file__).resolve().parent
MODELS_DIR = APP_DIR / "models"
TMP_DIR = APP_DIR / "tmp"
MODELS_DIR.mkdir(exist_ok=True)
TMP_DIR.mkdir(exist_ok=True)
MODEL_REPO_ID = os.environ.get("MODEL_REPO_ID", "ezioruan/inswapper_128.onnx")
MODEL_FILENAME = os.environ.get("MODEL_FILENAME", "inswapper_128.onnx")
MODEL_REVISION = os.environ.get("MODEL_REVISION")
HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACEHUB_API_TOKEN")
FACE_MODEL_NAME = os.environ.get("FACE_MODEL_NAME", "buffalo_l")
DETECTION_SIZE = int(os.environ.get("DETECTION_SIZE", "640"))
MAX_FRAMES = int(os.environ.get("MAX_FRAMES", "0"))
face_analyzer = None
face_swapper = None
model_path = None
def ensure_ffmpeg():
if shutil.which("ffmpeg") is None:
raise RuntimeError("ffmpeg is required but not available in PATH.")
def available_providers():
return ort.get_available_providers()
def gpu_ready():
providers = available_providers()
return "CUDAExecutionProvider" in providers, providers
def gpu_guard():
ok, providers = gpu_ready()
if not ok:
raise RuntimeError(
"GPU device not attached. CUDAExecutionProvider is missing. "
f"Available providers: {providers}"
)
def ensure_swap_model() -> str:
global model_path
if model_path and os.path.exists(model_path):
return model_path
local_target = MODELS_DIR / MODEL_FILENAME
if local_target.exists():
model_path = str(local_target)
return model_path
model_path = hf_hub_download(
repo_id=MODEL_REPO_ID,
filename=MODEL_FILENAME,
revision=MODEL_REVISION,
token=HF_TOKEN,
local_dir=str(MODELS_DIR),
)
return model_path
def load_models():
global face_analyzer, face_swapper
gpu_guard()
if face_analyzer is not None and face_swapper is not None:
return face_analyzer, face_swapper
swap_model_path = ensure_swap_model()
providers = ["CUDAExecutionProvider"]
face_analyzer = FaceAnalysis(name=FACE_MODEL_NAME, providers=providers)
face_analyzer.prepare(ctx_id=0, det_size=(DETECTION_SIZE, DETECTION_SIZE))
face_swapper = get_model(swap_model_path, providers=providers)
return face_analyzer, face_swapper
def pick_largest_face(faces):
if not faces:
return None
return max(faces, key=lambda f: (f.bbox[2] - f.bbox[0]) * (f.bbox[3] - f.bbox[1]))
def extract_audio(input_video: str, audio_path: str):
subprocess.run(["ffmpeg", "-y", "-i", input_video, "-vn", "-acodec", "copy", audio_path], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def mux_audio(video_path: str, audio_path: str, output_path: str):
if not os.path.exists(audio_path):
shutil.move(video_path, output_path)
return
subprocess.run(["ffmpeg", "-y", "-i", video_path, "-i", audio_path, "-c:v", "copy", "-c:a", "aac", "-shortest", output_path], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
os.remove(video_path)
def swap_frame(frame_bgr, source_face, analyzer, swapper):
target_faces = analyzer.get(frame_bgr)
if not target_faces:
return frame_bgr
result = frame_bgr.copy()
for target_face in target_faces:
result = swapper.get(result, target_face, source_face, paste_back=True)
return result
def startup_status():
ok, providers = gpu_ready()
return f"GPU ready: {ok} | Providers: {providers}"
def process_video(face_image_path, video_path, progress=gr.Progress(track_tqdm=False)):
gpu_guard()
ensure_ffmpeg()
analyzer, swapper = load_models()
src = cv2.imread(face_image_path)
if src is None:
raise ValueError("Could not read the source face image.")
source_face = pick_largest_face(analyzer.get(src))
if source_face is None:
raise ValueError("No source face detected in the uploaded image.")
work_dir = Path(tempfile.mkdtemp(dir=TMP_DIR))
silent_video = str(work_dir / "silent.mp4")
audio_file = str(work_dir / "audio.aac")
final_video = str(work_dir / "faceswapped.mp4")
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError("Could not open the uploaded video.")
fps = cap.get(cv2.CAP_PROP_FPS) or 24.0
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or 1
limit = min(frame_count, MAX_FRAMES) if MAX_FRAMES > 0 else frame_count
writer = cv2.VideoWriter(silent_video, cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height))
extract_audio(video_path, audio_file)
idx = 0
while idx < limit:
ok, frame = cap.read()
if not ok:
break
writer.write(swap_frame(frame, source_face, analyzer, swapper))
idx += 1
progress(idx / max(limit, 1), desc=f"Processed {idx}/{limit} frames")
cap.release()
writer.release()
mux_audio(silent_video, audio_file, final_video)
return final_video, f"Done. Processed {idx} frame(s)."
with gr.Blocks(title="Video FaceSwap GPU Only") as demo:
gr.Markdown("""# Video FaceSwap GPU Only
This Space only works when a real GPU is attached. If CUDA is missing, processing is blocked.""")
gr.Textbox(label="GPU status", value=startup_status(), interactive=False)
with gr.Row():
face_image = gr.Image(type="filepath", label="Source face image")
target_video = gr.Video(label="Target video")
run_btn = gr.Button("Run face swap", variant="primary")
output_video = gr.Video(label="Output video")
status = gr.Textbox(label="Status")
run_btn.click(fn=process_video, inputs=[face_image, target_video], outputs=[output_video, status], api_name="faceswap_video")
if __name__ == "__main__":
demo.queue(max_size=8).launch(server_name="0.0.0.0", server_port=7860, share=False)