Seniordev22 commited on
Commit
b74a2d5
·
verified ·
1 Parent(s): 09dfafb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -29
app.py CHANGED
@@ -106,23 +106,27 @@ def get_hair_and_exclude_masks(pil_image: Image.Image):
106
 
107
  hair = cv2.resize(hair, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
108
 
109
- # ====================== IMPROVED EXCLUDE MASK (LIPS + EYES) ======================
110
  exclude = np.zeros((128, 128), dtype=np.float32)
111
- # Mouth / lips classes (common indices from CelebAMask-HQ via Segformer)
112
- # 10 = mouth, 11 = upper lip, 12 = lower lip
113
- exclude = np.maximum(exclude, (probs[10] > 0.4).cpu().numpy().astype(np.float32))
114
- exclude = np.maximum(exclude, (probs[11] > 0.4).cpu().numpy().astype(np.float32))
115
- exclude = np.maximum(exclude, (probs[12] > 0.4).cpu().numpy().astype(np.float32))
116
- # Eyes protection (optional but safe)
117
  exclude = np.maximum(exclude, (probs[4] > 0.4).cpu().numpy().astype(np.float32)) # left eye
118
  exclude = np.maximum(exclude, (probs[5] > 0.4).cpu().numpy().astype(np.float32)) # right eye
119
- # Original nose (class 2) – keep as is (beard can overlap)
120
- # But we already have exclude from lips/eyes only.
121
 
122
  exclude = cv2.resize(exclude, (orig_w, orig_h), interpolation=cv2.INTER_NEAREST)
123
  exclude = cv2.dilate(exclude, kernel, iterations=1)
124
 
125
- return hair, exclude
 
 
 
 
 
 
 
126
 
127
  @timed("Beard Mask")
128
  def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
@@ -131,12 +135,11 @@ def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
131
  img_small = pil_image.resize((128, 128), Image.BILINEAR)
132
  img_array = np.array(img_small)
133
 
134
- # ====================== LOWER CONFIDENCE FOR MORE DETECTION ======================
135
  results = model.predict(
136
  img_array,
137
  device=DEVICE.type,
138
- conf=0.20, # was 0.28 catch more beard
139
- iou=0.45, # was 0.50
140
  imgsz=128,
141
  half=(DEVICE.type == "cuda"),
142
  verbose=False,
@@ -149,22 +152,20 @@ def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
149
  if int(cls) == 0: # beard class
150
  m = results[0].masks.data[i].cpu().numpy()
151
  m = cv2.resize(m, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
152
- # ====================== LOWER MASK THRESHOLD ======================
153
- mask = np.maximum(mask, (m > 0.35).astype(np.float32)) # was 0.42
154
 
155
- # Remove areas that should not change (lips, eyes)
156
  mask = np.maximum(mask - exclude_mask, 0)
157
 
158
  if mask.sum() > 30:
159
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
160
- # ====================== ADD DILATION TO COVER HAIRS ======================
161
- mask = cv2.dilate(mask, kernel, iterations=1) # NEW: expand beard coverage
162
  mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
163
  mask = cv2.GaussianBlur(mask, (3,3), 0.8)
164
 
165
  return mask
166
 
167
- # ====================== COLOR TRANSFER (Unchanged, Balanced) ======================
168
  @timed("Color Transfer")
169
  def apply_strong_grey_hair(image: Image.Image, hair_mask: np.ndarray, beard_mask: np.ndarray):
170
  comb = np.maximum(hair_mask, beard_mask)
@@ -175,13 +176,9 @@ def apply_strong_grey_hair(image: Image.Image, hair_mask: np.ndarray, beard_mask
175
  orig_lab = cv2.cvtColor((img * 255).astype(np.uint8), cv2.COLOR_RGB2LAB).astype(np.float32)
176
  hsv = cv2.cvtColor((img * 255).astype(np.uint8), cv2.COLOR_RGB2HSV).astype(np.float32)
177
 
178
- # ====================== HAIR - Balanced Light Grey ======================
179
  hsv_hair = hsv.copy()
180
-
181
- # Desaturation (grey effect)
182
  hsv_hair[..., 1] = hsv_hair[..., 1] * (1 - 0.78 * hair_mask)
183
-
184
- # Balanced Brightness
185
  original_v = hsv[..., 2]
186
  boost_amount = 89 * hair_mask
187
  hsv_hair[..., 2] = np.clip(
@@ -200,7 +197,7 @@ def apply_strong_grey_hair(image: Image.Image, hair_mask: np.ndarray, beard_mask
200
  mean_hair = np.array([130., 0., 0.])
201
  std_hair = np.array([30., 10., 10.])
202
 
203
- # Beard Transfer
204
  beard_bin = beard_mask > 0.5
205
  if np.sum(beard_bin) > 30:
206
  beard_pix = orig_lab[beard_bin]
@@ -219,12 +216,9 @@ def apply_strong_grey_hair(image: Image.Image, hair_mask: np.ndarray, beard_mask
219
  final = hair_grey * hair_mask_3ch + final * (1 - hair_mask_3ch)
220
  final = final * comb_3ch + img * (1 - comb_3ch)
221
 
222
- # Soft cool tint
223
  final = final + (np.array([9, 7, 5], dtype=np.float32)/255.0 * comb[..., None] * 0.18)
224
-
225
  final = np.clip(final * 255, 0, 255).astype(np.uint8)
226
  result = Image.fromarray(final)
227
-
228
  result = result.filter(ImageFilter.UnsharpMask(radius=0.7, percent=70, threshold=2))
229
 
230
  return result
@@ -241,9 +235,12 @@ def process_face_whitening(input_image: Image.Image):
241
 
242
  img_resized = orig.resize((target, target), Image.BILINEAR)
243
 
244
- hair_mask, exclude_mask = get_hair_and_exclude_masks(img_resized)
245
  beard_mask = get_beard_mask_fast(img_resized, exclude_mask)
246
 
 
 
 
247
  final_resized = apply_strong_grey_hair(img_resized, hair_mask, beard_mask)
248
 
249
  final_img = final_resized.resize((ow, oh), Image.LANCZOS)
 
106
 
107
  hair = cv2.resize(hair, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
108
 
109
+ # ====================== EXCLUDE MASK (LIPS + EYES) ======================
110
  exclude = np.zeros((128, 128), dtype=np.float32)
111
+ # Mouth / lips classes
112
+ exclude = np.maximum(exclude, (probs[10] > 0.4).cpu().numpy().astype(np.float32)) # mouth
113
+ exclude = np.maximum(exclude, (probs[11] > 0.4).cpu().numpy().astype(np.float32)) # upper lip
114
+ exclude = np.maximum(exclude, (probs[12] > 0.4).cpu().numpy().astype(np.float32)) # lower lip
115
+ # Eyes protection
 
116
  exclude = np.maximum(exclude, (probs[4] > 0.4).cpu().numpy().astype(np.float32)) # left eye
117
  exclude = np.maximum(exclude, (probs[5] > 0.4).cpu().numpy().astype(np.float32)) # right eye
 
 
118
 
119
  exclude = cv2.resize(exclude, (orig_w, orig_h), interpolation=cv2.INTER_NEAREST)
120
  exclude = cv2.dilate(exclude, kernel, iterations=1)
121
 
122
+ # ====================== MUSTACHE MASK (from upper lip region) ======================
123
+ mustache = (probs[11].cpu().numpy() > 0.35).astype(np.float32) # upper lip as mustache
124
+ mustache = cv2.resize(mustache, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
125
+ mustache = cv2.dilate(mustache, kernel, iterations=1)
126
+ # Remove overlap with exclude (lips) to avoid coloring lips
127
+ mustache = np.maximum(mustache - exclude, 0)
128
+
129
+ return hair, exclude, mustache
130
 
131
  @timed("Beard Mask")
132
  def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
 
135
  img_small = pil_image.resize((128, 128), Image.BILINEAR)
136
  img_array = np.array(img_small)
137
 
 
138
  results = model.predict(
139
  img_array,
140
  device=DEVICE.type,
141
+ conf=0.20, # lower confidence for more beard
142
+ iou=0.45,
143
  imgsz=128,
144
  half=(DEVICE.type == "cuda"),
145
  verbose=False,
 
152
  if int(cls) == 0: # beard class
153
  m = results[0].masks.data[i].cpu().numpy()
154
  m = cv2.resize(m, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
155
+ mask = np.maximum(mask, (m > 0.35).astype(np.float32))
 
156
 
157
+ # Remove lips/eyes
158
  mask = np.maximum(mask - exclude_mask, 0)
159
 
160
  if mask.sum() > 30:
161
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
162
+ mask = cv2.dilate(mask, kernel, iterations=1)
 
163
  mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
164
  mask = cv2.GaussianBlur(mask, (3,3), 0.8)
165
 
166
  return mask
167
 
168
+ # ====================== COLOR TRANSFER ======================
169
  @timed("Color Transfer")
170
  def apply_strong_grey_hair(image: Image.Image, hair_mask: np.ndarray, beard_mask: np.ndarray):
171
  comb = np.maximum(hair_mask, beard_mask)
 
176
  orig_lab = cv2.cvtColor((img * 255).astype(np.uint8), cv2.COLOR_RGB2LAB).astype(np.float32)
177
  hsv = cv2.cvtColor((img * 255).astype(np.uint8), cv2.COLOR_RGB2HSV).astype(np.float32)
178
 
179
+ # Hair greying
180
  hsv_hair = hsv.copy()
 
 
181
  hsv_hair[..., 1] = hsv_hair[..., 1] * (1 - 0.78 * hair_mask)
 
 
182
  original_v = hsv[..., 2]
183
  boost_amount = 89 * hair_mask
184
  hsv_hair[..., 2] = np.clip(
 
197
  mean_hair = np.array([130., 0., 0.])
198
  std_hair = np.array([30., 10., 10.])
199
 
200
+ # Beard transfer
201
  beard_bin = beard_mask > 0.5
202
  if np.sum(beard_bin) > 30:
203
  beard_pix = orig_lab[beard_bin]
 
216
  final = hair_grey * hair_mask_3ch + final * (1 - hair_mask_3ch)
217
  final = final * comb_3ch + img * (1 - comb_3ch)
218
 
 
219
  final = final + (np.array([9, 7, 5], dtype=np.float32)/255.0 * comb[..., None] * 0.18)
 
220
  final = np.clip(final * 255, 0, 255).astype(np.uint8)
221
  result = Image.fromarray(final)
 
222
  result = result.filter(ImageFilter.UnsharpMask(radius=0.7, percent=70, threshold=2))
223
 
224
  return result
 
235
 
236
  img_resized = orig.resize((target, target), Image.BILINEAR)
237
 
238
+ hair_mask, exclude_mask, mustache_mask = get_hair_and_exclude_masks(img_resized)
239
  beard_mask = get_beard_mask_fast(img_resized, exclude_mask)
240
 
241
+ # ====================== MERGE MUSTACHE INTO BEARD MASK ======================
242
+ beard_mask = np.maximum(beard_mask, mustache_mask)
243
+
244
  final_resized = apply_strong_grey_hair(img_resized, hair_mask, beard_mask)
245
 
246
  final_img = final_resized.resize((ow, oh), Image.LANCZOS)