Spaces:
Runtime error
Runtime error
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| from mediapipe.tasks import python | |
| from mediapipe.tasks.python import vision | |
| import gradio as gr | |
| # Initialize MediaPipe hair segmentation model | |
| MODEL_PATH = "hair_segmenter.tflite" | |
| base_options = python.BaseOptions(model_asset_path=MODEL_PATH) | |
| options = vision.ImageSegmenterOptions( | |
| base_options=base_options, | |
| running_mode=vision.RunningMode.IMAGE | |
| ) | |
| segmenter = vision.ImageSegmenter.create_from_options(options) | |
| def apply_hair_color(frame, mask, color, alpha=0.5, preserve_highlights=0.3): | |
| """Apply realistic hair color with preserved highlights""" | |
| lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) | |
| l, a, b = cv2.split(lab) | |
| highlight_mask = (l > 180).astype(np.float32) * preserve_highlights | |
| hair_mask = mask.astype(np.uint8) | |
| colored_hair = np.zeros_like(frame) | |
| colored_hair[:] = color[::-1] # BGR format | |
| colored_lab = cv2.cvtColor(colored_hair, cv2.COLOR_BGR2LAB) | |
| colored_lab[:,:,0] = l * (1 + highlight_mask) | |
| blended_lab = cv2.addWeighted(lab, 1-alpha, colored_lab, alpha, 0) | |
| colored_result = cv2.cvtColor(blended_lab, cv2.COLOR_LAB2BGR) | |
| inverse_mask = cv2.bitwise_not(hair_mask) | |
| background = cv2.bitwise_and(frame, frame, mask=inverse_mask) | |
| hair_region = cv2.bitwise_and(colored_result, colored_result, mask=hair_mask) | |
| return cv2.add(background, hair_region) | |
| def process_frame(frame, color_name, custom_color, intensity, highlights): | |
| """Process webcam frame with hair coloring""" | |
| # Professional hair color presets | |
| hair_colors = { | |
| "Chocolate Brown": [139, 69, 19], | |
| "Light Golden Brown": [222, 184, 135], | |
| "Soft Black": [51, 51, 51], | |
| "Golden Blonde": [218, 165, 32], | |
| "Dark Auburn": [139, 0, 0], | |
| "Deep Brown": [85, 46, 10], | |
| "Light Auburn": [205, 133, 63], | |
| "Medium Auburn": [165, 42, 42], | |
| "Platinum Blonde": [240, 220, 180], | |
| "Sienna Brown": [160, 82, 45] | |
| } | |
| # Get selected color | |
| if color_name == "Custom": | |
| # Convert hex to RGB | |
| selected_color = [int(custom_color[1:][i:i+2], 16) for i in (0, 2, 4)] | |
| else: | |
| selected_color = hair_colors[color_name] | |
| # Convert frame for MediaPipe | |
| frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb) | |
| # Perform hair segmentation | |
| segmentation_result = segmenter.segment(mp_image) | |
| if segmentation_result.confidence_masks: | |
| # Process mask | |
| mask = segmentation_result.confidence_masks[0].numpy_view() | |
| mask_resized = cv2.resize(mask, (frame.shape[1], frame.shape[0])) | |
| binary_mask = (mask_resized > 0.3).astype(np.uint8) * 255 | |
| binary_mask = cv2.bitwise_not(binary_mask) | |
| # Clean up mask | |
| kernel = np.ones((5,5), np.uint8) | |
| binary_mask = cv2.erode(binary_mask, kernel, iterations=1) | |
| binary_mask = cv2.dilate(binary_mask, kernel, iterations=2) | |
| # Apply hair color | |
| colored_frame = apply_hair_color( | |
| frame, | |
| binary_mask, | |
| selected_color, | |
| alpha=intensity, | |
| preserve_highlights=highlights | |
| ) | |
| return cv2.cvtColor(colored_frame, cv2.COLOR_BGR2RGB) | |
| return frame_rgb | |
| def create_interface(): | |
| """Create Gradio interface""" | |
| css = """ | |
| .my-group {max-width: 800px !important; max-height: 800px !important;} | |
| .my-column {display: flex !important; justify-content: center !important; align-items: center !important;} | |
| """ | |
| with gr.Blocks(css=css) as demo: | |
| gr.Markdown("# Virtual Hair Color Try-On") | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Color selection controls | |
| color_dropdown = gr.Dropdown( | |
| choices=["Chocolate Brown", "Light Golden Brown", "Soft Black", | |
| "Golden Blonde", "Dark Auburn", "Deep Brown", "Light Auburn", | |
| "Medium Auburn", "Platinum Blonde", "Sienna Brown", "Custom"], | |
| value="Chocolate Brown", | |
| label="Choose Hair Color" | |
| ) | |
| custom_color = gr.ColorPicker( | |
| label="Custom Color", | |
| visible=False | |
| ) | |
| # Advanced controls | |
| intensity = gr.Slider( | |
| minimum=0.0, | |
| maximum=1.0, | |
| value=0.8, | |
| step=0.1, | |
| label="Color Intensity" | |
| ) | |
| highlights = gr.Slider( | |
| minimum=0.0, | |
| maximum=1.0, | |
| value=0.3, | |
| step=0.1, | |
| label="Preserve Natural Highlights" | |
| ) | |
| with gr.Column(): | |
| # Modified WebRTC component for live preview | |
| webrtc = gr.Image( | |
| label="Live Preview", | |
| source="webcam", | |
| streaming=True, | |
| type="numpy" | |
| ) | |
| # Show/hide custom color picker based on selection | |
| def update_color_picker(choice): | |
| return gr.ColorPicker(visible=(choice == "Custom")) | |
| color_dropdown.change( | |
| fn=update_color_picker, | |
| inputs=[color_dropdown], | |
| outputs=[custom_color] | |
| ) | |
| # Stream processing with continuous updates | |
| webrtc.stream( | |
| fn=process_frame, | |
| inputs=[ | |
| webrtc, | |
| color_dropdown, | |
| custom_color, | |
| intensity, | |
| highlights | |
| ], | |
| outputs=[webrtc], | |
| stream_every=0.1 # Process frame every 0.1 seconds for smooth updates | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_interface() | |
| demo.queue() | |
| demo.launch() |