File size: 4,677 Bytes
0dd4bc4
 
919da89
 
 
 
 
 
 
 
0dd4bc4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919da89
 
0dd4bc4
919da89
0dd4bc4
 
 
919da89
 
 
 
 
 
0dd4bc4
 
 
 
919da89
 
0dd4bc4
 
919da89
 
0dd4bc4
919da89
 
0dd4bc4
919da89
 
0dd4bc4
919da89
 
0dd4bc4
 
 
 
 
 
 
 
 
 
 
919da89
0dd4bc4
 
 
919da89
0dd4bc4
 
 
 
 
919da89
 
 
0dd4bc4
 
 
 
 
 
 
 
 
919da89
0dd4bc4
 
 
 
 
 
 
 
 
 
 
 
919da89
 
0dd4bc4
919da89
 
 
 
 
0dd4bc4
919da89
0dd4bc4
 
 
 
 
 
 
 
 
 
919da89
 
0dd4bc4
 
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
# app.py - Interface principale Face Swap (Hugging Face Space safe)

import gradio as gr
import cv2
import insightface
from insightface.app import FaceAnalysis
from insightface.model_zoo import get_model
import numpy as np
import os

# ===============================
# 1️⃣ Initialisation FaceAnalysis
# ===============================

app = FaceAnalysis(name="buffalo_l")

# GPU si dispo, sinon CPU
try:
    app.prepare(ctx_id=0, det_size=(640, 640))
    print("✅ FaceAnalysis chargé avec GPU")
except Exception:
    app.prepare(ctx_id=-1, det_size=(640, 640))
    print("⚠️ GPU indisponible → fallback CPU")

# ===============================
# 2️⃣ Téléchargement sécurisé du modèle inswapper
# ===============================

MODEL_PATH = "inswapper_128.onnx"
HF_URL = (
    "https://huggingface.co/ezioruan/inswapper_128.onnx/"
    "resolve/main/inswapper_128.onnx"
)
MIN_BYTES = 200 * 1024 * 1024  # 200 MB minimum (sécurité)

def ensure_inswapper(path: str):
    # Fichier existant mais trop petit = corrompu
    if os.path.exists(path) and os.path.getsize(path) < MIN_BYTES:
        print(f"⚠️ {path} corrompu → suppression")
        os.remove(path)

    # Télécharger si absent
    if not os.path.exists(path):
        print("⬇️ Téléchargement de inswapper_128.onnx depuis Hugging Face...")
        cmd = (
            "wget -L --retry-connrefused --waitretry=1 "
            "--tries=5 --timeout=30 "
            f"-O {path} '{HF_URL}'"
        )
        code = os.system(cmd)
        if code != 0:
            raise RuntimeError("❌ Échec du téléchargement de inswapper_128.onnx")

    # Vérification finale
    size = os.path.getsize(path)
    if size < MIN_BYTES:
        raise RuntimeError(
            f"❌ inswapper_128.onnx invalide (taille {size} bytes)"
        )

    print(f"✅ inswapper_128.onnx prêt ({size // (1024*1024)} MB)")

ensure_inswapper(MODEL_PATH)

# Charger le modèle de swap
swapper = get_model(MODEL_PATH, download=False, download_zip=False)

# ===============================
# 3️⃣ Fonction de face swap
# ===============================

def swap_face(source_img, target_img):
    """
    Swap le visage de source_img vers target_img
    """
    try:
        if source_img is None or target_img is None:
            return None, "❌ Merci de charger deux images"

        # Convertir PIL → OpenCV (BGR)
        source = cv2.cvtColor(np.array(source_img), cv2.COLOR_RGB2BGR)
        target = cv2.cvtColor(np.array(target_img), cv2.COLOR_RGB2BGR)

        # Détection des visages
        source_faces = app.get(source)
        target_faces = app.get(target)

        if len(source_faces) == 0:
            return None, "❌ Aucun visage détecté dans l'image source"

        if len(target_faces) == 0:
            return None, "❌ Aucun visage détecté dans l'image cible"

        source_face = source_faces[0]
        result = target.copy()

        # Swap sur chaque visage cible
        for face in target_faces:
            result = swapper.get(
                result,
                face,
                source_face,
                paste_back=True
            )

        # OpenCV → RGB
        result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)

        return result_rgb, "✅ Face swap réussi"

    except Exception as e:
        return None, f"❌ Erreur : {str(e)}"

# ===============================
# 4️⃣ Interface Gradio
# ===============================

with gr.Blocks(title="Face Swapper Pro", theme=gr.themes.Soft()) as demo:
    gr.Markdown("""
# 🎭 Face Swapper Pro
### Échangez les visages entre deux images

**Instructions :**
1. Image source = visage à utiliser  
2. Image cible = visage(s) à remplacer  
3. Cliquez sur **Swap Faces**
""")

    with gr.Row():
        source_image = gr.Image(
            label="📸 Image source",
            type="pil",
            height=400
        )
        target_image = gr.Image(
            label="🎯 Image cible",
            type="pil",
            height=400
        )

    swap_btn = gr.Button("🔄 Swap Faces", variant="primary")
    status_text = gr.Textbox(label="Statut", interactive=False)
    result_image = gr.Image(label="✨ Résultat", height=500)

    swap_btn.click(
        fn=swap_face,
        inputs=[source_image, target_image],
        outputs=[result_image, status_text]
    )

    gr.Markdown("""
---
💡 **Conseils**
- Visages bien éclairés
- Angle similaire = meilleur rendu
- Fonctionne avec plusieurs visages sur la cible
""")

# ===============================
# 5️⃣ Lancement
# ===============================

if __name__ == "__main__":
    demo.launch()