"""Shared loader for the sae_embed nanobind module (SCRFD + ArcFace). sae_embed.FaceEmbedder loads both ONNX sessions once and exposes an embed(path) -> FaceResult method, avoiding the per-process model reload cost of spawning the embed_faces CLI binary for every image. resolve_arcface() exposes the same default-resolution logic load_embedder uses, so a caller can stamp the gallery it is about to write with the model that actually produced its embeddings (GR-004) — the resolved path, not the CLI argument, which is often None. """ import sys import os from pathlib import Path DEFAULT_ARCFACE = "arcface_w600k_r50.onnx" def resolve_arcface(models_dir: str, arcface: str | None = None) -> str: """The ArcFace/LVFace ONNX path load_embedder would use for these arguments. TRACES: GR-004 | SR-001 Single source of truth for "which model is this", so the stamp written into a gallery can never drift from the model loaded.""" return arcface if arcface else str(Path(models_dir) / DEFAULT_ARCFACE) def load_embedder(build_dir: str, models_dir: str, arcface: str | None = None, conf: float = 0.5, nms: float = 0.4, max_side: int = 500): """Import sae_embed from build_dir and construct a FaceEmbedder. Exits with a clear error if the module or models are missing — there is no subprocess fallback. """ build_path = Path(build_dir).resolve() sys.path.insert(0, str(build_path)) try: import sae_embed except ImportError as e: sys.exit( f"sae_embed module not found in {build_path}: {e}\n" f"Build it first: cmake --build {build_dir} --target sae_embed" ) models_path = Path(models_dir) detector_path = str(models_path / "scrfd_500m_bnkps.onnx") arcface_path = resolve_arcface(models_dir, arcface) for model, name in [(detector_path, "SCRFD"), (arcface_path, "ArcFace")]: if not Path(model).is_file(): sys.exit(f"{name} model not found: {model}\nRun: bash scripts/download_models.sh") # A TRT-backend build cannot load .onnx; it needs pre-built engines from # scripts/build_trt_engines.sh. # # These are passed only on request. The old comment here claimed they were # "ignored by ORT" — they are not. The ORT backend treats an engine path as # an instruction and raises, which is the right behaviour (silently ignoring # a requested engine would be worse), but it meant that merely HAVING a # populated trt_cache/ broke every ORT gallery build in the repo, with an # error naming a flag the caller never set. use_engines = os.environ.get("SAE_USE_TRT_ENGINES", "") not in ("", "0", "false") trt = Path(models_path).parent / "trt_cache" det_engine = trt / "scrfd.scrfd_500m_bnkps.640.fp16.engine" arc_engine = trt / f"arcface.{Path(arcface_path).stem}.b4.fp16.engine" return sae_embed.FaceEmbedder( detector_path, arcface_path, conf, nms, max_side, str(det_engine) if (use_engines and det_engine.is_file()) else "", str(arc_engine) if (use_engines and arc_engine.is_file()) else "", )