Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -160,31 +160,45 @@ def get_beard_mask_fast(pil_image: Image.Image, exclude_mask: np.ndarray):
|
|
| 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.
|
| 164 |
|
| 165 |
# Remove protected areas
|
| 166 |
mask = np.maximum(mask - exclude_mask, 0)
|
| 167 |
|
| 168 |
-
if mask.sum() >
|
| 169 |
-
# ====================== IMPROVED ROUNDING
|
| 170 |
|
| 171 |
-
#
|
| 172 |
-
kernel_erode = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (
|
| 173 |
-
mask = cv2.erode(mask, kernel_erode, iterations=
|
| 174 |
|
| 175 |
-
#
|
| 176 |
-
kernel_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (
|
| 177 |
-
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_close, iterations=
|
| 178 |
|
| 179 |
-
#
|
| 180 |
kernel_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
|
| 181 |
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_open, iterations=1)
|
| 182 |
|
| 183 |
-
#
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
-
#
|
| 187 |
-
mask = (mask
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
return mask
|
| 190 |
|
|
@@ -259,8 +273,8 @@ def process_face_whitening(input_image: Image.Image):
|
|
| 259 |
hair_mask, exclude_mask, mustache_mask = get_hair_and_exclude_masks(img_resized)
|
| 260 |
beard_mask = get_beard_mask_fast(img_resized, exclude_mask)
|
| 261 |
|
| 262 |
-
# Combine
|
| 263 |
-
beard_mask = np.maximum(beard_mask, mustache_mask * 0.
|
| 264 |
|
| 265 |
final_resized = apply_strong_grey_hair(img_resized, hair_mask, beard_mask)
|
| 266 |
final_img = final_resized.resize((ow, oh), Image.LANCZOS)
|
|
|
|
| 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
|
| 204 |
|
|
|
|
| 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)
|