File size: 3,685 Bytes
c59c099
 
 
 
 
8ac49a9
 
 
c59c099
 
 
 
 
 
 
88cff7b
8ac49a9
 
 
88cff7b
8ac49a9
 
 
 
88cff7b
8ac49a9
 
 
 
c59c099
 
 
9d694f4
8ac49a9
 
c59c099
88cff7b
c59c099
8ac49a9
9d694f4
c59c099
8ac49a9
 
 
 
88cff7b
8ac49a9
 
 
 
 
88cff7b
8ac49a9
9d694f4
8ac49a9
c59c099
9d694f4
88cff7b
8ac49a9
88cff7b
8ac49a9
c59c099
8ac49a9
 
 
c59c099
88cff7b
 
c59c099
 
9d694f4
c59c099
8ac49a9
9d694f4
8ac49a9
 
9d694f4
8ac49a9
 
 
9d694f4
 
 
 
 
8ac49a9
9d694f4
 
 
 
 
8ac49a9
 
 
 
 
9d694f4
 
 
 
 
8ac49a9
 
 
9d694f4
8ac49a9
 
 
 
 
 
9d694f4
8ac49a9
 
 
 
 
88cff7b
 
8ac49a9
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
import os
import cv2
import tempfile
import time
import gradio as gr
import insightface
import onnxruntime
from gfpgan import GFPGANer


class Predictor:
    def __init__(self):
        self.setup()

    def setup(self):
        model_dir = "models"

        print("Loading models...")
        self.face_swapper = insightface.model_zoo.get_model(
            f"{model_dir}/inswapper_128.onnx",
            providers=onnxruntime.get_available_providers()
        )

        self.face_enhancer = GFPGANer(
            model_path=f"{model_dir}/GFPGANv1.4.pth",
            upscale=1,
        )

        self.face_analyser = insightface.app.FaceAnalysis(name="buffalo_l")
        self.face_analyser.prepare(ctx_id=0, det_size=(640, 640))

    def get_face(self, img_data):
        """Return the largest detected face"""
        faces = self.face_analyser.get(img_data)
        if len(faces) == 0:
            return None
        return max(faces, key=lambda x: (x.bbox[2] - x.bbox[0]) * (x.bbox[3] - x.bbox[1]))

    def predict(self, target_image, swap_image):
        """Face swap function"""
        try:
            frame = cv2.imread(target_image)
            source = cv2.imread(swap_image)

            if frame is None or source is None:
                return None, "❌ Could not read one of the images."

            target_face = self.get_face(frame)
            source_face = self.get_face(source)

            if target_face is None or source_face is None:
                return None, "❌ Could not detect a clear face in one or both images."

            # Perform face swap
            result = self.face_swapper.get(frame, target_face, source_face, paste_back=True)

            # Enhance with GFPGAN
            _, _, result = self.face_enhancer.enhance(result, paste_back=True)

            # Save result
            out_path = tempfile.mkdtemp() + f"/swapped_{int(time.time())}.jpg"
            cv2.imwrite(out_path, result)

            return out_path, "✅ Face swap completed successfully!"

        except Exception as e:
            print(f"Prediction error: {e}")
            return None, f"❌ Error: {str(e)}"


# Load the predictor once at startup
predictor = Predictor()

# Clean UI without examples
with gr.Blocks(title="Swap Face Model", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 😻 Face Swap Model")
    gr.Markdown("Upload a **target image** and a **face to swap in**. Works best with clear, well-lit frontal faces.")

    with gr.Row():
        with gr.Column():
            target_input = gr.Image(
                type="filepath", 
                label="Target Image (the photo you want to change)", 
                height=450
            )
        with gr.Column():
            swap_input = gr.Image(
                type="filepath", 
                label="Swap Face Image (the face you want to use)", 
                height=450
            )

    with gr.Row():
        swap_btn = gr.Button("🔄 Swap Faces", variant="primary", size="large")

    with gr.Row():
        output_image = gr.Image(
            type="filepath", 
            label="Result", 
            height=500
        )

    status = gr.Textbox(label="Status", interactive=False)

    # Button action
    swap_btn.click(
        fn=predictor.predict,
        inputs=[target_input, swap_input],
        outputs=[output_image, status]
    )

    gr.Markdown("**Tips:**\n• Use high-resolution, well-lit photos\n• Frontal or near-frontal faces work best\n• Results improve with similar lighting and angles.")

if __name__ == "__main__":
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        show_error=True
    )