28Senaru commited on
Commit
0e1b37f
·
verified ·
1 Parent(s): 4b22cb7

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -126
app.py DELETED
@@ -1,126 +0,0 @@
1
- import gradio as gr
2
- import cv2
3
- import numpy as np
4
- from PIL import Image
5
- import torch
6
- import face_alignment
7
- import insightface
8
- from scipy import stats
9
-
10
- # -------------------- Device --------------------
11
- device = "cuda" if torch.cuda.is_available() else "cpu"
12
-
13
- # -------------------- Face Alignment --------------------
14
- fa = face_alignment.FaceAlignment(
15
- face_alignment.LandmarksType["2D"],
16
- device=device,
17
- flip_input=False
18
- )
19
-
20
- # -------------------- Identity Model --------------------
21
- face_analyzer = insightface.app.FaceAnalysis(name="buffalo_l")
22
- face_analyzer.prepare(ctx_id=0 if device == "cuda" else -1)
23
-
24
- # -------------------- Utilities --------------------
25
- def pil_to_cv(img):
26
- return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
27
-
28
- def cv_to_pil(img):
29
- return Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
30
-
31
- def get_landmarks(img):
32
- preds = fa.get_landmarks(np.array(img))
33
- if preds is None or len(preds) == 0:
34
- return None
35
- return preds[0].astype(np.float32) # (68,2)
36
-
37
- def align_face(src, tgt):
38
- src_lm = get_landmarks(src)
39
- tgt_lm = get_landmarks(tgt)
40
- if src_lm is None or tgt_lm is None:
41
- return None
42
-
43
- # Use 5 key landmarks for affine transform: eyes, nose tip, mouth corners
44
- idx = [36, 45, 30, 48, 54]
45
- M, _ = cv2.estimateAffinePartial2D(src_lm[idx], tgt_lm[idx])
46
- if M is None:
47
- return None
48
-
49
- aligned = cv2.warpAffine(
50
- pil_to_cv(src),
51
- M,
52
- (tgt.width, tgt.height),
53
- flags=cv2.INTER_LINEAR
54
- )
55
- return cv_to_pil(aligned)
56
-
57
- def identity_similarity(a, b):
58
- ea = face_analyzer.get(np.array(a))
59
- eb = face_analyzer.get(np.array(b))
60
- if not ea or not eb:
61
- return 0.0
62
- v1 = ea[0].embedding
63
- v2 = eb[0].embedding
64
- return float(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
65
-
66
- def color_match(src, tgt):
67
- src_lab = cv2.cvtColor(src, cv2.COLOR_BGR2LAB)
68
- tgt_lab = cv2.cvtColor(tgt, cv2.COLOR_BGR2LAB)
69
-
70
- for i in range(3):
71
- s = src_lab[:, :, i].flatten()
72
- t = tgt_lab[:, :, i].flatten()
73
- s_rank = stats.rankdata(s)
74
- s_norm = (s_rank - s_rank.min()) / (s_rank.max() - s_rank.min() + 1e-6)
75
- t_sorted = np.sort(t)
76
- src_lab[:, :, i] = t_sorted[(s_norm * (len(t_sorted) - 1)).astype(int)].reshape(src_lab[:, :, i].shape)
77
-
78
- return cv2.cvtColor(src_lab.astype(np.uint8), cv2.COLOR_LAB2BGR)
79
-
80
- # -------------------- Core Face Swap --------------------
81
- def face_swap(src_img, tgt_img):
82
- if src_img is None or tgt_img is None:
83
- return "Upload both images", None
84
-
85
- aligned = align_face(src_img, tgt_img)
86
- if aligned is None:
87
- return "Face alignment failed", None
88
-
89
- src_cv = pil_to_cv(aligned)
90
- tgt_cv = pil_to_cv(tgt_img)
91
-
92
- # Color harmonization
93
- src_cv = color_match(src_cv, tgt_cv)
94
-
95
- # Poisson blending
96
- mask = 255 * np.ones(src_cv.shape[:2], dtype=np.uint8)
97
- center = (tgt_cv.shape[1] // 2, tgt_cv.shape[0] // 2)
98
- blended = cv2.seamlessClone(src_cv, tgt_cv, mask, center, cv2.NORMAL_CLONE)
99
-
100
- result = cv_to_pil(blended)
101
-
102
- # Identity validation
103
- sim = identity_similarity(src_img, result)
104
- if sim < 0.94:
105
- return f"Identity similarity too low: {sim:.3f}", result
106
-
107
- return f"Identity similarity OK: {sim:.3f}", result
108
-
109
- # -------------------- Gradio UI --------------------
110
- with gr.Blocks() as demo:
111
- gr.Markdown("## Ultra-Realistic Face Swap (Photographic Only)")
112
- gr.Markdown(
113
- "- Strict 2D face swap\n"
114
- "- Identity similarity ≥0.94\n"
115
- "- Zero AI / 3D look\n"
116
- )
117
-
118
- src = gr.Image(label="Source Face", type="pil")
119
- tgt = gr.Image(label="Target Image", type="pil")
120
- btn = gr.Button("Run Face Swap")
121
- status = gr.Textbox(label="Status")
122
- output = gr.Image(label="Result")
123
-
124
- btn.click(face_swap, [src, tgt], [status, output])
125
-
126
- demo.launch()