Seniordev22 commited on
Commit
cdbee3f
·
verified ·
1 Parent(s): cd3f452

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -41
app.py CHANGED
@@ -107,30 +107,25 @@ def get_hair_and_exclude_masks(pil_image: Image.Image):
107
  hair = hair * (1 - face_m)
108
  hair = cv2.resize(hair, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
109
 
110
- # ====================== STRONGER LIP PROTECTION (Exclude Mask) ======================
111
  exclude = np.zeros((128, 128), dtype=np.float32)
112
-
113
- # Mouth, upper lip, lower lip, teeth ko strong exclude
114
- exclude = np.maximum(exclude, (probs[10].numpy() > 0.22).astype(np.float32)) # mouth
115
- exclude = np.maximum(exclude, (probs[11].numpy() > 0.25).astype(np.float32)) # upper lip
116
- exclude = np.maximum(exclude, (probs[12].numpy() > 0.25).astype(np.float32)) # lower lip
117
- exclude = np.maximum(exclude, (probs[4].numpy() > 0.28).astype(np.float32)) # teeth/nose area
118
- exclude = np.maximum(exclude, (probs[5].numpy() > 0.28).astype(np.float32))
119
-
120
- # Strong dilation for full coverage
121
- kernel_big = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
122
- exclude = cv2.dilate(exclude, kernel_big, iterations=3)
123
- exclude = cv2.GaussianBlur(exclude, (9, 9), 1.8)
124
  exclude = cv2.resize(exclude, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
125
 
126
  # ====================== MUSTACHE MASK ======================
127
- mustache_upper = (probs[11].numpy() > 0.28).astype(np.float32)
128
- mustache_lower = (probs[12].numpy() > 0.28).astype(np.float32)
129
  mustache = np.maximum(mustache_upper, mustache_lower)
130
 
131
- mouth = (probs[10].numpy() > 0.28).astype(np.float32)
132
- mustache = np.maximum(mustache, mouth * 0.50)
133
-
134
  kernel_must = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
135
  mustache = cv2.morphologyEx(mustache, cv2.MORPH_CLOSE, kernel_must, iterations=2)
136
  mustache = cv2.GaussianBlur(mustache, (5, 5), 1.2)
@@ -150,12 +145,12 @@ def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
150
  results = model.predict(
151
  img_array,
152
  device=DEVICE.type,
153
- conf=0.28, # Higher confidence = less false beard
154
- iou=0.50,
155
  imgsz=128,
156
  half=False,
157
  verbose=False,
158
- max_det=6
159
  )
160
 
161
  mask = np.zeros((orig_h, orig_w), dtype=np.float32)
@@ -165,46 +160,44 @@ def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
165
  if int(cls) == 0: # beard class
166
  m = results[0].masks.data[i].cpu().numpy()
167
  m = cv2.resize(m, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
168
- mask = np.maximum(mask, (m > 0.42).astype(np.float32)) # stricter threshold
169
 
170
- # Strong lip protection
171
  mask = np.maximum(mask - exclude_mask, 0)
172
 
173
- if mask.sum() > 25:
174
- # ====================== STABLE ROUND BEARD MASK ======================
175
 
176
- # Aggressive erosion to remove extra space
177
  kernel_erode = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
178
  mask = cv2.erode(mask, kernel_erode, iterations=2)
179
 
180
- # Big close for perfect round corners
181
- kernel_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
182
  mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_close, iterations=3)
183
 
184
- # Clean small noise
185
  kernel_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
186
  mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_open, iterations=1)
187
 
188
- # Contour-based smoothing for natural round shape
189
- contours, _ = cv2.findContours((mask > 0.12).astype(np.uint8),
190
- cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
191
-
192
  if contours:
193
  smooth_mask = np.zeros_like(mask, dtype=np.float32)
194
  for cnt in contours:
195
- if cv2.contourArea(cnt) > 100: # ignore tiny noisy parts
196
- epsilon = 0.004 * cv2.arcLength(cnt, True) # smoother rounding
197
  approx = cv2.approxPolyDP(cnt, epsilon, True)
198
  cv2.drawContours(smooth_mask, [approx], -1, 1.0, thickness=cv2.FILLED)
199
  mask = smooth_mask
200
 
201
- # Soft final blur
202
  mask = cv2.GaussianBlur(mask, (9, 9), 2.0)
203
 
204
- # Tight final threshold
205
- mask = (mask > 0.32).astype(np.float32)
206
 
207
- # Final safety erosion
208
  mask = cv2.erode(mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)), iterations=1)
209
 
210
  return mask
@@ -280,8 +273,8 @@ def process_face_whitening(input_image: Image.Image):
280
  hair_mask, exclude_mask, mustache_mask = get_hair_and_exclude_masks(img_resized)
281
  beard_mask = get_beard_mask_fast(img_resized, exclude_mask)
282
 
283
- # Combine beard + mustache
284
- beard_mask = np.maximum(beard_mask, mustache_mask * 0.85)
285
 
286
  final_resized = apply_strong_grey_hair(img_resized, hair_mask, beard_mask)
287
  final_img = final_resized.resize((ow, oh), Image.LANCZOS)
 
107
  hair = hair * (1 - face_m)
108
  hair = cv2.resize(hair, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
109
 
110
+ # ====================== EXCLUDE MASK ======================
111
  exclude = np.zeros((128, 128), dtype=np.float32)
112
+ exclude = np.maximum(exclude, (probs[10].numpy() > 0.35).astype(np.float32))
113
+ exclude = np.maximum(exclude, (probs[11].numpy() > 0.35).astype(np.float32))
114
+ exclude = np.maximum(exclude, (probs[12].numpy() > 0.35).astype(np.float32))
115
+ exclude = np.maximum(exclude, (probs[4].numpy() > 0.35).astype(np.float32))
116
+ exclude = np.maximum(exclude, (probs[5].numpy() > 0.35).astype(np.float32))
117
+
118
+ exclude = cv2.dilate(exclude, kernel, iterations=2)
119
+ exclude = cv2.GaussianBlur(exclude, (5, 5), 1.2)
 
 
 
 
120
  exclude = cv2.resize(exclude, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
121
 
122
  # ====================== MUSTACHE MASK ======================
123
+ mustache_upper = (probs[11].numpy() > 0.30).astype(np.float32)
124
+ mustache_lower = (probs[12].numpy() > 0.30).astype(np.float32)
125
  mustache = np.maximum(mustache_upper, mustache_lower)
126
 
127
+ mouth = (probs[10].numpy() > 0.30).astype(np.float32)
128
+ mustache = np.maximum(mustache, mouth * 0.6)
 
129
  kernel_must = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
130
  mustache = cv2.morphologyEx(mustache, cv2.MORPH_CLOSE, kernel_must, iterations=2)
131
  mustache = cv2.GaussianBlur(mustache, (5, 5), 1.2)
 
145
  results = model.predict(
146
  img_array,
147
  device=DEVICE.type,
148
+ conf=0.20,
149
+ iou=0.45,
150
  imgsz=128,
151
  half=False,
152
  verbose=False,
153
+ max_det=8
154
  )
155
 
156
  mask = np.zeros((orig_h, orig_w), dtype=np.float32)
 
160
  if int(cls) == 0: # beard class
161
  m = results[0].masks.data[i].cpu().numpy()
162
  m = cv2.resize(m, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
163
+ mask = np.maximum(mask, (m > 0.32).astype(np.float32)) # slightly lower threshold
164
 
165
+ # Remove protected areas
166
  mask = np.maximum(mask - exclude_mask, 0)
167
 
168
+ if mask.sum() > 25: # lowered a bit
169
+ # ====================== HIGHLY IMPROVED ROUNDING ======================
170
 
171
+ # 1. Aggressive erosion to kill extra space
172
  kernel_erode = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
173
  mask = cv2.erode(mask, kernel_erode, iterations=2)
174
 
175
+ # 2. Strong close with very big kernel for perfect round corners
176
+ kernel_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (13, 13))
177
  mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_close, iterations=3)
178
 
179
+ # 3. Clean noise
180
  kernel_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
181
  mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_open, iterations=1)
182
 
183
+ # 4. Contour based smoothing (Best for round & tight beard shape)
184
+ contours, _ = cv2.findContours((mask > 0.1).astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
 
 
185
  if contours:
186
  smooth_mask = np.zeros_like(mask, dtype=np.float32)
187
  for cnt in contours:
188
+ if cv2.contourArea(cnt) > 50: # ignore very small noise
189
+ epsilon = 0.008 * cv2.arcLength(cnt, True) # lower = more round
190
  approx = cv2.approxPolyDP(cnt, epsilon, True)
191
  cv2.drawContours(smooth_mask, [approx], -1, 1.0, thickness=cv2.FILLED)
192
  mask = smooth_mask
193
 
194
+ # 5. Final soft blur
195
  mask = cv2.GaussianBlur(mask, (9, 9), 2.0)
196
 
197
+ # 6. Very tight final threshold (no extra space)
198
+ mask = (mask > 0.28).astype(np.float32)
199
 
200
+ # 7. Final light erosion for extra safety
201
  mask = cv2.erode(mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)), iterations=1)
202
 
203
  return mask
 
273
  hair_mask, exclude_mask, mustache_mask = get_hair_and_exclude_masks(img_resized)
274
  beard_mask = get_beard_mask_fast(img_resized, exclude_mask)
275
 
276
+ # Combine with mustache
277
+ beard_mask = np.maximum(beard_mask, mustache_mask * 0.88)
278
 
279
  final_resized = apply_strong_grey_hair(img_resized, hair_mask, beard_mask)
280
  final_img = final_resized.resize((ow, oh), Image.LANCZOS)