minhho's picture
fix: corrupted emoji character in app.py
87ffaaf
ο»Ώ# -*- coding: utf-8 -*-
import gradio as gr
from refacer import Refacer
import os
# Configuration
MAX_NUM_FACES = int(os.environ.get("MAX_NUM_FACES", "5"))
FORCE_CPU = os.environ.get("FORCE_CPU", "False").lower() == "true"
# Initialize the face swapper
print("Initializing FaceSwapLite...")
refacer = Refacer(force_cpu=FORCE_CPU, colab_performance=False)
print("Initialization complete!")
num_faces = MAX_NUM_FACES
def run(*vars):
try:
video_path = vars[0]
origins = vars[1:(num_faces+1)]
destinations = vars[(num_faces+1):(num_faces*2)+1]
thresholds = vars[(num_faces*2)+1:]
# Debug: print what we received
print(f"Debug - Video path: {video_path}")
print(f"Debug - Origins: {[type(o).__name__ if o is not None else 'None' for o in origins]}")
print(f"Debug - Destinations: {[type(d).__name__ if d is not None else 'None' for d in destinations]}")
# Validate inputs
if video_path is None:
raise ValueError("Please upload a target video")
faces = []
for k in range(0, num_faces):
# Debug each face pair
print(f"Debug - Face #{k+1}: origin={type(origins[k]).__name__ if origins[k] is not None else 'None'}, dest={type(destinations[k]).__name__ if destinations[k] is not None else 'None'}")
import numpy as np
from PIL import Image
# SIMPLE MODE: Only destination (source face) is required
# If no origin is specified, it will replace the FIRST/ANY face in the video
if destinations[k] is not None:
dest_img = destinations[k]
# Handle destination image
if isinstance(dest_img, np.ndarray):
print(f"Debug - Dest is numpy array, shape: {dest_img.shape}")
elif isinstance(dest_img, Image.Image):
print(f"Debug - Dest is PIL Image, converting...")
dest_img = np.array(dest_img)
elif isinstance(dest_img, str):
print(f"Debug - Dest is file path: {dest_img}")
import cv2
dest_img = cv2.imread(dest_img)
dest_img = cv2.cvtColor(dest_img, cv2.COLOR_BGR2RGB)
# Handle origin image (optional for simple mode)
origin_img = None
if origins[k] is not None:
origin_img = origins[k]
if isinstance(origin_img, np.ndarray):
print(f"Debug - Origin is numpy array, shape: {origin_img.shape}")
elif isinstance(origin_img, Image.Image):
print(f"Debug - Origin is PIL Image, converting...")
origin_img = np.array(origin_img)
elif isinstance(origin_img, str):
print(f"Debug - Origin is file path: {origin_img}")
import cv2
origin_img = cv2.imread(origin_img)
origin_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2RGB)
# Add face swap configuration
if origin_img is not None:
# SPECIFIC MODE: Replace a specific face
faces.append({
'origin': origin_img,
'destination': dest_img,
'threshold': thresholds[k]
})
print(f"βœ… Added SPECIFIC face swap #{k+1} (will match target face)")
else:
# SIMPLE MODE: Replace first/any face
faces.append({
'destination': dest_img,
'threshold': thresholds[k]
})
print(f"βœ… Added SIMPLE face swap #{k+1} (will replace first face in video)")
# Validate that at least one source face is provided
if len(faces) == 0:
raise ValueError(
"Please provide at least one Source Face (πŸ“Έ):\n\n"
"1. Go to 'Face #1' tab\n"
"2. Upload a Source Face (πŸ“Έ) - the face you want to INSERT into the video\n"
"3. (Optional) Upload Target Face (🎯) if you want to replace a specific face\n"
"4. Click 'Start processing'\n\n"
"πŸ’‘ Tip: If you only upload the Source Face, it will replace the first/main face in the video!"
)
print(f"Processing video with {len(faces)} face swap(s)...")
result = refacer.reface(video_path, faces)
print(f"βœ… Face swap completed successfully!")
return result
except Exception as e:
error_msg = f"❌ Error: {str(e)}"
print(error_msg)
import traceback
traceback.print_exc()
raise gr.Error(error_msg)
origin = []
destination = []
thresholds = []
with gr.Blocks(title="FaceSwap Lite") as demo:
with gr.Row():
gr.Markdown(
"""# πŸŽƒ FaceSwap Lite πŸŽƒ
**AI-Powered Face Swapping for Videos - Professional Quality**
### πŸ“ Quick Start Guide:
1. **Upload Video** ⬇️ Upload your target video (MP4 recommended, 10-30 seconds for best results)
2. **Go to "Face #1" tab** ⬇️ Upload **Source Face** (πŸ“Έ):
- πŸ“Έ **Source Face** = The face you want to INSERT into the video (REQUIRED)
- 🎯 **Target Face** = OPTIONAL - only if you want to replace a specific face
3. **Click "Start processing"** ⬇️ Wait for the AI to work its magic!
4. **Download Result** ⬇️ Get your face-swapped video!
---
✨ **Quality Enhancements Active:**
- 🎨 Smart color matching (subtle lighting adjustment)
- πŸ” Detail preservation (maintains sharpness)
- 🎬 Temporal smoothing (eliminates frame jitter)
- 🎯 Advanced face tracking (stable during motion)
πŸ’‘ **Tip**: For most users, just upload the video and ONE Source Face image!
The app will automatically replace the first/main face in the video.
"""
)
with gr.Row():
video=gr.Video(label=u"πŸ–₯️ Target Video (upload MP4)", format="mp4")
video2=gr.Video(label=u"🎞️ Result Video (download after processing)", interactive=False, format="mp4")
for i in range(0,num_faces):
with gr.Tab(u"Face #" + f"{i+1}"):
with gr.Row():
gr.Markdown(
f"""
### Face Swap Pair #{i+1}
Upload **BOTH** images below:
"""
)
with gr.Row():
origin.append(gr.Image(
label=u"🎯 Target Face (OPTIONAL) - Specific face to replace from video",
type="numpy",
placeholder="Leave empty to replace first/any face"
))
destination.append(gr.Image(
label=u"πŸ“Έ Source Face (REQUIRED) - Face to INSERT into video",
type="numpy",
placeholder="Upload the replacement face"
))
with gr.Row():
thresholds.append(gr.Slider(
label=u"Match Threshold (Recommended: 0.5 for stability)",
minimum=0.0,
maximum=1.0,
value=0.5,
step=0.05,
info="Lower = more stable during fast motion | Higher = stricter matching"
))
with gr.Row():
button=gr.Button(u"⏳ Start processing", variant="primary")
button.click(fn=run,inputs=[video]+origin+destination+thresholds,outputs=[video2])
# Launch the Gradio app
demo.queue().launch()