faceswap / app.py
plexbazar's picture
Update app.py
1ccca68 verified
Raw
History Blame Contribute Delete
3.99 kB
import gradio as gr
import insightface
from insightface.app import FaceAnalysis
import numpy as np
from PIL import Image
import cv2
import tempfile
import os
MODEL_PATH = "inswapper_128.onnx"
# --------- Load models once ---------
print("Loading models...")
face_app = FaceAnalysis(name="buffalo_l")
face_app.prepare(ctx_id=0, det_size=(640, 640)) # ctx_id=0 -> CPU on Spaces free tier
swapper = insightface.model_zoo.get_model(MODEL_PATH)
print("Models loaded.")
# --------- IMAGE SWAP ---------
def swap_faces_image(source, target):
if source is None or target is None:
raise gr.Error("Please upload both source and target images.")
# Gradio gives numpy arrays (H, W, 3) in RGB
src = np.array(source)
dst = np.array(target)
src_faces = face_app.get(src)
dst_faces = face_app.get(dst)
if len(src_faces) == 0:
raise gr.Error("No face found in source image.")
if len(dst_faces) == 0:
raise gr.Error("No face found in target image.")
src_face = src_faces[0]
dst_face = dst_faces[0]
result = swapper.get(dst.copy(), dst_face, src_face, paste_back=True)
return Image.fromarray(result)
# --------- VIDEO SWAP ---------
def swap_faces_video(source_image, video_file):
if source_image is None:
raise gr.Error("Please upload a source face image.")
if video_file is None:
raise gr.Error("Please upload a target video (mp4).")
# source_image: numpy RGB
src = np.array(source_image)
src_faces = face_app.get(src)
if len(src_faces) == 0:
raise gr.Error("No face found in source image.")
src_face = src_faces[0]
# video_file is a file-like object; get its path
video_path = video_file.name
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise gr.Error("Could not open uploaded video.")
fps = cap.get(cv2.CAP_PROP_FPS) or 25
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# temp output file
tmp_out = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
tmp_out_path = tmp_out.name
tmp_out.close()
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
writer = cv2.VideoWriter(tmp_out_path, fourcc, fps, (w, h))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f"[INFO] Total frames: {total_frames}")
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
frame_idx += 1
if frame_idx % 10 == 0:
print(f"[INFO] Frame {frame_idx}/{total_frames}")
# BGR -> RGB
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
dst_faces = face_app.get(rgb)
if len(dst_faces) > 0:
dst_face = dst_faces[0]
swapped_rgb = swapper.get(rgb.copy(), dst_face, src_face, paste_back=True)
out_frame = cv2.cvtColor(swapped_rgb, cv2.COLOR_RGB2BGR)
writer.write(out_frame)
else:
writer.write(frame)
cap.release()
writer.release()
print(f"[INFO] Video finished: {tmp_out_path}")
return tmp_out_path
# --------- Gradio UI (Image + Video) ---------
with gr.Blocks() as demo:
gr.Markdown("## InsightFace FaceSwap (Image & Video) on HuggingFace")
with gr.Tab("Image Swap"):
src_img = gr.Image(type="numpy", label="Your Face (Source)")
tgt_img = gr.Image(type="numpy", label="Target Face")
out_img = gr.Image(label="Swapped Result")
btn_img = gr.Button("Swap Image")
btn_img.click(fn=swap_faces_image, inputs=[src_img, tgt_img], outputs=out_img)
with gr.Tab("Video Swap"):
src_vid_img = gr.Image(type="numpy", label="Your Face (Source)")
tgt_vid = gr.File(label="Target Video (mp4)")
out_vid = gr.Video(label="Swapped Video")
btn_vid = gr.Button("Swap Video")
btn_vid.click(fn=swap_faces_video, inputs=[src_vid_img, tgt_vid], outputs=out_vid)
if __name__ == "__main__":
demo.launch()