MakeUp_LipStick / app.py
ukzada's picture
Update app.py
b92b229 verified
Raw
History Blame Contribute Delete
9.83 kB
#!/usr/bin/env python3
"""
Lipstick Virtual Makeup App
Dedicated app for applying lipstick with various finishes and colors
Features: Multiple shades, Gloss/Matte/Shimmer finishes, Before/After preview
"""
import cv2
import numpy as np
import gradio as gr
from PIL import Image
import os
from landmarks import detect_landmarks, normalize_landmarks
# Lipstick color presets with RGB values
LIPSTICK_COLORS = {
"Classic Red": (0, 0, 255),
"Rose Pink": (203, 192, 255),
"Deep Burgundy": (75, 0, 130),
"Coral Orange": (0, 165, 255),
"Nude Beige": (180, 140, 200),
"Wine Red": (0, 0, 120),
"Peach Coral": (100, 165, 255),
"Berry": (128, 0, 128),
"Mauve": (150, 112, 224),
"Crimson": (60, 20, 220),
"Hot Pink": (180, 105, 255),
"Plum": (142, 69, 133),
}
# Landmark indices for lips
UPPER_LIP = [61, 185, 40, 39, 37, 0, 267, 269, 270, 408, 415, 272, 271, 268, 12, 38, 41, 42, 191, 78, 76]
LOWER_LIP = [61, 146, 91, 181, 84, 17, 314, 405, 320, 307, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95]
def apply_lipstick_with_finish(image, color_rgb, landmarks, alpha=0.5, finish="matte"):
"""
Apply lipstick to lips with different finishes.
Args:
image: Input image
color_rgb: RGB color tuple
landmarks: Facial landmarks
alpha: Opacity/intensity (0-1)
finish: "matte", "gloss", or "shimmer"
Returns:
Image with lipstick applied
"""
if landmarks is None:
return image
h, w = image.shape[:2]
mask = np.zeros_like(image)
# Get lip points
lip_points = normalize_landmarks(landmarks, h, w, UPPER_LIP + LOWER_LIP)
if len(lip_points) == 0:
return image
lip_points = lip_points.astype(np.int32)
# Fill lip region with color
cv2.fillPoly(mask, [lip_points], color_rgb)
# Apply finish-specific effects
if finish == "gloss":
# Glossy finish: higher alpha, add highlights
mask = cv2.GaussianBlur(mask, (15, 15), 3)
output = cv2.addWeighted(image, 1.0, mask, alpha * 1.2, 0)
# Add highlight effect on upper lip
highlight_mask = np.zeros_like(image)
upper_lip_points = normalize_landmarks(landmarks, h, w, UPPER_LIP)
if len(upper_lip_points) > 0:
upper_lip_points = upper_lip_points.astype(np.int32)
cv2.fillPoly(highlight_mask, [upper_lip_points], (255, 255, 255))
highlight_mask = cv2.GaussianBlur(highlight_mask, (25, 25), 8)
output = cv2.addWeighted(output, 1.0, highlight_mask, 0.15, 0)
elif finish == "shimmer":
# Shimmer finish: add sparkle effect
mask = cv2.GaussianBlur(mask, (13, 13), 3)
output = cv2.addWeighted(image, 1.0, mask, alpha, 0)
# Add shimmer particles
shimmer_mask = np.zeros_like(image)
cv2.fillPoly(shimmer_mask, [lip_points], (255, 255, 255))
# Create random shimmer points
np.random.seed(42) # For consistent shimmer pattern
lip_region = cv2.fillPoly(np.zeros((h, w), dtype=np.uint8), [lip_points], 255)
y_coords, x_coords = np.where(lip_region > 0)
if len(y_coords) > 0:
num_sparkles = min(len(y_coords) // 20, 100)
indices = np.random.choice(len(y_coords), num_sparkles, replace=False)
for idx in indices:
y, x = y_coords[idx], x_coords[idx]
cv2.circle(shimmer_mask, (x, y), 1, (255, 255, 255), -1)
shimmer_mask = cv2.GaussianBlur(shimmer_mask, (5, 5), 1)
output = cv2.addWeighted(output, 1.0, shimmer_mask, 0.2, 0)
else: # matte finish
# Matte finish: smooth, no shine
mask = cv2.GaussianBlur(mask, (15, 15), 3)
mask = cv2.GaussianBlur(mask, (7, 7), 2)
output = cv2.addWeighted(image, 1.0, mask, alpha * 0.9, 0)
return output
def process_lipstick(image, color_name, finish, intensity):
"""
Process image to apply lipstick with selected parameters.
Args:
image: Input image (PIL or numpy)
color_name: Name of the lipstick color
finish: Type of finish (matte/gloss/shimmer)
intensity: Intensity percentage (0-100)
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 = LIPSTICK_COLORS.get(color_name, LIPSTICK_COLORS["Classic Red"])
# Convert intensity percentage to alpha
alpha = intensity / 100.0 * 0.7 # Scale to reasonable range
# Apply lipstick
output = apply_lipstick_with_finish(img, color, landmarks, alpha=alpha, finish=finish.lower())
# Convert back to RGB
output_rgb = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
status = f"Applied {color_name} lipstick with {finish} finish at {intensity}% intensity"
return output_rgb, status
def create_comparison(original, processed):
"""Create a side-by-side before/after comparison."""
if original is None or processed is None:
return None
# Convert to numpy arrays if needed
if isinstance(original, Image.Image):
original = np.array(original)
if isinstance(processed, Image.Image):
processed = np.array(processed)
# Ensure same dimensions
if original.shape != processed.shape:
return processed
# Create side-by-side comparison
h, w = original.shape[:2]
comparison = np.zeros((h, w * 2, 3), dtype=np.uint8)
comparison[:, :w] = original
comparison[:, w:] = processed
# Add labels
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(comparison, "BEFORE", (20, 40), font, 1, (255, 255, 255), 2)
cv2.putText(comparison, "AFTER", (w + 20, 40), font, 1, (255, 255, 255), 2)
return comparison
# Create Gradio interface
with gr.Blocks(title="Lipstick Virtual Makeup", theme=gr.themes.Soft()) as demo:
gr.Markdown("# πŸ’‹ Lipstick Virtual Makeup App")
gr.Markdown("Apply professional lipstick effects with customizable colors and finishes")
with gr.Row():
with gr.Column(scale=1):
# Image upload
image_input = gr.Image(label="Upload Your Photo", type="pil")
# Lipstick color selection
gr.Markdown("### Choose Your Shade")
color_dropdown = gr.Dropdown(
choices=list(LIPSTICK_COLORS.keys()),
value="Classic Red",
label="Lipstick Color",
interactive=True
)
# Finish type
gr.Markdown("### Select Finish")
finish_radio = gr.Radio(
choices=["Matte", "Gloss", "Shimmer"],
value="Matte",
label="Finish Type",
interactive=True
)
# Intensity slider
gr.Markdown("### Adjust Intensity")
intensity_slider = gr.Slider(
minimum=0,
maximum=100,
value=60,
step=5,
label="Intensity (%)",
interactive=True
)
# Apply button
apply_btn = gr.Button("πŸ’„ Apply Lipstick", variant="primary", size="lg")
# Preview mode toggle
preview_checkbox = gr.Checkbox(
label="Show Before/After Comparison",
value=False
)
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=2
)
# Info box
gr.Markdown("""
### πŸ’‘ Tips for Best Results
- Use a well-lit, front-facing photo
- **Matte**: Classic, long-lasting look
- **Gloss**: Shiny, plump appearance
- **Shimmer**: Sparkly, party-ready finish
- Adjust intensity for subtle or bold looks
""")
# Store original and processed images
original_state = gr.State()
processed_state = gr.State()
# Process and store both images
def process_and_store(img, color, finish, intensity):
processed, status = process_lipstick(img, color, finish, intensity)
return processed, status, img, processed
# Handle preview mode toggle
def handle_preview(show_preview, original, processed):
if show_preview and original is not None and processed is not None:
return create_comparison(original, processed)
elif processed is not None:
return processed
return None
# Link apply button
apply_btn.click(
fn=process_and_store,
inputs=[image_input, color_dropdown, finish_radio, intensity_slider],
outputs=[image_output, status_text, original_state, processed_state]
)
# Link preview checkbox
preview_checkbox.change(
fn=handle_preview,
inputs=[preview_checkbox, original_state, processed_state],
outputs=[image_output]
)
if __name__ == "__main__":
print("\n" + "="*60)
print("🌸 LipStick Virtual Makeup App")
print("="*60 + "\n")
demo.launch()