|
import os |
|
import cv2 |
|
import time |
|
import torch |
|
import argparse |
|
import insightface |
|
import onnxruntime |
|
import numpy as np |
|
import gradio as gr |
|
from tqdm import tqdm |
|
|
|
from face_swapper import Inswapper, paste_to_whole |
|
from face_analyser import analyse_face |
|
from face_enhancer import load_face_enhancer_model, cv2_interpolations |
|
from utils import create_image_grid |
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Free Face Swapper (Male/Female mode)") |
|
parser.add_argument("--out_dir", default=os.getcwd()) |
|
parser.add_argument("--batch_size", default=32) |
|
parser.add_argument("--cuda", action="store_true", default=False) |
|
user_args = parser.parse_args() |
|
|
|
USE_CUDA = user_args.cuda |
|
DEF_OUTPUT_PATH = user_args.out_dir |
|
BATCH_SIZE = int(user_args.batch_size) |
|
|
|
|
|
|
|
PROVIDER = ["CPUExecutionProvider"] |
|
if USE_CUDA: |
|
if "CUDAExecutionProvider" in onnxruntime.get_available_providers(): |
|
PROVIDER = ["CUDAExecutionProvider", "CPUExecutionProvider"] |
|
print(">>> Running on CUDA") |
|
else: |
|
USE_CUDA = False |
|
print(">>> CUDA not available, running on CPU") |
|
|
|
device = "cuda" if USE_CUDA else "cpu" |
|
EMPTY_CACHE = lambda: torch.cuda.empty_cache() if device == "cuda" else None |
|
|
|
|
|
|
|
FACE_ANALYSER = insightface.app.FaceAnalysis(name="buffalo_l", providers=PROVIDER) |
|
FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640), det_thresh=0.6) |
|
|
|
FACE_SWAPPER = Inswapper( |
|
model_file="./assets/pretrained_models/inswapper_128.onnx", |
|
batch_size=(BATCH_SIZE if USE_CUDA else 1), |
|
providers=PROVIDER, |
|
) |
|
|
|
|
|
|
|
def swap_faces(image_path, male_source_path, female_source_path, face_enhancer_name="NONE"): |
|
start_time = time.time() |
|
|
|
|
|
target = cv2.imread(image_path) |
|
|
|
|
|
analysed_source_male = analyse_face(cv2.imread(male_source_path), FACE_ANALYSER) |
|
analysed_source_female = analyse_face(cv2.imread(female_source_path), FACE_ANALYSER) |
|
|
|
|
|
analysed_faces = FACE_ANALYSER.get(target) |
|
|
|
preds, matrs = [], [] |
|
for analysed_face in tqdm(analysed_faces, desc="Swapping faces"): |
|
if analysed_face["gender"] == 1: |
|
src = analysed_source_male |
|
else: |
|
src = analysed_source_female |
|
|
|
batch_pred, batch_matr = FACE_SWAPPER.get([target], [analysed_face], [src]) |
|
preds.extend(batch_pred) |
|
matrs.extend(batch_matr) |
|
EMPTY_CACHE() |
|
|
|
|
|
for p, m in zip(preds, matrs): |
|
target = paste_to_whole(p, target, m, blend_method="laplacian") |
|
|
|
|
|
if face_enhancer_name != "NONE": |
|
model, runner = load_face_enhancer_model(face_enhancer_name, device=device) |
|
target = runner(target, model) |
|
|
|
elapsed = time.time() - start_time |
|
print(f"✔ Done in {elapsed:.2f} sec") |
|
return target[:, :, ::-1] |
|
|
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("## 🧑➡👩 Face Swapper (Male+Female sources)") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
image_input = gr.Image(label="Target Image", type="filepath") |
|
male_input = gr.Image(label="Source Male", type="filepath") |
|
female_input = gr.Image(label="Source Female", type="filepath") |
|
enhancer = gr.Dropdown( |
|
["NONE"] + cv2_interpolations, label="Face Enhancer", value="NONE" |
|
) |
|
run_btn = gr.Button("✨ Swap") |
|
|
|
with gr.Column(): |
|
output_image = gr.Image(label="Output") |
|
|
|
run_btn.click( |
|
fn=swap_faces, |
|
inputs=[image_input, male_input, female_input, enhancer], |
|
outputs=output_image, |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch() |