from __future__ import annotations from typing import Optional import numpy as np from .aggregate import l2_normalize class OnnxFaceRecognizer: def __init__(self, model_path: str, expected_dim: int = 512) -> None: self.model_path = model_path self.expected_dim = int(expected_dim) self._sess = None self._input_name: Optional[str] = None self._output_name: Optional[str] = None def _ensure_session(self) -> None: if self._sess is not None: return try: import onnxruntime as ort except Exception as e: # pragma: no cover raise RuntimeError("onnxruntime is required for recognition") from e self._sess = ort.InferenceSession(self.model_path, providers=["CPUExecutionProvider"]) self._input_name = self._sess.get_inputs()[0].name self._output_name = self._sess.get_outputs()[0].name def embed_aligned_rgb112(self, aligned_rgb112: np.ndarray) -> np.ndarray: """aligned_rgb112: uint8 RGB 112x112x3. Return float32 (D,) L2-normalized.""" self._ensure_session() x = np.asarray(aligned_rgb112) if x.shape[:2] != (112, 112) or x.ndim != 3 or x.shape[2] != 3: raise ValueError("aligned image must be 112x112x3 RGB") if x.dtype != np.uint8: x = x.astype(np.uint8) x = x.astype(np.float32) x = (x - 127.5) / 128.0 x = np.transpose(x, (2, 0, 1)) # CHW x = np.expand_dims(x, axis=0) # NCHW out = self._sess.run([self._output_name], {self._input_name: x})[0] out = np.asarray(out, dtype=np.float32).reshape(-1) if out.size != self.expected_dim: raise ValueError(f"unexpected embedding dim: got {out.size}, expected {self.expected_dim}") return l2_normalize(out)