Spaces:
Paused
Paused
File size: 6,483 Bytes
67812a9 71694c3 242c739 9bd2cd2 242c739 f63630b 242c739 97f568b 9bd2cd2 97f568b 5273177 97f568b 5273177 97f568b 5273177 97f568b 242c739 97f568b 242c739 97f568b 242c739 97f568b 242c739 97f568b 242c739 97f568b 5273177 97f568b 9bd2cd2 97f568b 242c739 97f568b 242c739 97f568b 242c739 97f568b 5273177 97f568b 242c739 9bd2cd2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | 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)
|