kaizma commited on
Commit
e547059
·
verified ·
1 Parent(s): 7f60d62

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +115 -0
  2. face_swap.py +46 -0
app.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import streamlit as st
3
+ from PIL import Image
4
+ import io
5
+ from face_swap import swap_faces
6
+
7
+ # --- Page config ---
8
+ st.set_page_config(page_title="🎭 Face Swap Demo", layout="centered")
9
+
10
+ # --- Hero section ---
11
+ st.markdown(
12
+ """
13
+ <style>
14
+ .title {
15
+ font-size: 2.5rem;
16
+ font-weight: bold;
17
+ text-align: center;
18
+ margin-bottom: 0.5rem;
19
+ }
20
+ .subtitle {
21
+ text-align: center;
22
+ font-size: 1.2rem;
23
+ color: #555;
24
+ margin-bottom: 2rem;
25
+ }
26
+ </style>
27
+ <div class="title">🎭 Face Swap Demo</div>
28
+ <div class="subtitle">Upload two images and see the magic of AI face-swapping</div>
29
+ """,
30
+ unsafe_allow_html=True,
31
+ )
32
+
33
+ # --- Upload section in cards ---
34
+ col1, col2 = st.columns(2)
35
+ with col1:
36
+ st.markdown("### 🧑 Source Face")
37
+ src_file = st.file_uploader(
38
+ "Upload Source Image", type=["jpg", "jpeg", "png"], key="src"
39
+ )
40
+ with col2:
41
+ st.markdown("### 🎯 Target Image")
42
+ tgt_file = st.file_uploader(
43
+ "Upload Target Image", type=["jpg", "jpeg", "png"], key="tgt"
44
+ )
45
+
46
+ # --- Options ---
47
+ with st.sidebar:
48
+ st.header("⚙️ Options")
49
+ resize_max = st.number_input(
50
+ "Resize images to max dimension (px)", value=800, min_value=200, max_value=2000
51
+ )
52
+ download_name = st.text_input("Download filename", value="face_swap_result.png")
53
+
54
+ def load_image(file):
55
+ img = Image.open(file).convert("RGB")
56
+ # resize if large
57
+ w, h = img.size
58
+ max_dim = max(w, h)
59
+ if max_dim > resize_max:
60
+ scale = resize_max / max_dim
61
+ img = img.resize((int(w * scale), int(h * scale)), Image.LANCZOS)
62
+ return img
63
+
64
+ # --- Run button ---
65
+ if st.button("🚀 Run Face Swap", use_container_width=True):
66
+ if not src_file or not tgt_file:
67
+ st.error("⚠️ Please upload both source and target images.")
68
+ else:
69
+ src_img = load_image(src_file)
70
+ tgt_img = load_image(tgt_file)
71
+
72
+ st.markdown("### 🔍 Preview Images")
73
+ st.image([src_img, tgt_img], caption=["Source", "Target"], width=300)
74
+
75
+ with st.spinner("Running face-swap... please wait ⏳"):
76
+ try:
77
+ result_pil = swap_faces(src_img, tgt_img)
78
+
79
+ st.success("✨ Done! Check the result below")
80
+
81
+ # Show side-by-side comparison
82
+ col_a, col_b = st.columns(2)
83
+ with col_a:
84
+ st.markdown("**Before**")
85
+ st.image(tgt_img, use_column_width=True)
86
+ with col_b:
87
+ st.markdown("**After (Swapped)**")
88
+ st.image(result_pil, use_column_width=True)
89
+
90
+ # Download button
91
+ buf = io.BytesIO()
92
+ result_pil.save(buf, format="PNG")
93
+ st.download_button(
94
+ "⬇️ Download result",
95
+ buf.getvalue(),
96
+ file_name=download_name,
97
+ mime="image/png",
98
+ use_container_width=True,
99
+ )
100
+
101
+ except Exception as e:
102
+ st.error(f"❌ Face-swap failed: {e}")
103
+ st.exception(e)
104
+
105
+ # --- Footer ---
106
+ st.markdown(
107
+ """
108
+ <hr>
109
+ <div style="text-align:center; color: gray; font-size: 0.9rem;">
110
+ Built with ❤️ using Streamlit and InsightFace
111
+ </div>
112
+ """,
113
+ unsafe_allow_html=True,
114
+ )
115
+
face_swap.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image
3
+ import insightface
4
+ from insightface.app import FaceAnalysis
5
+
6
+ import os
7
+ import requests
8
+
9
+ MODEL_URL = "https://huggingface.co/kaizma/face-swap-inswapper/resolve/da20be1c8ba9b074d52c6a0540f8935d3e3605e5/inswapper_128.onnx"
10
+ MODEL_PATH = "inswapper_128.onnx"
11
+
12
+ # Download model if not exists
13
+ if not os.path.exists(MODEL_PATH):
14
+ print("Downloading model...")
15
+ r = requests.get(MODEL_URL, stream=True)
16
+ with open(MODEL_PATH, "wb") as f:
17
+ for chunk in r.iter_content(chunk_size=8192):
18
+ f.write(chunk)
19
+ print("Model downloaded.")
20
+
21
+
22
+ def swap_faces(src_pil, tgt_pil):
23
+ # Convert PIL to numpy (BGR)
24
+ src = np.array(src_pil)[:, :, ::-1].copy()
25
+ tgt = np.array(tgt_pil)[:, :, ::-1].copy()
26
+
27
+ # Initialize face analysis and swapper
28
+ app = FaceAnalysis(name='buffalo_l')
29
+ app.prepare(ctx_id=0, det_size=(640, 640))
30
+ model_path = r"C:\Users\Dell\Desktop\Face Swap\inswapper_128.onnx"
31
+ swapper = insightface.model_zoo.get_model(model_path)
32
+
33
+ # Detect faces
34
+ src_faces = app.get(src)
35
+ tgt_faces = app.get(tgt)
36
+ if len(src_faces) == 0 or len(tgt_faces) == 0:
37
+ raise ValueError("No face detected in one of the images.")
38
+
39
+ source_face = src_faces[0]
40
+ res = tgt.copy()
41
+ for face in tgt_faces:
42
+ res = swapper.get(res, face, source_face, paste_back=True)
43
+
44
+ # Convert back to PIL (RGB)
45
+ res_pil = Image.fromarray(res[:, :, ::-1])
46
+ return res_pil