import threading from typing import Any import insightface import numpy as np from PIL import Image import roop.globals from roop.typing import Frame FACE_ANALYSER = None THREAD_LOCK = threading.Lock() def get_face_analyser() -> Any: global FACE_ANALYSER with THREAD_LOCK: if FACE_ANALYSER is None: FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=roop.globals.execution_providers) FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640)) return FACE_ANALYSER def get_precise_face_mask(frame: Frame) -> Any: """ Get precise face mask using advanced segmentation (same as detect_face_and_forehead_no_hair). Returns both InsightFace detection and precise mask. """ try: # Import the precise detection function import sys import os sys.path.append(os.path.dirname(os.path.dirname(__file__))) from segmentation import detect_face_and_forehead_no_hair # Convert frame to PIL Image if isinstance(frame, np.ndarray): pil_image = Image.fromarray(frame) else: pil_image = frame # Get precise face mask (clean skin only) precise_mask = detect_face_and_forehead_no_hair(pil_image) # Also get InsightFace detection for face swapping compatibility insightface_faces = get_face_analyser().get(frame) return { 'precise_mask': precise_mask, 'insightface_faces': insightface_faces, 'has_face': precise_mask.sum() > 0 and len(insightface_faces) > 0 } except Exception as e: print(f"Precise face detection failed: {e}") # Fallback to regular InsightFace insightface_faces = get_face_analyser().get(frame) return { 'precise_mask': None, 'insightface_faces': insightface_faces, 'has_face': len(insightface_faces) > 0 } def get_one_face(frame: Frame) -> Any: """ Get one face with enhanced precision detection. """ # Get precise detection info face_info = get_precise_face_mask(frame) if face_info['has_face'] and face_info['insightface_faces']: try: # Select face (leftmost) for compatibility selected_face = min(face_info['insightface_faces'], key=lambda x: x.bbox[0]) # Add precise mask info to face object if face_info['precise_mask'] is not None: selected_face.precise_mask = face_info['precise_mask'] print(f"✅ Enhanced face detection: {face_info['precise_mask'].sum()} precise pixels") return selected_face except (ValueError, IndexError): return None # Fallback to original method face = get_face_analyser().get(frame) try: selected_face = min(face, key=lambda x: x.bbox[0]) return selected_face except ValueError: return None def get_many_faces(frame: Frame) -> Any: """ Get many faces with enhanced precision detection. """ # Get precise detection info face_info = get_precise_face_mask(frame) if face_info['has_face'] and face_info['insightface_faces']: faces = face_info['insightface_faces'] # Add precise mask info to all face objects if face_info['precise_mask'] is not None: for face in faces: face.precise_mask = face_info['precise_mask'] print(f"✅ Enhanced multi-face detection: {len(faces)} faces with precise masks") return faces # Fallback to original method try: return get_face_analyser().get(frame) except IndexError: return None def has_precise_face_mask(face_obj) -> bool: """ Check if face object has precise mask attached. """ return hasattr(face_obj, 'precise_mask') and face_obj.precise_mask is not None