Spaces:
Runtime error
Runtime error
| ο»Ώ# -*- 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() | |