#!/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()