Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -193,17 +193,45 @@ def apply_dreamy_glow(img_np):
|
|
| 193 |
return cv2.addWeighted(img_np, 1.0, blurred, 0.6, 0)
|
| 194 |
|
| 195 |
|
| 196 |
-
# ---
|
| 197 |
-
def _create_sunglasses_mask():
|
| 198 |
-
|
|
|
|
| 199 |
draw = ImageDraw.Draw(sunglasses)
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
return sunglasses
|
| 204 |
|
| 205 |
-
def apply_sunglasses(img_np):
|
| 206 |
-
"""Applies sunglasses to all detected faces (up to 2)."""
|
| 207 |
if img_np is None: return img_np
|
| 208 |
results = face_mesh.process(img_np)
|
| 209 |
pil_image = Image.fromarray(img_np)
|
|
@@ -214,103 +242,18 @@ def apply_sunglasses(img_np):
|
|
| 214 |
eye_center = (left_eye + right_eye) / 2
|
| 215 |
eye_width = np.linalg.norm(left_eye - right_eye)
|
| 216 |
angle = math.degrees(math.atan2(right_eye[1] - left_eye[1], right_eye[0] - left_eye[0]))
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
| 219 |
resized_sunglasses = sunglasses_img.resize((w, h), Image.Resampling.LANCZOS)
|
| 220 |
rotated_sunglasses = resized_sunglasses.rotate(angle, expand=True, resample=Image.Resampling.BICUBIC)
|
| 221 |
pos_x, pos_y = int(eye_center[0] - rotated_sunglasses.width / 2), int(eye_center[1] - rotated_sunglasses.height / 2)
|
|
|
|
| 222 |
pil_image.paste(rotated_sunglasses, (pos_x, pos_y), rotated_sunglasses)
|
|
|
|
| 223 |
return np.array(pil_image)
|
| 224 |
|
| 225 |
-
def _draw_low_poly_accessories(draw, landmarks, wearable_type):
|
| 226 |
-
"""Draws a single piece of headwear or accessory using low-poly polygons."""
|
| 227 |
-
chin, forehead_top, left_cheek, right_cheek = landmarks[152], landmarks[10], landmarks[234], landmarks[454]
|
| 228 |
-
nose_tip = landmarks[1]
|
| 229 |
-
upper_lip = landmarks[13]
|
| 230 |
-
left_ear = landmarks[234]
|
| 231 |
-
right_ear = landmarks[454]
|
| 232 |
-
|
| 233 |
-
face_width, face_height = np.linalg.norm(left_cheek - right_cheek), np.linalg.norm(forehead_top - chin)
|
| 234 |
-
|
| 235 |
-
# Improved hat positioning: find the "true" top of the head
|
| 236 |
-
top_of_head_y = forehead_top[1] - (face_height * 0.2)
|
| 237 |
-
|
| 238 |
-
if wearable_type == "Plumber":
|
| 239 |
-
hat_color = "#E62E21" # Red
|
| 240 |
-
mustache_color = "#4E2700" # Brown
|
| 241 |
-
hat_width, hat_height = face_width * 1.1, face_height * 0.4
|
| 242 |
-
hat_center_x = forehead_top[0]
|
| 243 |
-
hat_brim_y = top_of_head_y + hat_height * 0.5
|
| 244 |
-
# Brim (as a polygon)
|
| 245 |
-
draw.polygon([
|
| 246 |
-
(hat_center_x - hat_width/2, hat_brim_y), (hat_center_x + hat_width/2, hat_brim_y),
|
| 247 |
-
(hat_center_x + hat_width/2.1, hat_brim_y + hat_height*0.1), (hat_center_x - hat_width/2.1, hat_brim_y + hat_height*0.1)
|
| 248 |
-
], fill=hat_color)
|
| 249 |
-
# Top part (as a polygon)
|
| 250 |
-
draw.polygon([
|
| 251 |
-
(hat_center_x - hat_width/2.2, hat_brim_y), (hat_center_x + hat_width/2.2, hat_brim_y),
|
| 252 |
-
(hat_center_x + hat_width/2.3, top_of_head_y), (hat_center_x - hat_width/2.3, top_of_head_y)
|
| 253 |
-
], fill=hat_color)
|
| 254 |
-
# Mustache
|
| 255 |
-
mustache_y = upper_lip[1]
|
| 256 |
-
mustache_w, mustache_h = face_width * 0.4, face_height * 0.1
|
| 257 |
-
draw.polygon([
|
| 258 |
-
(nose_tip[0] - mustache_w/2, mustache_y), (nose_tip[0] + mustache_w/2, mustache_y),
|
| 259 |
-
(nose_tip[0] + mustache_w/2.2, mustache_y + mustache_h), (nose_tip[0] - mustache_w/2.2, mustache_y + mustache_h)
|
| 260 |
-
], fill=mustache_color)
|
| 261 |
-
|
| 262 |
-
elif wearable_type == "Elf Hero":
|
| 263 |
-
hat_color = "#008000" # Green
|
| 264 |
-
ear_color = "#FFDAB9" # Skin tone
|
| 265 |
-
# Hat
|
| 266 |
-
pts = [(forehead_top[0], top_of_head_y - face_height*0.5), (left_cheek[0], top_of_head_y + face_height*0.2), (right_cheek[0], top_of_head_y + face_height*0.2)]
|
| 267 |
-
draw.polygon(pts, fill=hat_color)
|
| 268 |
-
# Elf Ears
|
| 269 |
-
ear_w, ear_h = face_width * 0.2, face_height * 0.3
|
| 270 |
-
# Left Ear
|
| 271 |
-
draw.polygon([(left_ear[0], left_ear[1]), (left_ear[0] - ear_w, left_ear[1] - ear_h*0.2), (left_ear[0] - ear_w*0.2, left_ear[1] + ear_h*0.8)], fill=ear_color, outline="black")
|
| 272 |
-
# Right Ear
|
| 273 |
-
draw.polygon([(right_ear[0], right_ear[1]), (right_ear[0] + ear_w, right_ear[1] - ear_h*0.2), (right_ear[0] + ear_w*0.2, right_ear[1] + ear_h*0.8)], fill=ear_color, outline="black")
|
| 274 |
-
|
| 275 |
-
elif wearable_type == "Cowboy Hat":
|
| 276 |
-
hat_color = "#634519" # Brown
|
| 277 |
-
hat_width, hat_height = face_width * 1.5, face_height * 0.5
|
| 278 |
-
hat_center_x, brim_center_y = int(forehead_top[0]), int(top_of_head_y + hat_height * 0.7)
|
| 279 |
-
# Brim
|
| 280 |
-
draw.polygon([
|
| 281 |
-
(hat_center_x - hat_width/2, brim_center_y), (hat_center_x + hat_width/2, brim_center_y),
|
| 282 |
-
(hat_center_x + hat_width/2.2, brim_center_y + hat_height*0.2), (hat_center_x - hat_width/2.2, brim_center_y + hat_height*0.2)
|
| 283 |
-
], fill=hat_color)
|
| 284 |
-
# Crown
|
| 285 |
-
draw.polygon([
|
| 286 |
-
(hat_center_x - hat_width/3, brim_center_y), (hat_center_x + hat_width/3, brim_center_y),
|
| 287 |
-
(hat_center_x + hat_width/3.5, top_of_head_y), (hat_center_x - hat_width/3.5, top_of_head_y)
|
| 288 |
-
], fill=hat_color)
|
| 289 |
-
|
| 290 |
-
elif wearable_type == "Crown":
|
| 291 |
-
hat_color = "#FFD700" # Gold
|
| 292 |
-
hat_width, hat_height = face_width * 1.1, face_height * 0.25
|
| 293 |
-
base_y = int(top_of_head_y)
|
| 294 |
-
base_x_start = int(forehead_top[0] - hat_width/2)
|
| 295 |
-
draw.polygon([base_x_start, base_y, base_x_start + hat_width, base_y, base_x_start + hat_width, base_y + hat_height * 0.3, base_x_start, base_y + hat_height * 0.3], fill=hat_color)
|
| 296 |
-
for i in range(5):
|
| 297 |
-
spike_base_x = base_x_start + (i * hat_width/4)
|
| 298 |
-
pts = [(spike_base_x - hat_width*0.05, base_y), (spike_base_x + hat_width*0.05, base_y), (spike_base_x, base_y - hat_height)]
|
| 299 |
-
draw.polygon(pts, fill=hat_color)
|
| 300 |
-
|
| 301 |
-
def apply_accessories(img_np, wearable_type):
|
| 302 |
-
"""Applies low-poly accessories to all detected faces (up to 2)."""
|
| 303 |
-
if img_np is None: return img_np
|
| 304 |
-
results = face_mesh.process(img_np)
|
| 305 |
-
pil_image = Image.fromarray(img_np)
|
| 306 |
-
if results.multi_face_landmarks:
|
| 307 |
-
overlay = Image.new('RGBA', pil_image.size, (0, 0, 0, 0))
|
| 308 |
-
draw = ImageDraw.Draw(overlay)
|
| 309 |
-
for face_landmarks in results.multi_face_landmarks:
|
| 310 |
-
landmarks = np.array([(lm.x * img_np.shape[1], lm.y * img_np.shape[0]) for lm in face_landmarks.landmark])
|
| 311 |
-
_draw_low_poly_accessories(draw, landmarks, wearable_type)
|
| 312 |
-
pil_image.paste(overlay, (0, 0), overlay)
|
| 313 |
-
return np.array(pil_image)
|
| 314 |
|
| 315 |
# --- Main Processing Function ---
|
| 316 |
def process_image(image, filter_name, splash_color):
|
|
@@ -326,11 +269,9 @@ def process_image(image, filter_name, splash_color):
|
|
| 326 |
"HDR Effect": apply_hdr_effect, "Sunburst Glow": apply_sunburst_glow,
|
| 327 |
"Dreamy Glow": apply_dreamy_glow,
|
| 328 |
"Color Splash": lambda img: apply_color_splash(img, splash_color),
|
| 329 |
-
"Sunglasses": apply_sunglasses,
|
| 330 |
-
"
|
| 331 |
-
"
|
| 332 |
-
"Cowboy Hat": lambda img: apply_accessories(img, "Cowboy Hat"),
|
| 333 |
-
"Crown": lambda img: apply_accessories(img, "Crown"),
|
| 334 |
"None": lambda img: img
|
| 335 |
}
|
| 336 |
|
|
@@ -351,7 +292,7 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 351 |
filters_standard = ["Grayscale", "Sepia", "Invert", "Posterize", "Solarize", "Vignette", "Contour", "Sharpen"]
|
| 352 |
filters_artistic = ["Cartoon", "Sketch", "Pixelate"]
|
| 353 |
filters_advanced = ["N64-ify (Triangles)", "HDR Effect", "Color Splash", "Sunburst Glow", "Dreamy Glow"]
|
| 354 |
-
filters_face = ["Sunglasses", "
|
| 355 |
|
| 356 |
all_filters = ["None"] + filters_standard + filters_artistic + filters_advanced + filters_face
|
| 357 |
|
|
|
|
| 193 |
return cv2.addWeighted(img_np, 1.0, blurred, 0.6, 0)
|
| 194 |
|
| 195 |
|
| 196 |
+
# --- Sunglasses Functions ---
|
| 197 |
+
def _create_sunglasses_mask(style="aviator"):
|
| 198 |
+
"""Creates a PIL image mask for a given sunglass style."""
|
| 199 |
+
sunglasses = Image.new('RGBA', (300, 150), (0, 0, 0, 0))
|
| 200 |
draw = ImageDraw.Draw(sunglasses)
|
| 201 |
+
|
| 202 |
+
if style == "aviator":
|
| 203 |
+
# Left Lens
|
| 204 |
+
draw.polygon([(20, 50), (130, 40), (120, 110), (30, 110)], fill=(20, 20, 20, 200), outline='gold', width=3)
|
| 205 |
+
# Right Lens
|
| 206 |
+
draw.polygon([(170, 40), (280, 50), (270, 110), (180, 110)], fill=(20, 20, 20, 200), outline='gold', width=3)
|
| 207 |
+
# Bridge
|
| 208 |
+
draw.line((130, 45, 170, 45), fill='gold', width=5)
|
| 209 |
+
draw.line((130, 55, 170, 55), fill='gold', width=5)
|
| 210 |
+
|
| 211 |
+
elif style == "retro_square":
|
| 212 |
+
# Left Lens
|
| 213 |
+
draw.rectangle((20, 40, 130, 110), fill=(10, 10, 10, 210), outline='white', width=6)
|
| 214 |
+
# Right Lens
|
| 215 |
+
draw.rectangle((170, 40, 280, 110), fill=(10, 10, 10, 210), outline='white', width=6)
|
| 216 |
+
# Bridge
|
| 217 |
+
draw.rectangle((130, 60, 170, 75), fill='white')
|
| 218 |
+
|
| 219 |
+
elif style == "heart_shaped":
|
| 220 |
+
# Left Heart
|
| 221 |
+
draw.polygon([
|
| 222 |
+
(75, 40), (125, 60), (75, 120), (25, 60)
|
| 223 |
+
], fill='deeppink', outline='hotpink', width=4)
|
| 224 |
+
# Right Heart
|
| 225 |
+
draw.polygon([
|
| 226 |
+
(225, 40), (275, 60), (225, 120), (175, 60)
|
| 227 |
+
], fill='deeppink', outline='hotpink', width=4)
|
| 228 |
+
# Bridge
|
| 229 |
+
draw.line((125, 70, 175, 70), fill='hotpink', width=6)
|
| 230 |
+
|
| 231 |
return sunglasses
|
| 232 |
|
| 233 |
+
def apply_sunglasses(img_np, style="aviator"):
|
| 234 |
+
"""Applies a specific style of sunglasses to all detected faces (up to 2)."""
|
| 235 |
if img_np is None: return img_np
|
| 236 |
results = face_mesh.process(img_np)
|
| 237 |
pil_image = Image.fromarray(img_np)
|
|
|
|
| 242 |
eye_center = (left_eye + right_eye) / 2
|
| 243 |
eye_width = np.linalg.norm(left_eye - right_eye)
|
| 244 |
angle = math.degrees(math.atan2(right_eye[1] - left_eye[1], right_eye[0] - left_eye[0]))
|
| 245 |
+
|
| 246 |
+
sunglasses_img = _create_sunglasses_mask(style)
|
| 247 |
+
|
| 248 |
+
w, h = int(eye_width * 1.8), int(eye_width * 1.8 * sunglasses_img.height / sunglasses_img.width)
|
| 249 |
resized_sunglasses = sunglasses_img.resize((w, h), Image.Resampling.LANCZOS)
|
| 250 |
rotated_sunglasses = resized_sunglasses.rotate(angle, expand=True, resample=Image.Resampling.BICUBIC)
|
| 251 |
pos_x, pos_y = int(eye_center[0] - rotated_sunglasses.width / 2), int(eye_center[1] - rotated_sunglasses.height / 2)
|
| 252 |
+
|
| 253 |
pil_image.paste(rotated_sunglasses, (pos_x, pos_y), rotated_sunglasses)
|
| 254 |
+
|
| 255 |
return np.array(pil_image)
|
| 256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
# --- Main Processing Function ---
|
| 259 |
def process_image(image, filter_name, splash_color):
|
|
|
|
| 269 |
"HDR Effect": apply_hdr_effect, "Sunburst Glow": apply_sunburst_glow,
|
| 270 |
"Dreamy Glow": apply_dreamy_glow,
|
| 271 |
"Color Splash": lambda img: apply_color_splash(img, splash_color),
|
| 272 |
+
"Aviator Sunglasses": lambda img: apply_sunglasses(img, style="aviator"),
|
| 273 |
+
"Retro Square Sunglasses": lambda img: apply_sunglasses(img, style="retro_square"),
|
| 274 |
+
"Heart-Shaped Sunglasses": lambda img: apply_sunglasses(img, style="heart_shaped"),
|
|
|
|
|
|
|
| 275 |
"None": lambda img: img
|
| 276 |
}
|
| 277 |
|
|
|
|
| 292 |
filters_standard = ["Grayscale", "Sepia", "Invert", "Posterize", "Solarize", "Vignette", "Contour", "Sharpen"]
|
| 293 |
filters_artistic = ["Cartoon", "Sketch", "Pixelate"]
|
| 294 |
filters_advanced = ["N64-ify (Triangles)", "HDR Effect", "Color Splash", "Sunburst Glow", "Dreamy Glow"]
|
| 295 |
+
filters_face = ["Aviator Sunglasses", "Retro Square Sunglasses", "Heart-Shaped Sunglasses"]
|
| 296 |
|
| 297 |
all_filters = ["None"] + filters_standard + filters_artistic + filters_advanced + filters_face
|
| 298 |
|