import cv2 import mediapipe as mp import numpy as np from typing import Tuple, Optional # Initialize MediaPipe face detection mp_face_detection = mp.solutions.face_detection mp_face_mesh = mp.solutions.face_mesh # For detailed landmarks def classify_face_shape(landmarks) -> str: """Determine face shape using geometric ratios.""" # Example: Calculate jaw-to-forehead ratio jaw_width = landmarks[234].x - landmarks[454].x # Left/right jaw points forehead_height = landmarks[10].y - landmarks[152].y # Forehead to chin ratio = jaw_width / forehead_height if ratio > 1.1: return "Square" elif ratio > 0.9: return "Round" else: return "Oval" def estimate_skin_tone(image, face_roi) -> str: """Analyze skin tone in the cheek region.""" x, y, w, h = face_roi cheek_region = image[y:y+h//2, x:x+w//2] # Get left cheek area # Convert to LAB color space for skin tone analysis lab = cv2.cvtColor(cheek_region, cv2.COLOR_BGR2LAB) l, a, b = np.mean(lab, axis=(0,1)) if l > 160: return "Fair" elif l > 120: return "Medium" else: return "Dark" def extract_features(image_path: str) -> Tuple[Optional[str], Optional[str], Optional[str]]: """Extract face attributes from an image.""" # Read and convert image image = cv2.imread(image_path) if image is None: return None, None, None image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) height, width = image.shape[:2] # Face detection with mp_face_detection.FaceDetection(min_detection_confidence=0.5) as detector: results = detector.process(image_rgb) if not results.detections: return None, None, None # Get face bounding box bbox = results.detections[0].location_data.relative_bounding_box x, y = int(bbox.xmin * width), int(bbox.ymin * height) w, h = int(bbox.width * width), int(bbox.height * height) # Face size classification face_size = "Large" if w > 300 else "Medium" if w > 200 else "Small" # Face mesh for detailed landmarks with mp_face_mesh.FaceMesh(static_image_mode=True) as mesh: mesh_results = mesh.process(image_rgb) if mesh_results.multi_face_landmarks: landmarks = mesh_results.multi_face_landmarks[0].landmark face_shape = classify_face_shape(landmarks) skin_tone = estimate_skin_tone(image, (x, y, w, h)) return face_shape, skin_tone, face_size return None, None, None # Fallback if mesh detection fails