#!/opt/homebrew/bin/python3.11 """ Face Registration Script Register new faces to the face database """ import sys import json import argparse import os import numpy as np import time from typing import Dict, Any, Optional sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) class FaceRegistration: def __init__(self): self.face_model = None self.face_database = {} self.database_path = None def load_models(self, use_mps: bool = False): """Load InsightFace models with MPS support""" try: import insightface from insightface.app import FaceAnalysis # Determine execution providers based on configuration providers = ["CPUExecutionProvider"] if use_mps: try: # Try to import MPS provider import onnxruntime as ort available_providers = ort.get_available_providers() if "CoreMLExecutionProvider" in available_providers: print( "[INFO] Using CoreMLExecutionProvider for MPS acceleration" ) providers = ["CoreMLExecutionProvider", "CPUExecutionProvider"] elif "CUDAExecutionProvider" in available_providers: print("[INFO] Using CUDAExecutionProvider") providers = ["CUDAExecutionProvider", "CPUExecutionProvider"] else: print("[INFO] MPS/CUDA not available, using CPU") providers = ["CPUExecutionProvider"] except ImportError: print("[WARNING] ONNX Runtime not available, using CPU") providers = ["CPUExecutionProvider"] print(f"[INFO] Using execution providers: {providers}") # Initialize face analysis app self.face_model = FaceAnalysis( name="buffalo_l", # or 'buffalo_s' for smaller model providers=providers, ) # For MPS/CoreML, we need to adjust context ctx_id = -1 # Default for CPU if use_mps and "CoreMLExecutionProvider" in providers: ctx_id = 0 # CoreML uses device 0 self.face_model.prepare(ctx_id=ctx_id, det_size=(640, 640)) print("[INFO] InsightFace models loaded successfully") return True except ImportError as e: print(f"[ERROR] Failed to import InsightFace: {e}") print("[INFO] Install with: pip install insightface") return False except Exception as e: print(f"[ERROR] Failed to load models: {e}") return False def load_database(self, database_path: str): """Load existing face database""" self.database_path = database_path if os.path.exists(database_path): try: with open(database_path, "r") as f: self.face_database = json.load(f) print(f"[INFO] Loaded {len(self.face_database)} faces from database") except Exception as e: print(f"[WARNING] Failed to load database: {e}") self.face_database = {} else: print("[INFO] Creating new face database") self.face_database = {} def save_database(self): """Save face database to file""" if not self.database_path: print("[ERROR] No database path specified") return False try: # Create directory if it doesn't exist os.makedirs(os.path.dirname(self.database_path), exist_ok=True) with open(self.database_path, "w") as f: json.dump(self.face_database, f, indent=2) print(f"[INFO] Saved {len(self.face_database)} faces to database") return True except Exception as e: print(f"[ERROR] Failed to save database: {e}") return False def register_face( self, image_path: str, name: str, metadata: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """Register a new face from image""" # Check if image exists if not os.path.exists(image_path): return { "success": False, "message": f"Image not found: {image_path}", "face_id": None, "embedding": None, "attributes": None, } # Load models if not already loaded if self.face_model is None: if not self.load_models(): return { "success": False, "message": "Failed to load face models", "face_id": None, "embedding": None, "attributes": None, } # Load image try: import cv2 image = cv2.imread(image_path) if image is None: return { "success": False, "message": f"Failed to load image: {image_path}", "face_id": None, "embedding": None, "attributes": None, } except ImportError: return { "success": False, "message": "OpenCV not installed", "face_id": None, "embedding": None, "attributes": None, } # Detect faces try: faces = self.face_model.get(image) if len(faces) == 0: return { "success": False, "message": "No faces detected in image", "face_id": None, "embedding": None, "attributes": None, } if len(faces) > 1: print(f"[WARNING] Multiple faces detected, using the first one") # Use the first face face = faces[0] # Get embedding embedding = face.embedding.tolist() if hasattr(face, "embedding") else None if embedding is None: return { "success": False, "message": "Failed to extract face embedding", "face_id": None, "embedding": None, "attributes": None, } # Get attributes attributes = {} if hasattr(face, "age") and face.age is not None: attributes["age"] = int(face.age) if hasattr(face, "gender") and face.gender is not None: attributes["gender"] = "female" if face.gender == 0 else "male" # Get pose if available pose = None if hasattr(face, "pose") and face.pose is not None: pose = { "yaw": float(face.pose[0]), "pitch": float(face.pose[1]), "roll": float(face.pose[2]), } # Generate face ID import uuid face_id = str(uuid.uuid4()) # Create face record face_record = { "face_id": face_id, "name": name, "embedding": embedding, "attributes": { "age": attributes.get("age"), "gender": attributes.get("gender"), "emotion": None, "glasses": None, "mask": None, "pose": pose, } if any([attributes.get("age"), attributes.get("gender"), pose]) else None, "metadata": metadata or {}, "registration_time": time.time(), "image_path": image_path, } # Add to database self.face_database[face_id] = face_record # Save database if not self.save_database(): return { "success": False, "message": "Failed to save face to database", "face_id": face_id, "embedding": embedding, "attributes": face_record["attributes"], } return { "success": True, "message": f"Face registered successfully as '{name}'", "face_id": face_id, "embedding": embedding, "attributes": face_record["attributes"], } except Exception as e: return { "success": False, "message": f"Face registration failed: {str(e)}", "face_id": None, "embedding": None, "attributes": None, } def list_faces(self) -> Dict[str, Any]: """List all registered faces""" faces = [] for face_id, face_data in self.face_database.items(): faces.append( { "face_id": face_id, "name": face_data.get("name", "Unknown"), "registration_time": face_data.get("registration_time"), "metadata": face_data.get("metadata", {}), } ) return { "success": True, "message": f"Found {len(faces)} registered faces", "faces": faces, } def delete_face(self, face_id: str) -> Dict[str, Any]: """Delete a face from database""" if face_id not in self.face_database: return {"success": False, "message": f"Face ID not found: {face_id}"} # Remove from database deleted_face = self.face_database.pop(face_id) # Save database if not self.save_database(): # Try to restore self.face_database[face_id] = deleted_face return { "success": False, "message": "Failed to save database after deletion", } return { "success": True, "message": f"Face '{deleted_face.get('name', 'Unknown')}' deleted successfully", } def main(): parser = argparse.ArgumentParser(description="Face Registration Tool") parser.add_argument("image_path", help="Path to face image") parser.add_argument("output_path", help="Output JSON path") parser.add_argument("name", help="Name for the registered face") parser.add_argument( "--metadata", "-m", help="Path to metadata JSON file", default="" ) parser.add_argument( "--database", "-d", help="Path to face database", default="face_database.json" ) parser.add_argument( "--list", "-l", help="List registered faces", action="store_true" ) parser.add_argument("--delete", help="Delete face by ID", default="") args = parser.parse_args() # Initialize registration registration = FaceRegistration() # Load database registration.load_database(args.database) # Handle list command if args.list: result = registration.list_faces() with open(args.output_path, "w") as f: json.dump(result, f, indent=2) print(result["message"]) return # Handle delete command if args.delete: result = registration.delete_face(args.delete) with open(args.output_path, "w") as f: json.dump(result, f, indent=2) print(result["message"]) return # Load metadata if provided metadata = {} if args.metadata and os.path.exists(args.metadata): try: with open(args.metadata, "r") as f: metadata = json.load(f) except Exception as e: print(f"[WARNING] Failed to load metadata: {e}") # Register face result = registration.register_face( image_path=args.image_path, name=args.name, metadata=metadata ) # Save result with open(args.output_path, "w") as f: json.dump(result, f, indent=2) print(result["message"]) if __name__ == "__main__": main()