faster calibration curve generation
jellyfin intergration
This commit is contained in:
+13
-35
@@ -8,8 +8,7 @@ Usage:
|
||||
--arcface models/arcface_w600k_r50.onnx \
|
||||
--gt eval/gt.json \
|
||||
--output eval/predictions_r50.json \
|
||||
[--yunet models/face_detection_yunet_2023mar.onnx] \
|
||||
[--embed-bin build/embed_faces]
|
||||
[--build-dir build]
|
||||
|
||||
Input (--gt): list of {"crop": <path>, "imdb_id": <str>, "actor_name": <str>}
|
||||
Output: list of {"crop", "gt", "pred", "similarity", "detection_failed", "all_scores"}
|
||||
@@ -17,11 +16,12 @@ Output: list of {"crop", "gt", "pred", "similarity", "detection_failed", "all_sc
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from sae_embed_loader import load_embedder
|
||||
|
||||
|
||||
def load_gallery(path: str) -> dict[str, dict]:
|
||||
"""Return {imdb_id: {"name": str, "embeddings": [[float]]}}."""
|
||||
@@ -35,22 +35,6 @@ def dot(a: list[float], b: list[float]) -> float:
|
||||
return sum(x * y for x, y in zip(a, b))
|
||||
|
||||
|
||||
def embed_images(paths: list[Path], embed_bin: str, yunet: str, arcface: str) -> list[dict | None]:
|
||||
if not paths:
|
||||
return []
|
||||
cmd = [embed_bin, "--yunet", yunet, "--arcface", arcface] + [str(p) for p in paths]
|
||||
try:
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[error] embed_faces failed:\n{e.stderr}", file=sys.stderr)
|
||||
return [None] * len(paths)
|
||||
try:
|
||||
return json.loads(proc.stdout)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"[error] embed_faces JSON parse error: {e}", file=sys.stderr)
|
||||
return [None] * len(paths)
|
||||
|
||||
|
||||
def match(embedding: list[float], gallery: dict[str, dict]) -> tuple[str, float, dict[str, float]]:
|
||||
"""Return (best_imdb_id, best_similarity, {imdb_id: similarity})."""
|
||||
scores: dict[str, float] = {}
|
||||
@@ -67,10 +51,14 @@ def main():
|
||||
p.add_argument("--arcface", required=True)
|
||||
p.add_argument("--gt", required=True)
|
||||
p.add_argument("--output", required=True)
|
||||
p.add_argument("--yunet", default="models/face_detection_yunet_2023mar.onnx")
|
||||
p.add_argument("--embed-bin", default="build/embed_faces")
|
||||
p.add_argument("--build-dir", default="build",
|
||||
help="Build directory containing the sae_embed module (default: build)")
|
||||
p.add_argument("--models-dir", default="models",
|
||||
help="Directory containing ONNX models (default: models/)")
|
||||
args = p.parse_args()
|
||||
|
||||
embedder = load_embedder(args.build_dir, args.models_dir, args.arcface)
|
||||
|
||||
gallery = load_gallery(args.gallery)
|
||||
print(f"[eval] gallery: {len(gallery)} actors", file=sys.stderr)
|
||||
|
||||
@@ -78,29 +66,19 @@ def main():
|
||||
gt_entries = json.load(f)
|
||||
print(f"[eval] probe crops: {len(gt_entries)}", file=sys.stderr)
|
||||
|
||||
# Batch all crops in one embed_faces call to amortise startup cost
|
||||
crop_paths = [Path(e["crop"]) for e in gt_entries]
|
||||
missing = [p for p in crop_paths if not p.exists()]
|
||||
if missing:
|
||||
print(f"[warn] {len(missing)} crop(s) not found on disk, skipping", file=sys.stderr)
|
||||
|
||||
results_raw = embed_images(
|
||||
[p for p in crop_paths if p.exists()],
|
||||
args.embed_bin, args.yunet, args.arcface
|
||||
)
|
||||
|
||||
# Re-index results back to original list (missing files get None)
|
||||
raw_iter = iter(results_raw)
|
||||
embed_results: list[dict | None] = []
|
||||
for p in crop_paths:
|
||||
embed_results.append(next(raw_iter) if p.exists() else None)
|
||||
embed_results = [embedder.embed(str(p)) if p.exists() else None for p in crop_paths]
|
||||
|
||||
predictions = []
|
||||
n_det_fail = 0
|
||||
n_correct = 0
|
||||
|
||||
for entry, result in zip(gt_entries, embed_results):
|
||||
detection_failed = result is None or result.get("embedding") is None
|
||||
detection_failed = result is None or not result.ok
|
||||
if detection_failed:
|
||||
n_det_fail += 1
|
||||
predictions.append({
|
||||
@@ -113,7 +91,7 @@ def main():
|
||||
})
|
||||
continue
|
||||
|
||||
pred_id, sim, all_scores = match(result["embedding"], gallery)
|
||||
pred_id, sim, all_scores = match(result.embedding, gallery)
|
||||
correct = pred_id == entry["imdb_id"]
|
||||
if correct:
|
||||
n_correct += 1
|
||||
|
||||
Reference in New Issue
Block a user