Salman Alfarisi commited on
Commit
6cc375a
·
0 Parent(s):

deploy: clean Space build without binary examples

Browse files
Files changed (6) hide show
  1. .flake8 +11 -0
  2. LICENSE +22 -0
  3. README.md +98 -0
  4. app.py +70 -0
  5. requirements.txt +5 -0
  6. swap.py +100 -0
.flake8 ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [flake8]
2
+ max-line-length = 88
3
+
4
+ # Directories and files to skip
5
+ exclude =
6
+ .venv,
7
+ venv,
8
+ env,
9
+ __pycache__,
10
+ .git,
11
+ .github
LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 [Salman Alfarisi]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
README.md ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ sdk_version: gradio
3
+ app_file: app.py
4
+ ---
5
+
6
+ # Simple Face-Swap App
7
+ [![CI](https://github.com/salmanalfarisi11/reface-app-simple/actions/workflows/ci.yml/badge.svg)](https://github.com/salmanalfarisi11/reface-app-simple/actions)
8
+ [![Live Demo](https://img.shields.io/badge/demo-Hugging%20Face-blue)](https://huggingface.co/spaces/salman555/reface-app-simple)
9
+
10
+ > A high-performance, 128 px face-swap prototype built with ONNX & GPU acceleration, designed for AI practitioners and developers.
11
+
12
+ ---
13
+
14
+ ## 📖 Overview
15
+
16
+ This repository provides a streamlined pipeline for swapping faces in still images using a 128×128 px ONNX model and the InsightFace framework. The design prioritizes:
17
+
18
+ - **Accuracy**: Leveraging InsightFace’s state-of-the-art face detector and embedding model.
19
+ - **Performance**: Native ONNXRuntime GPU support for real-time inference.
20
+ - **Usability**: Simple CLI and web UI via Gradio for drag-and-drop interaction.
21
+
22
+ ---
23
+
24
+ ## 🚀 Features
25
+
26
+ - **CLI Interface**: `swap.py` for scripted batch processing.
27
+ - **Web Demo**: `gradio_app.py` enables drag-and-drop face-swap in your browser.
28
+ - **Cross-Platform**: Pure Python 3.13 compatibility on Linux, macOS, and Windows.
29
+ - **Container-Ready**: Minimal dependencies for Docker or cloud deployment.
30
+
31
+ ---
32
+
33
+ ## 🛠️ Tech Stack
34
+
35
+ - **Python 3.13**
36
+ - **InsightFace** (buffalo_l detector + INSwapper ONNX)
37
+ - **ONNXRuntime-GPU** for hardware acceleration
38
+ - **OpenCV** for image I/O and post-processing
39
+ - **Gradio** for interactive web UI
40
+
41
+ ---
42
+
43
+ ## 📥 Installation
44
+
45
+ 1. **Clone & enter** the project directory
46
+ ```bash
47
+ git clone https://github.com/username/reface-app-simple.git
48
+ cd reface-app-simple
49
+ ```
50
+
51
+ 2. **Create & activate** a virtual environment
52
+ ```bash
53
+ python3 -m venv .venv && source .venv/bin/activate
54
+ ```
55
+
56
+ 3. **Install dependencies**
57
+ ```bash
58
+ pip install --upgrade pip
59
+ pip install -r requirements.txt
60
+ ```
61
+
62
+ 4. **Download ONNX model**
63
+ ```bash
64
+ mkdir -p ~/.insightface/models
65
+ wget -O ~/.insightface/models/inswapper_128.onnx \ https://github.com/deepinsight/insightface/releases/download/v0.7/inswapper_128.onnx
66
+ ```
67
+
68
+ ## ⚙️ Usage
69
+
70
+ ### Command-Line
71
+ ```bash
72
+ python swap_128.py \
73
+ --src path/to/your_photo.jpg \
74
+ --dst path/to/target.jpg \
75
+ --out result.png \
76
+ --device gpu
77
+ ```
78
+
79
+ ### WEB-UI
80
+ ```bash
81
+ python gradio_app.py
82
+ ```
83
+ - Open your browser at http://localhost:7860, drag-and-drop two images, and view the swapped result in real time.
84
+
85
+ ## Contributing
86
+
87
+ Contributions are welcome! Please:
88
+
89
+ 1. Fork the repository.
90
+ 2. Create a feature branch (git checkout -b feature/YourFeature).
91
+ 3. Commit your changes (git commit -m "feat: your feature").
92
+ 4. Open a Pull Request and reference any related issues.
93
+ We follow the Contributor Covenant code of conduct.
94
+ ---
95
+
96
+ ## License
97
+
98
+ This project is licensed under the **MIT License**. See `LICENSE` for details.
app.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ gradio_app.py — Drag-and-drop 128px face-swap web UI with Gradio
4
+ Usage:
5
+ python gradio_app.py
6
+ """
7
+ import os
8
+ import numpy as np
9
+ import cv2
10
+ import gradio as gr
11
+
12
+
13
+ def get_models():
14
+ import onnxruntime as ort
15
+ from insightface.app import FaceAnalysis
16
+ from insightface.model_zoo.inswapper import INSwapper
17
+
18
+ providers = ["CUDAExecutionProvider", "CPUExecutionProvider"]
19
+
20
+ app = FaceAnalysis(name="buffalo_l", providers=providers)
21
+ app.prepare(ctx_id=0, det_size=(640, 640))
22
+
23
+ model_path = os.path.expanduser(
24
+ "~/.insightface/models/inswapper_128.onnx"
25
+ )
26
+ if not os.path.isfile(model_path):
27
+ raise FileNotFoundError(f"Model not found: {model_path}")
28
+
29
+ session = ort.InferenceSession(model_path, providers=providers)
30
+ swapper = INSwapper(model_file=model_path, session=session)
31
+
32
+ return app, swapper
33
+
34
+
35
+ def swap_fn(src, dst):
36
+ app, swapper = get_models()
37
+
38
+ src_img = cv2.cvtColor(np.array(src), cv2.COLOR_RGB2BGR)
39
+ dst_img = cv2.cvtColor(np.array(dst), cv2.COLOR_RGB2BGR)
40
+
41
+ faces_src = app.get(src_img)
42
+ faces_dst = app.get(dst_img)
43
+ if not faces_src or not faces_dst:
44
+ return None, "❌ Face not detected in one of the images."
45
+
46
+ result = swapper.get(dst_img, faces_dst[0], faces_src[0], paste_back=True)
47
+ out_img = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
48
+
49
+ return out_img, "✅ Success"
50
+
51
+
52
+ iface = gr.Interface(
53
+ fn=swap_fn,
54
+ inputs=[
55
+ gr.Image(type="pil", label="Source (your face)"),
56
+ gr.Image(type="pil", label="Target (celebrity)"),
57
+ ],
58
+ outputs=[
59
+ gr.Image(type="numpy", label="Swapped Result"),
60
+ gr.Textbox(label="Status"),
61
+ ],
62
+ title="Simple 128px Face-Swap",
63
+ description="Upload two images and get a face-swapped result.",
64
+ allow_flagging="never",
65
+ live=False,
66
+ )
67
+
68
+
69
+ if __name__ == "__main__":
70
+ iface.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch>=2.2.0
2
+ opencv-python
3
+ insightface==0.7.3
4
+ onnxruntime-gpu
5
+ gradio # <— UI dependency
swap.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ swap.py — Simple 128px face-swap using INSwapperONNX + InsightFace
4
+ Usage:
5
+ python swap.py --src <your.jpg> --dst <target.jpg> --out <out.png> --device cpu|gpu
6
+ """
7
+ import argparse
8
+ import os
9
+ import sys
10
+ import cv2
11
+
12
+
13
+ def swap_faces(
14
+ src_path: str,
15
+ dst_path: str,
16
+ out_path: str,
17
+ device: str,
18
+ ):
19
+ import onnxruntime as ort
20
+ from insightface.app import FaceAnalysis
21
+ from insightface.model_zoo.inswapper import INSwapper
22
+
23
+ providers = ["CUDAExecutionProvider"] if device == "gpu" else [
24
+ "CPUExecutionProvider"
25
+ ]
26
+
27
+ app = FaceAnalysis(name="buffalo_l", providers=providers)
28
+ app.prepare(ctx_id=0 if device == "gpu" else -1, det_size=(640, 640))
29
+
30
+ # path to the ONNX model
31
+ model_path = os.path.expanduser(
32
+ "~/.insightface/models/inswapper_128.onnx"
33
+ )
34
+ if not os.path.isfile(model_path):
35
+ sys.exit(f"❌ Model file not found: {model_path}")
36
+
37
+ session = ort.InferenceSession(model_path, providers=providers)
38
+ swapper = INSwapper(model_file=model_path, session=session)
39
+
40
+ src_img = cv2.imread(src_path)
41
+ dst_img = cv2.imread(dst_path)
42
+ if src_img is None or dst_img is None:
43
+ sys.exit("❌ Failed to load one of the images.")
44
+
45
+ faces_src = app.get(src_img)
46
+ faces_dst = app.get(dst_img)
47
+ if not faces_src or not faces_dst:
48
+ sys.exit("❌ Could not detect a face in one of the images.")
49
+
50
+ result = swapper.get(
51
+ dst_img,
52
+ faces_dst[0],
53
+ faces_src[0],
54
+ paste_back=True,
55
+ )
56
+
57
+ ext = os.path.splitext(out_path)[1].lower()
58
+ params = [cv2.IMWRITE_JPEG_QUALITY, 100] if ext in (
59
+ ".jpg",
60
+ ".jpeg",
61
+ ) else []
62
+
63
+ cv2.imwrite(out_path, result, params)
64
+ print(f"✅ Swap complete → {out_path}")
65
+
66
+
67
+ def main():
68
+ parser = argparse.ArgumentParser(description="Face-swap 128px CLI")
69
+
70
+ parser.add_argument(
71
+ "--src",
72
+ required=True,
73
+ help="Path to your source image",
74
+ )
75
+
76
+ parser.add_argument(
77
+ "--dst",
78
+ required=True,
79
+ help="Path to target image",
80
+ )
81
+
82
+ parser.add_argument(
83
+ "--out",
84
+ default="result.png",
85
+ help="Output file path",
86
+ )
87
+
88
+ parser.add_argument(
89
+ "--device",
90
+ choices=["cpu", "gpu"],
91
+ default="cpu",
92
+ help="Run on CPU or GPU (needs onnxruntime-gpu)",
93
+ )
94
+
95
+ args = parser.parse_args()
96
+ swap_faces(args.src, args.dst, args.out, args.device)
97
+
98
+
99
+ if __name__ == "__main__":
100
+ main()