import numpy as np import cv2 import torch from PIL import Image import os from .face_recognition import Siamese, FaceClassifier, trnscm, classes current_path = os.path.dirname(os.path.abspath(__file__)) _SIAMESE = None _CLASSIFIER = None _DEVICE = None def _to_bgr_np(img): if img is None: return None if isinstance(img, str): return cv2.imread(img) if isinstance(img, Image.Image): rgb = np.array(img.convert("RGB")) return cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) if isinstance(img, np.ndarray): arr = img if arr.ndim == 2: return cv2.cvtColor(arr, cv2.COLOR_GRAY2BGR) if arr.ndim == 3 and arr.shape[2] == 4: arr = arr[:, :, :3] if arr.ndim == 3 and arr.shape[2] == 3: return cv2.cvtColor(arr, cv2.COLOR_RGB2BGR) return arr return None def _load_models(): global _SIAMESE, _CLASSIFIER, _DEVICE if _SIAMESE is not None and _CLASSIFIER is not None: return _SIAMESE, _CLASSIFIER, _DEVICE _DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") siamese_path = os.path.join(current_path, "siamese_model.t7") clf_path = os.path.join(current_path, "face_classifier_model.pth") if not os.path.exists(siamese_path): raise FileNotFoundError(f"Missing siamese model: {siamese_path}") if not os.path.exists(clf_path): raise FileNotFoundError(f"Missing classifier model: {clf_path}") siamese = Siamese().to(_DEVICE) ckpt = torch.load(siamese_path, map_location=_DEVICE) if isinstance(ckpt, dict) and "net_dict" in ckpt: siamese.load_state_dict(ckpt["net_dict"], strict=True) else: siamese.load_state_dict(ckpt, strict=True) siamese.eval() classifier = FaceClassifier(input_dim=5, num_classes=len(classes)).to(_DEVICE) classifier.load_state_dict(torch.load(clf_path, map_location=_DEVICE), strict=True) classifier.eval() _SIAMESE, _CLASSIFIER = siamese, classifier return _SIAMESE, _CLASSIFIER, _DEVICE def detected_face(image_bgr): face_haar = os.path.join(current_path, "haarcascade_frontalface_default.xml") face_cascade = cv2.CascadeClassifier(face_haar) if face_cascade.empty(): return 0 gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) if faces is None or len(faces) == 0: return 0 x, y, w, h = max(faces, key=lambda f: f[2]*f[3]) crop = gray[y:y+h, x:x+w] return Image.fromarray(crop) def get_similarity(img1, img2): siamese, classifier, device = _load_models() i1 = _to_bgr_np(img1) i2 = _to_bgr_np(img2) if i1 is None or i2 is None: return 0.0 f1 = detected_face(i1) f2 = detected_face(i2) if f1 == 0: f1 = Image.fromarray(cv2.cvtColor(i1, cv2.COLOR_BGR2GRAY)) if f2 == 0: f2 = Image.fromarray(cv2.cvtColor(i2, cv2.COLOR_BGR2GRAY)) t1 = trnscm(f1).unsqueeze(0).to(device) t2 = trnscm(f2).unsqueeze(0).to(device) with torch.no_grad(): e1 = siamese.forward_once(t1) e2 = siamese.forward_once(t2) dist = torch.nn.functional.pairwise_distance(e1, e2).item() return float(dist) def get_face_class(img1): siamese, classifier, device = _load_models() i1 = _to_bgr_np(img1) if i1 is None: return "UNKNOWN" f1 = detected_face(i1) if f1 == 0: f1 = Image.fromarray(cv2.cvtColor(i1, cv2.COLOR_BGR2GRAY)) t1 = trnscm(f1).unsqueeze(0).to(device) with torch.no_grad(): emb = siamese.forward_once(t1) logits = classifier(emb) pred = int(torch.argmax(logits, dim=1).item()) if pred < 0 or pred >= len(classes): return "UNKNOWN" return str(classes[pred])