Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Blush Virtual Makeup App | |
| Dedicated app for applying blush with customizable placement and styles | |
| Features: Multiple styles (Powder/Cream/Tint), Intensity control, Placement adjustment | |
| """ | |
| import cv2 | |
| import numpy as np | |
| import gradio as gr | |
| from PIL import Image | |
| import os | |
| from landmarks import detect_landmarks, normalize_landmarks | |
| # Blush color presets | |
| BLUSH_COLORS = { | |
| "Coral Pink": (66, 135, 245), | |
| "Rose Pink": (192, 135, 220), | |
| "Peach": (152, 165, 255), | |
| "Mauve Rose": (100, 70, 200), | |
| "Berry": (140, 50, 150), | |
| "Apricot": (140, 140, 230), | |
| "Dusty Rose": (150, 120, 200), | |
| "Terracotta": (80, 100, 180), | |
| "Plum": (120, 60, 160), | |
| "Natural Pink": (180, 150, 230), | |
| } | |
| # Landmark indices for cheeks | |
| CHEEKS = [425, 205] # Left and right cheek centers | |
| # Placement presets (offset adjustments from cheek center) | |
| PLACEMENT_STYLES = { | |
| "Classic (Apple of Cheek)": {"x_offset": 0, "y_offset": 0, "radius_mult": 1.0}, | |
| "High Cheekbone": {"x_offset": 0, "y_offset": -15, "radius_mult": 0.9}, | |
| "Contour (Lower Cheek)": {"x_offset": -10, "y_offset": 10, "radius_mult": 1.1}, | |
| "Lifted (Temple)": {"x_offset": -20, "y_offset": -20, "radius_mult": 0.85}, | |
| "Soft Drape": {"x_offset": -5, "y_offset": 5, "radius_mult": 1.2}, | |
| } | |
| # Blush style characteristics | |
| BLUSH_STYLES = { | |
| "Powder": { | |
| "opacity": 0.35, | |
| "blur_amount": 0.4, | |
| "gradient_sharpness": 1.0, | |
| "description": "Matte, buildable finish" | |
| }, | |
| "Cream": { | |
| "opacity": 0.45, | |
| "blur_amount": 0.5, | |
| "gradient_sharpness": 0.7, | |
| "description": "Dewy, blended finish" | |
| }, | |
| "Tint": { | |
| "opacity": 0.25, | |
| "blur_amount": 0.6, | |
| "gradient_sharpness": 0.5, | |
| "description": "Sheer, natural flush" | |
| }, | |
| } | |
| def apply_blush_advanced(image, color_rgb, landmarks, intensity=50, radius=40, | |
| style="Powder", placement="Classic (Apple of Cheek)"): | |
| """ | |
| Apply blush with advanced controls. | |
| Args: | |
| image: Input image (BGR format) | |
| color_rgb: RGB color tuple for blush | |
| landmarks: Facial landmarks | |
| intensity: Intensity percentage (0-100) | |
| radius: Base radius for blush application | |
| style: Blush style (Powder/Cream/Tint) | |
| placement: Placement style | |
| Returns: | |
| Image with blush applied | |
| """ | |
| if landmarks is None: | |
| return image | |
| h, w = image.shape[:2] | |
| mask = np.zeros_like(image, dtype=np.float32) | |
| # Get cheek points | |
| cheek_points = normalize_landmarks(landmarks, h, w, CHEEKS) | |
| # Get placement adjustments | |
| placement_info = PLACEMENT_STYLES.get(placement, PLACEMENT_STYLES["Classic (Apple of Cheek)"]) | |
| x_offset = placement_info["x_offset"] | |
| y_offset = placement_info["y_offset"] | |
| radius_mult = placement_info["radius_mult"] | |
| # Get style characteristics | |
| style_info = BLUSH_STYLES.get(style, BLUSH_STYLES["Powder"]) | |
| base_opacity = style_info["opacity"] | |
| blur_amount = style_info["blur_amount"] | |
| gradient_sharp = style_info["gradient_sharpness"] | |
| # Adjust radius based on placement and intensity | |
| effective_radius = int(radius * radius_mult) | |
| # Convert intensity to multiplier | |
| intensity_mult = intensity / 100.0 | |
| for point in cheek_points: | |
| x, y = int(point[0]) + x_offset, int(point[1]) + y_offset | |
| # Ensure within bounds | |
| x = max(effective_radius, min(w - effective_radius, x)) | |
| y = max(effective_radius, min(h - effective_radius, y)) | |
| # Create circular blush with gradient | |
| y_min = max(0, y - effective_radius) | |
| y_max = min(h, y + effective_radius + 1) | |
| x_min = max(0, x - effective_radius) | |
| x_max = min(w, x + effective_radius + 1) | |
| yy, xx = np.ogrid[y_min:y_max, x_min:x_max] | |
| dist = np.sqrt((yy - y) ** 2 + (xx - x) ** 2) | |
| # Create gradient based on style | |
| gradient = np.zeros_like(dist, dtype=np.float32) | |
| valid = dist <= effective_radius | |
| # Different gradient functions for different styles | |
| if style == "Powder": | |
| # Sharper gradient for powder | |
| gradient[valid] = (1.0 - (dist[valid] / effective_radius)) ** gradient_sharp | |
| elif style == "Cream": | |
| # Smoother gradient for cream | |
| gradient[valid] = (1.0 + np.cos(np.pi * dist[valid] / effective_radius)) / 2.0 | |
| else: # Tint | |
| # Very soft gradient for tint | |
| gradient[valid] = np.exp(-2 * (dist[valid] / effective_radius) ** 2) | |
| # Apply color with gradient | |
| for c in range(3): | |
| color_val = color_rgb[c] | |
| mask[y_min:y_max, x_min:x_max, c] = np.maximum( | |
| mask[y_min:y_max, x_min:x_max, c], | |
| color_val * gradient | |
| ) | |
| # Apply blur based on style | |
| blur_radius = int(effective_radius * blur_amount) | |
| if blur_radius % 2 == 0: | |
| blur_radius += 1 | |
| blur_radius = max(3, blur_radius) | |
| mask = cv2.GaussianBlur(mask, (blur_radius, blur_radius), blur_radius // 3) | |
| # Convert to uint8 | |
| mask = mask.astype(np.uint8) | |
| # Apply with intensity and style-specific opacity | |
| alpha = base_opacity * intensity_mult | |
| output = cv2.addWeighted(image, 1.0, mask, alpha, 0) | |
| return output | |
| def process_blush(image, color_name, style, placement, intensity, radius): | |
| """ | |
| Process image to apply blush. | |
| Args: | |
| image: Input image | |
| color_name: Name of blush color | |
| style: Blush style (Powder/Cream/Tint) | |
| placement: Placement style | |
| intensity: Intensity percentage | |
| radius: Application radius | |
| Returns: | |
| Tuple of (processed_image, status_message) | |
| """ | |
| if image is None: | |
| return None, "No image provided" | |
| # Convert PIL to OpenCV format | |
| if isinstance(image, Image.Image): | |
| img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) | |
| else: | |
| img = image.copy() | |
| # Detect landmarks | |
| landmarks = detect_landmarks(img) | |
| if landmarks is None: | |
| return cv2.cvtColor(img, cv2.COLOR_BGR2RGB), "No face detected" | |
| # Get color | |
| color = BLUSH_COLORS.get(color_name, BLUSH_COLORS["Rose Pink"]) | |
| # Apply blush | |
| output = apply_blush_advanced( | |
| img, color, landmarks, | |
| intensity=intensity, | |
| radius=radius, | |
| style=style, | |
| placement=placement | |
| ) | |
| # Convert back to RGB | |
| output_rgb = cv2.cvtColor(output, cv2.COLOR_BGR2RGB) | |
| style_info = BLUSH_STYLES.get(style, BLUSH_STYLES["Powder"]) | |
| status = (f"Applied {color_name} blush in {style} style " | |
| f"({style_info['description']}) at {intensity}% intensity " | |
| f"with {placement} placement") | |
| return output_rgb, status | |
| # Create Gradio interface | |
| with gr.Blocks(title="Blush Virtual Makeup", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# πΈ Blush Virtual Makeup App") | |
| gr.Markdown("Apply professional blush with customizable placement, intensity, and finish styles") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # Image upload | |
| image_input = gr.Image(label="Upload Your Photo", type="pil") | |
| # Color selection | |
| gr.Markdown("### π¨ Choose Your Color") | |
| color_dropdown = gr.Dropdown( | |
| choices=list(BLUSH_COLORS.keys()), | |
| value="Rose Pink", | |
| label="Blush Color", | |
| interactive=True | |
| ) | |
| # Style selection | |
| gr.Markdown("### π Select Style") | |
| style_radio = gr.Radio( | |
| choices=list(BLUSH_STYLES.keys()), | |
| value="Powder", | |
| label="Blush Type", | |
| interactive=True | |
| ) | |
| style_desc = gr.Markdown( | |
| f"*{BLUSH_STYLES['Powder']['description']}*", | |
| visible=True | |
| ) | |
| # Placement selection | |
| gr.Markdown("### π Placement") | |
| placement_dropdown = gr.Dropdown( | |
| choices=list(PLACEMENT_STYLES.keys()), | |
| value="Classic (Apple of Cheek)", | |
| label="Placement Style", | |
| interactive=True | |
| ) | |
| # Intensity control | |
| gr.Markdown("### ποΈ Intensity") | |
| intensity_slider = gr.Slider( | |
| minimum=0, | |
| maximum=100, | |
| value=50, | |
| step=5, | |
| label="Intensity (%)", | |
| interactive=True | |
| ) | |
| # Radius control | |
| gr.Markdown("### π Size") | |
| radius_slider = gr.Slider( | |
| minimum=20, | |
| maximum=80, | |
| value=45, | |
| step=5, | |
| label="Blush Size (radius in pixels)", | |
| interactive=True | |
| ) | |
| # Apply button | |
| apply_btn = gr.Button("πΈ Apply Blush", variant="primary", size="lg") | |
| with gr.Column(scale=1): | |
| # Output image | |
| image_output = gr.Image(label="Result", type="pil") | |
| # Status message | |
| status_text = gr.Textbox( | |
| label="Status", | |
| interactive=False, | |
| lines=3 | |
| ) | |
| # Info guide | |
| gr.Markdown(""" | |
| ### π Style Guide | |
| **Powder Blush** | |
| - Matte finish | |
| - Buildable coverage | |
| - Long-lasting | |
| - Best for oily skin | |
| **Cream Blush** | |
| - Dewy finish | |
| - Blended look | |
| - Natural glow | |
| - Best for dry skin | |
| **Tint Blush** | |
| - Sheer coverage | |
| - Natural flush | |
| - Very subtle | |
| - Best for minimal makeup | |
| ### π‘ Placement Tips | |
| - **Classic**: Smile and apply to apple of cheek | |
| - **High Cheekbone**: For lifted, sculpted look | |
| - **Contour**: Add warmth and definition | |
| - **Lifted**: Temple placement for face-lifting effect | |
| - **Soft Drape**: Modern, soft-focus look | |
| """) | |
| # Update style description when style changes | |
| def update_style_desc(style): | |
| desc = BLUSH_STYLES.get(style, BLUSH_STYLES["Powder"])["description"] | |
| return f"*{desc}*" | |
| style_radio.change( | |
| fn=update_style_desc, | |
| inputs=[style_radio], | |
| outputs=[style_desc] | |
| ) | |
| # Apply blush | |
| apply_btn.click( | |
| fn=process_blush, | |
| inputs=[image_input, color_dropdown, style_radio, placement_dropdown, | |
| intensity_slider, radius_slider], | |
| outputs=[image_output, status_text] | |
| ) | |
| # Example gallery (optional - can be populated with sample results) | |
| gr.Markdown(""" | |
| ### π― Quick Start | |
| 1. Upload a clear, front-facing photo | |
| 2. Choose your desired blush color | |
| 3. Select your preferred style (Powder/Cream/Tint) | |
| 4. Adjust placement to customize your look | |
| 5. Fine-tune intensity and size | |
| 6. Click "Apply Blush" to see results | |
| """) | |
| if __name__ == "__main__": | |
| print("\n" + "="*60) | |
| print("πΈ Blush Virtual Makeup App") | |
| print("="*60 + "\n") | |
| demo.launch() |