LogicGoInfotechSpaces commited on
Commit
e24b088
·
verified ·
1 Parent(s): f6e4783

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -42
app.py CHANGED
@@ -182,73 +182,168 @@ def enhance_image_with_codeformer(rgb_img, temp_dir=None):
182
  enhanced = cv2.imread(final_path)
183
  return cv2.cvtColor(enhanced, cv2.COLOR_BGR2RGB)
184
 
185
- def multi_face_swap(src_img, tgt_img):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  src_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
187
  tgt_bgr = cv2.cvtColor(tgt_img, cv2.COLOR_RGB2BGR)
188
 
189
  src_faces = face_analysis_app.get(src_bgr)
190
  tgt_faces = face_analysis_app.get(tgt_bgr)
191
 
192
- if not src_faces or not tgt_faces:
193
  raise ValueError("No faces detected")
194
 
195
- def face_sort_key(face):
196
- x1, y1, x2, y2 = face.bbox
197
- area = (x2 - x1) * (y2 - y1)
198
- cx = (x1 + x2) / 2
199
- return (-area, cx)
200
 
201
- # Split by gender
202
- src_male = [f for f in src_faces if f.gender == 1]
203
- src_female = [f for f in src_faces if f.gender == 0]
204
 
205
- tgt_male = [f for f in tgt_faces if f.gender == 1]
206
- tgt_female = [f for f in tgt_faces if f.gender == 0]
207
 
208
- # Sort inside gender groups
209
- src_male = sorted(src_male, key=face_sort_key)
210
- src_female = sorted(src_female, key=face_sort_key)
211
 
212
- tgt_male = sorted(tgt_male, key=face_sort_key)
213
- tgt_female = sorted(tgt_female, key=face_sort_key)
 
214
 
215
- # Build final swap pairs
216
- pairs = []
217
 
218
- for s, t in zip(src_male, tgt_male):
219
- pairs.append((s, t))
220
 
221
- for s, t in zip(src_female, tgt_female):
222
- pairs.append((s, t))
 
223
 
224
- # Fallback if gender mismatch
225
- if not pairs:
226
- src_faces = sorted(src_faces, key=face_sort_key)
227
- tgt_faces = sorted(tgt_faces, key=face_sort_key)
228
- pairs = list(zip(src_faces, tgt_faces))
229
 
230
- result_img = tgt_bgr.copy()
 
231
 
232
- for src_face, _ in pairs:
233
- # 🔁 re-detect current target faces
234
- current_faces = face_analysis_app.get(result_img)
235
- current_faces = sorted(current_faces, key=face_sort_key)
236
 
237
- # choose best matching gender
238
- candidates = [
239
- f for f in current_faces if f.gender == src_face.gender
240
- ] or current_faces
241
 
242
- target_face = candidates[0]
 
243
 
244
- result_img = swapper.get(
245
- result_img,
246
- target_face,
 
247
  src_face,
248
  paste_back=True
249
  )
250
 
251
- return cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
 
254
 
 
182
  enhanced = cv2.imread(final_path)
183
  return cv2.cvtColor(enhanced, cv2.COLOR_BGR2RGB)
184
 
185
+ import numpy as np
186
+ from sklearn.metrics.pairwise import cosine_similarity
187
+
188
+ SIM_THRESHOLD = 0.35 # strict
189
+ MAX_YAW = 35
190
+ MAX_PITCH = 25
191
+ MIN_FACE_AREA = 10000
192
+
193
+
194
+ def face_ok(face):
195
+ yaw, pitch, roll = face.pose
196
+ x1, y1, x2, y2 = face.bbox
197
+ area = (x2 - x1) * (y2 - y1)
198
+
199
+ return (
200
+ abs(yaw) <= MAX_YAW and
201
+ abs(pitch) <= MAX_PITCH and
202
+ area >= MIN_FACE_AREA
203
+ )
204
+
205
+
206
+ def quality_score(face):
207
+ yaw, pitch, roll = face.pose
208
+ x1, y1, x2, y2 = face.bbox
209
+ area = (x2 - x1) * (y2 - y1)
210
+ return area - abs(yaw)*200 - abs(pitch)*150
211
+
212
+
213
+ def multi_face_swap_industry(src_img, tgt_img):
214
+ """
215
+ Industry-grade 2-face swap
216
+ Returns swapped RGB image or raises error
217
+ """
218
+
219
  src_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
220
  tgt_bgr = cv2.cvtColor(tgt_img, cv2.COLOR_RGB2BGR)
221
 
222
  src_faces = face_analysis_app.get(src_bgr)
223
  tgt_faces = face_analysis_app.get(tgt_bgr)
224
 
225
+ if len(src_faces) < 1 or len(tgt_faces) < 1:
226
  raise ValueError("No faces detected")
227
 
228
+ if len(src_faces) > 2 or len(tgt_faces) > 2:
229
+ raise ValueError("More than 2 faces detected (blocked)")
 
 
 
230
 
231
+ # ---- Quality filtering ----
232
+ src_faces = [f for f in src_faces if face_ok(f)]
233
+ tgt_faces = [f for f in tgt_faces if face_ok(f)]
234
 
235
+ if not src_faces or not tgt_faces:
236
+ raise ValueError("Faces rejected due to pose/quality")
237
 
238
+ # ---- Sort by quality (best first) ----
239
+ src_faces = sorted(src_faces, key=quality_score, reverse=True)
240
+ tgt_faces = sorted(tgt_faces, key=quality_score, reverse=True)
241
 
242
+ # ---- Extract embeddings ----
243
+ src_embs = np.array([f.normed_embedding for f in src_faces])
244
+ tgt_embs = np.array([f.normed_embedding for f in tgt_faces])
245
 
246
+ sim = cosine_similarity(src_embs, tgt_embs)
 
247
 
248
+ used_tgts = set()
249
+ pairs = []
250
 
251
+ for i in range(len(src_faces)):
252
+ j = np.argmax(sim[i])
253
+ score = sim[i][j]
254
 
255
+ if score < SIM_THRESHOLD:
256
+ raise ValueError(f"Low identity similarity ({score:.2f})")
 
 
 
257
 
258
+ if j in used_tgts:
259
+ continue
260
 
261
+ pairs.append((src_faces[i], tgt_faces[j], score))
262
+ used_tgts.add(j)
 
 
263
 
264
+ if not pairs:
265
+ raise ValueError("No valid face pairs")
 
 
266
 
267
+ # ---- Swap WITHOUT re-detection ----
268
+ result = tgt_bgr.copy()
269
 
270
+ for src_face, tgt_face, score in pairs:
271
+ result = swapper.get(
272
+ result,
273
+ tgt_face,
274
  src_face,
275
  paste_back=True
276
  )
277
 
278
+ return cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
279
+
280
+ # def multi_face_swap(src_img, tgt_img):
281
+ # src_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
282
+ # tgt_bgr = cv2.cvtColor(tgt_img, cv2.COLOR_RGB2BGR)
283
+
284
+ # src_faces = face_analysis_app.get(src_bgr)
285
+ # tgt_faces = face_analysis_app.get(tgt_bgr)
286
+
287
+ # if not src_faces or not tgt_faces:
288
+ # raise ValueError("No faces detected")
289
+
290
+ # def face_sort_key(face):
291
+ # x1, y1, x2, y2 = face.bbox
292
+ # area = (x2 - x1) * (y2 - y1)
293
+ # cx = (x1 + x2) / 2
294
+ # return (-area, cx)
295
+
296
+ # # Split by gender
297
+ # src_male = [f for f in src_faces if f.gender == 1]
298
+ # src_female = [f for f in src_faces if f.gender == 0]
299
+
300
+ # tgt_male = [f for f in tgt_faces if f.gender == 1]
301
+ # tgt_female = [f for f in tgt_faces if f.gender == 0]
302
+
303
+ # # Sort inside gender groups
304
+ # src_male = sorted(src_male, key=face_sort_key)
305
+ # src_female = sorted(src_female, key=face_sort_key)
306
+
307
+ # tgt_male = sorted(tgt_male, key=face_sort_key)
308
+ # tgt_female = sorted(tgt_female, key=face_sort_key)
309
+
310
+ # # Build final swap pairs
311
+ # pairs = []
312
+
313
+ # for s, t in zip(src_male, tgt_male):
314
+ # pairs.append((s, t))
315
+
316
+ # for s, t in zip(src_female, tgt_female):
317
+ # pairs.append((s, t))
318
+
319
+ # # Fallback if gender mismatch
320
+ # if not pairs:
321
+ # src_faces = sorted(src_faces, key=face_sort_key)
322
+ # tgt_faces = sorted(tgt_faces, key=face_sort_key)
323
+ # pairs = list(zip(src_faces, tgt_faces))
324
+
325
+ # result_img = tgt_bgr.copy()
326
+
327
+ # for src_face, _ in pairs:
328
+ # # 🔁 re-detect current target faces
329
+ # current_faces = face_analysis_app.get(result_img)
330
+ # current_faces = sorted(current_faces, key=face_sort_key)
331
+
332
+ # # choose best matching gender
333
+ # candidates = [
334
+ # f for f in current_faces if f.gender == src_face.gender
335
+ # ] or current_faces
336
+
337
+ # target_face = candidates[0]
338
+
339
+ # result_img = swapper.get(
340
+ # result_img,
341
+ # target_face,
342
+ # src_face,
343
+ # paste_back=True
344
+ # )
345
+
346
+ # return cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
347
 
348
 
349