import gradio as gr import cv2 import numpy as np import mediapipe as mp import time import tempfile import os # --- MediaPipe Initialization --- try: mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_face_mesh.FaceMesh( static_image_mode=True, max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.4, min_tracking_confidence=0.4 ) print("MediaPipe Face Mesh initialized successfully.") except (ImportError, AttributeError): print("Error: Could not initialize MediaPipe Face Mesh. Is mediapipe installed correctly?") face_mesh = None # --- Helper Functions --- def get_face_mask_box(img, feather_pct, padding_pct): h, w = img.shape[:2] mask = np.zeros((h, w), dtype=np.uint8) results = face_mesh.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) if not results.multi_face_landmarks: return None, None pts = np.array([(int(p.x * w), int(p.y * h)) for p in results.multi_face_landmarks[0].landmark], np.int32) hull = cv2.convexHull(pts) cv2.fillConvexPoly(mask, hull, 255) x, y, bw, bh = cv2.boundingRect(hull) # calculate padding and feather in pixels pad = int(max(bw, bh) * padding_pct) x_pad = max(x - pad, 0) y_pad = max(y - pad, 0) x2 = min(x + bw + pad, w) y2 = min(y + bh + pad, h) mask_roi = mask[y_pad:y2, x_pad:x2] # inside feather: kernel proportional to face size if feather_pct > 0 and mask_roi.size > 0: k = int(min(mask_roi.shape[0], mask_roi.shape[1]) * feather_pct) if k % 2 == 0: k += 1 mask_roi = cv2.GaussianBlur(mask_roi, (k, k), 0) return mask_roi, (x_pad, y_pad, x2 - x_pad, y2 - y_pad) def cut_and_feather(img, feather): h, w = img.shape[:2] mask = np.zeros((h, w), dtype=np.uint8) results = face_mesh.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) if not results.multi_face_landmarks: return np.zeros_like(img), None, None pts = np.array([(int(p.x * w), int(p.y * h)) for p in results.multi_face_landmarks[0].landmark], np.int32) hull = cv2.convexHull(pts) cv2.fillConvexPoly(mask, hull, 255) # bounding box x, y, bw, bh = cv2.boundingRect(hull) # feather mask k = int(feather) if k > 0: mask = cv2.GaussianBlur(mask, (k*2+1, k*2+1), 0) # extract face ROI face_roi = img[y:y+bh, x:x+bw] mask_roi = mask[y:y+bh, x:x+bw] # apply mask fg = cv2.bitwise_and(face_roi, face_roi, mask=mask_roi) # prepare alpha alpha = mask_roi.astype(np.float32) / 255.0 # composite onto transparent background same size out = (fg.astype(np.float32) * alpha[..., None]).astype(np.uint8) return out, mask_roi, (x, y, bw, bh) def get_landmarks(img, landmark_step=1): if img is None or face_mesh is None: return None img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) try: results = face_mesh.process(img_rgb) except Exception: return None if not results.multi_face_landmarks: return None landmarks_mp = results.multi_face_landmarks[0] h, w, _ = img.shape pts = np.array([(pt.x * w, pt.y * h) for pt in landmarks_mp.landmark], dtype=np.float32) landmarks = pts[::landmark_step] if landmark_step > 1 else pts if not np.all(np.isfinite(landmarks)): return None corners = np.array([[0,0],[w-1,0],[0,h-1],[w-1,h-1]], dtype=np.float32) return np.vstack((landmarks, corners)) def calculate_delaunay_triangles(rect, points): if points is None or len(points)<3: return [] points[:,0] = np.clip(points[:,0], rect[0], rect[0]+rect[2]-1) points[:,1] = np.clip(points[:,1], rect[1], rect[1]+rect[3]-1) subdiv = cv2.Subdiv2D(rect) inserted = {} for i,p in enumerate(points): key = (int(p[0]), int(p[1])) if key not in inserted: try: subdiv.insert(key) inserted[key]=i except cv2.error: continue tris = subdiv.getTriangleList() delaunay=[] for t in tris: coords=[(int(t[0]),int(t[1])),(int(t[2]),int(t[3])),(int(t[4]),int(t[5]))] if all(rect[0]<=x