mshassanali11 commited on
Commit
5956651
Β·
verified Β·
1 Parent(s): bf68f45

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -18
app.py CHANGED
@@ -6,8 +6,9 @@ Pipeline:
6
  3. 3-zone paste-back:
7
  Zone A β€” inner mouth (teeth): AI result, light color correction
8
  Zone B β€” lip tissue ring: AI shape + 90% color forced to original
9
- Zone C β€” skin extension area: AI shape + 98% color forced to original
10
  4. Only teeth change β€” lip color, texture, skin all stay original
 
11
  """
12
 
13
  import os, io, base64, urllib.request, textwrap
@@ -101,10 +102,11 @@ def sample_face_tones(img_rgb, face_lm, W, H):
101
  # ── Build 3 zone masks (in bbox-local coords) ─────────────────────────────────
102
  def build_zone_masks(H, W, shifted_outer, shifted_inner, lip_height):
103
  """
104
- Returns three uint8 masks (HΓ—W), each 0 or 255:
105
- zone_teeth β€” inner lip polygon (teeth area) β€” AI only
106
- zone_lips β€” outer minus inner (lip tissue ring) β€” shape AI, color original
107
- zone_skin β€” upward/downward extension outside lip β€” shape AI, color original
 
108
  """
109
  # Outer polygon filled
110
  outer_mask = np.zeros((H, W), np.uint8)
@@ -117,7 +119,7 @@ def build_zone_masks(H, W, shifted_outer, shifted_inner, lip_height):
117
  # Lip tissue = outer minus inner
118
  lip_tissue = cv2.subtract(outer_mask, inner_mask)
119
 
120
- # Skin extension zone: upward/downward rect outside lip polygon
121
  lx1 = int(shifted_outer[:, 0].min())
122
  lx2 = int(shifted_outer[:, 0].max())
123
  ly1 = int(shifted_outer[:, 1].min())
@@ -130,17 +132,34 @@ def build_zone_masks(H, W, shifted_outer, shifted_inner, lip_height):
130
  ext_left = max(0, lx1 - 4)
131
  ext_right = min(W, lx2 + 4)
132
 
133
- skin_ext = np.zeros((H, W), np.uint8)
134
- skin_ext[ext_top:ly1, ext_left:ext_right] = 255 # above lips
135
- skin_ext[ly2:ext_bot, ext_left:ext_right] = 255 # below lips
136
- # Remove the lip polygon area from skin zone (no overlap)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  skin_ext = cv2.subtract(skin_ext, outer_mask)
 
138
 
139
- # Dilate slightly for smooth edges
140
  k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
141
  inner_mask = cv2.dilate(inner_mask, k, iterations=1)
142
  lip_tissue = cv2.dilate(lip_tissue, k, iterations=1)
143
- skin_ext = cv2.dilate(skin_ext, k, iterations=1)
144
 
145
  return inner_mask, lip_tissue, skin_ext
146
 
@@ -248,8 +267,9 @@ def paste_back_3zone(original_rgb, generated_pil, bbox, face_lm, W, H, lip_heigh
248
  Result: lips keep their original skin tone, texture, gradients.
249
 
250
  Zone C β€” skin_ext (area outside lip polygon, inside the extended edit zone):
251
- Near-original: 98% color corrected back.
252
- Only the shape information (muscle movement) carries through.
 
253
  """
254
  x1, y1, x2, y2 = bbox
255
  bw, bh = x2 - x1, y2 - y1
@@ -285,10 +305,11 @@ def paste_back_3zone(original_rgb, generated_pil, bbox, face_lm, W, H, lip_heigh
285
  if mb_lips.any():
286
  corrected_lab = lab_color_transfer(corrected_lab, orig_lab, mb_lips, 0.90)
287
 
288
- # Zone C: skin extension β€” 98% color correction β†’ nearly original skin
 
289
  mb_skin = zone_skin > 0
290
  if mb_skin.any():
291
- corrected_lab = lab_color_transfer(corrected_lab, orig_lab, mb_skin, 0.98)
292
 
293
  # Reconstruct corrected RGB
294
  corrected_rgb = cv2.cvtColor(
@@ -296,15 +317,17 @@ def paste_back_3zone(original_rgb, generated_pil, bbox, face_lm, W, H, lip_heigh
296
  )
297
 
298
  # Build combined blend mask (all 3 zones together) for the final alpha blend
 
299
  combined_mask = np.clip(
300
  zone_teeth.astype(np.float32) +
301
  zone_lips.astype(np.float32) +
302
  zone_skin.astype(np.float32),
303
  0, 255
304
  )
 
305
  if feather > 0:
306
- k = feather * 2 + 1
307
- combined_mask = cv2.GaussianBlur(combined_mask, (k, k), feather * 0.5)
308
  combined_mask = np.clip(combined_mask / 255.0, 0, 1)[:, :, np.newaxis]
309
 
310
  # Alpha blend: inside combined zone β†’ corrected_rgb; outside β†’ original
 
6
  3. 3-zone paste-back:
7
  Zone A β€” inner mouth (teeth): AI result, light color correction
8
  Zone B β€” lip tissue ring: AI shape + 90% color forced to original
9
+ Zone C β€” skin extension area: Gaussian falloff blend, 100% color original
10
  4. Only teeth change β€” lip color, texture, skin all stay original
11
+ 5. FIX: Gaussian distance falloff on skin zone eliminates horizontal seam lines
12
  """
13
 
14
  import os, io, base64, urllib.request, textwrap
 
102
  # ── Build 3 zone masks (in bbox-local coords) ─────────────────────────────────
103
  def build_zone_masks(H, W, shifted_outer, shifted_inner, lip_height):
104
  """
105
+ Returns three masks:
106
+ zone_teeth β€” inner lip polygon (teeth area) β€” AI only, uint8 hard mask
107
+ zone_lips β€” outer minus inner (lip tissue ring) β€” shape AI, color original, uint8
108
+ zone_skin β€” upward/downward extension with GAUSSIAN FALLOFF (float32 0-255)
109
+ eliminates the hard horizontal seam lines at the zone boundary
110
  """
111
  # Outer polygon filled
112
  outer_mask = np.zeros((H, W), np.uint8)
 
119
  # Lip tissue = outer minus inner
120
  lip_tissue = cv2.subtract(outer_mask, inner_mask)
121
 
122
+ # Bounding box of outer lip
123
  lx1 = int(shifted_outer[:, 0].min())
124
  lx2 = int(shifted_outer[:, 0].max())
125
  ly1 = int(shifted_outer[:, 1].min())
 
132
  ext_left = max(0, lx1 - 4)
133
  ext_right = min(W, lx2 + 4)
134
 
135
+ # ── FIX: Gaussian falloff instead of hard rectangular zone ────────────
136
+ # This removes the visible horizontal seam lines above/below lips.
137
+ # Influence drops exponentially with distance from the lip boundary.
138
+ skin_ext = np.zeros((H, W), np.float32)
139
+
140
+ # Above lips β€” falloff away from lip top edge (ly1)
141
+ sigma_up = max(1.0, upward_ext * 0.35)
142
+ for row in range(ext_top, ly1):
143
+ dist = ly1 - row # pixels above lip edge
144
+ alpha = np.exp(-(dist ** 2) / (2 * sigma_up ** 2))
145
+ skin_ext[row, ext_left:ext_right] = alpha * 255.0
146
+
147
+ # Below lips β€” falloff away from lip bottom edge (ly2)
148
+ sigma_dn = max(1.0, downward_ext * 0.5)
149
+ for row in range(ly2, ext_bot):
150
+ dist = row - ly2
151
+ alpha = np.exp(-(dist ** 2) / (2 * sigma_dn ** 2))
152
+ skin_ext[row, ext_left:ext_right] = alpha * 255.0
153
+
154
+ skin_ext = np.clip(skin_ext, 0, 255).astype(np.uint8)
155
+ # Remove any overlap with the lip polygon itself
156
  skin_ext = cv2.subtract(skin_ext, outer_mask)
157
+ # ──────────────────────────────────────────────────────────────────────
158
 
159
+ # Dilate teeth and lip tissue for smooth edges (skin_ext already soft, skip)
160
  k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
161
  inner_mask = cv2.dilate(inner_mask, k, iterations=1)
162
  lip_tissue = cv2.dilate(lip_tissue, k, iterations=1)
 
163
 
164
  return inner_mask, lip_tissue, skin_ext
165
 
 
267
  Result: lips keep their original skin tone, texture, gradients.
268
 
269
  Zone C β€” skin_ext (area outside lip polygon, inside the extended edit zone):
270
+ Full original color (100% correction).
271
+ Uses Gaussian falloff mask β€” NO hard horizontal boundary line.
272
+ The seam is completely invisible.
273
  """
274
  x1, y1, x2, y2 = bbox
275
  bw, bh = x2 - x1, y2 - y1
 
305
  if mb_lips.any():
306
  corrected_lab = lab_color_transfer(corrected_lab, orig_lab, mb_lips, 0.90)
307
 
308
+ # Zone C: skin extension β€” 100% color correction β†’ identical skin color
309
+ # (The soft Gaussian mask already handles the blend; full correction here)
310
  mb_skin = zone_skin > 0
311
  if mb_skin.any():
312
+ corrected_lab = lab_color_transfer(corrected_lab, orig_lab, mb_skin, 1.0)
313
 
314
  # Reconstruct corrected RGB
315
  corrected_rgb = cv2.cvtColor(
 
317
  )
318
 
319
  # Build combined blend mask (all 3 zones together) for the final alpha blend
320
+ # zone_skin already has Gaussian falloff baked in β€” no hard edge
321
  combined_mask = np.clip(
322
  zone_teeth.astype(np.float32) +
323
  zone_lips.astype(np.float32) +
324
  zone_skin.astype(np.float32),
325
  0, 255
326
  )
327
+ # Use a wider kernel for more aggressive feathering on the teeth/lip hard edges
328
  if feather > 0:
329
+ k = feather * 4 + 1 # wider kernel: was feather*2+1
330
+ combined_mask = cv2.GaussianBlur(combined_mask, (k, k), feather * 1.5)
331
  combined_mask = np.clip(combined_mask / 255.0, 0, 1)[:, :, np.newaxis]
332
 
333
  # Alpha blend: inside combined zone β†’ corrected_rgb; outside β†’ original