import base64 import io import logging from typing import Any, Tuple, Optional import numpy as np import cv2 from PIL import Image, UnidentifiedImageError from deepface import DeepFace logger = logging.getLogger(__name__)\ MAX_IMAGE_BYTES = 10 * 1024 * 1024 class FaceVerificationError(Exception): """Raised when face verification cannot be completed.""" def load_image_from_base64(base64_str): """ Converts a base64 encoded image string to a NumPy array in RGB format. Parameters: base64_str (str): The base64-encoded image string. It may optionally include a header like "data:image/jpeg;base64,". Returns: np.ndarray: The image as a NumPy array in RGB format. """ if ',' in base64_str: base64_str = base64_str.split(',')[1] image_data = base64.b64decode(base64_str) image = Image.open(io.BytesIO(image_data)) return cv2.cvtColor(np.array(image), cv2.COLOR_BGR2RGB) def compare_id_images( id_card_image_base64: str, id_holder_image_base64: str, threshold: float = 0.6, model_name: str = "Facenet512", detector: str = "yolov11s", distance_metric: str = "cosine", ) -> dict[str, Any]: """ Compare two face images (base64-encoded) using DeepFace and determine whether they belong to the same person. Parameters: id_card_image_base64 (str): Base64-encoded image of the ID card face. id_holder_image_base64 (str): Base64-encoded image of the ID holder's face. threshold (float): Maximum allowed distance for a match. model_name (str): DeepFace model to use (e.g. "VGG-Face", "ArcFace", "SFace"). detector (str): Face detector backend (e.g. "opencv", "retinaface", "yolov11n"). distance_metric (str): Similarity metric ("cosine", "euclidean", "euclidean_l2"). Returns: (verified, distance): - verified (bool): Whether the faces match. - distance (float | None): Similarity distance, or None on failure. Raises: FaceVerificationError: If images are invalid or DeepFace fails. """ if not id_card_image_base64 or not id_holder_image_base64: raise FaceVerificationError("Both ID card and holder images are required") if threshold <= 0: raise ValueError("Threshold must be positive") try: id_card_image = load_image_from_base64(id_card_image_base64) id_holder_image = load_image_from_base64(id_holder_image_base64) except ValueError as exc: raise FaceVerificationError(str(exc)) from exc try: result: dict[str, Any] = DeepFace.verify( id_card_image, id_holder_image, model_name=model_name, detector_backend=detector, distance_metric=distance_metric, align=True, threshold=threshold, ) except Exception as exc: msg = str(exc).lower() if "face could not be detected" in msg: return { "status": "no_face_detected", "verified": False, "distance": None } raise FaceVerificationError("Face verification failed") from exc verified = bool(result.get("verified", False)) distance = result.get("distance") return { "status": "ok", "verified": verified, "distance": float(distance) }