#!/usr/bin/env python3 """Integrity check on the labelled set, before it is used as ground truth. TRACES: VR-013 | PR-002 VR-013's ground truth is hand-sorted rather than propagated by embedding similarity, because propagation would keep only the faces the embedder already gets right and silently drop the ones the sweep exists to find. This script is what makes that claim checkable, so it is part of the requirement rather than a helper of it. Checks, loudest failure first: 1. INDEX INTEGRITY. Each crop's embedding is taken by re-detecting its source frame and indexing with the manifest's `idx`. If detection order is not reproducible, the thumbnail you sorted and the embedding that gets scored are different faces — you would see a correct picture and score the wrong person, with nothing to signal it. Every crop's re-detected bbox is compared against the manifest's. 2. NO CROP IN TWO FOLDERS, and every manifest entry accounted for — so a move that half-completed cannot silently duplicate or drop a label. 3. ALIGNMENT. The 112x112 warp is what the embedder actually sees; the thumbnail is only context for your eyes. verify_.jpg pairs them: context-with-box on top, the real aligned crop beneath. A profile face whose alignment has collapsed is obvious there and nowhere else. 4. SEPARATION. Per person, the calibrated P of their own crops against the other people's, using the global gallery sigmoid. A label set where someone matches another person better than themselves is mislabelled. Nothing here changes a label. It reports. """ import sys, glob, json, os import numpy as np import cv2 sys.path.insert(0, "/home/dtourolle/Development/Jray-project/scene-actor-extraction/build-ort") import sae_embed ROOT = "/home/dtourolle/Development/Jray-project/scene-actor-extraction/" M = ROOT + "models/" EMBEDDER = M + "LVFace-B_Glint360K.onnx" GALLERY = ROOT + "gallery_lvface.h5" CLIPS = ["5157344", "5157339"] THUMB = 130 COLS = 10 eng = sae_embed.FaceEmbedder(detector_model=M + "scrfd_500m_bnkps.onnx", arcface_model=EMBEDDER, conf=0.5, nms=0.4, max_side=0) fail = 0 rows = [] for clip in CLIPS: man = {m["file"]: m for m in json.load(open(f"labelling/{clip}/manifest.json"))} # where each crop currently sits -> its label placed = {} for path in glob.glob(f"labelling/{clip}/*/*.jpg"): person = os.path.basename(os.path.dirname(path)) if person in ("discard", "unsorted"): continue # not people; scoring them would invent an extra identity fname = os.path.basename(path) if fname in placed: print(f"[FAIL] {fname} appears in both {placed[fname][0]} and {person}") fail += 1 placed[fname] = (person, path) missing = set(man) - set(placed) extra = set(placed) - set(man) if missing: print(f"[warn] {clip}: {len(missing)} manifest crops not in any folder") if extra: print(f"[FAIL] {clip}: {len(extra)} files with no manifest entry: " f"{sorted(extra)[:3]}") fail += 1 # index integrity + alignment, frame by frame by_frame = {} for fname, (person, path) in placed.items(): if fname in man: by_frame.setdefault(man[fname]["frame"], []).append((fname, person, path)) bad_idx = 0 for frame, items in sorted(by_frame.items()): img = cv2.imread(f"frames/d{clip}_{frame}.png") if img is None: print(f"[FAIL] missing frames/d{clip}_{frame}.png") fail += 1 continue dets = eng.detect(img) for fname, person, path in items: m = man[fname] i = m["idx"] if i >= len(dets): print(f"[FAIL] {fname}: idx {i} >= {len(dets)} detections now") bad_idx += 1 continue got = [float(v) for v in dets[i].bbox] want = m["bbox"] if max(abs(a - b) for a, b in zip(got, want)) > 1.0: print(f"[FAIL] {fname}: manifest bbox {[round(v) for v in want]} " f"!= re-detected {[round(v) for v in got]}") bad_idx += 1 continue lm = np.array(dets[i].landmarks, dtype=np.float32).reshape(5, 2) crop = sae_embed.align_face(img, lm) if crop is None: print(f"[warn] {fname}: alignment degenerate, no crop reaches the embedder") continue rows.append({"clip": clip, "person": person, "file": fname, "path": path, "px": m["px"], "aligned": np.asarray(crop), "emb": np.asarray(eng.embed_crop(crop), dtype=np.float32)}) fail += bad_idx print(f"[{clip}] {len(placed)} placed, {len(by_frame)} frames, " f"index mismatches: {bad_idx}") if not rows: sys.exit("nothing to verify") # ── separation, through the global gallery sigmoid ─────────────────────────── cal = sae_embed.gallery_calibration(GALLERY) E = np.stack([r["emb"] for r in rows]) people = sorted({r["person"] for r in rows}) lab = np.array([people.index(r["person"]) for r in rows]) S = E @ E.T np.fill_diagonal(S, -1.0) print(f"\n{'person':>8}{'crops':>7}{'344':>6}{'339':>6}" f"{'P(self)':>10}{'P(other)':>10}{'worst':>8}") for k, p in enumerate(people): mine = np.where(lab == k)[0] if len(mine) < 2: continue self_sim = S[np.ix_(mine, mine)].max(axis=1) other_sim = S[np.ix_(mine, np.where(lab != k)[0])].max(axis=1) p_self = np.array([cal.probability(float(s)) for s in self_sim]) p_other = np.array([cal.probability(float(s)) for s in other_sim]) n344 = sum(1 for i in mine if rows[i]["clip"] == "5157344") n339 = len(mine) - n344 # a crop that matches someone else better than anyone of its own label worst = int((other_sim > self_sim).sum()) print(f"{p:>8}{len(mine):>7}{n344:>6}{n339:>6}" f"{np.median(p_self):>10.3f}{np.median(p_other):>10.3f}{worst:>8}") if worst: for i in mine[other_sim > self_sim]: print(f" suspect: {rows[i]['file']} " f"P(self)={cal.probability(float(self_sim[list(mine).index(i)])):.3f} " f"< P(other)={cal.probability(float(other_sim[list(mine).index(i)])):.3f}") # ── verify sheets: context+box over the actual aligned crop ────────────────── for p in people: items = [r for r in rows if r["person"] == p] items.sort(key=lambda r: (r["clip"], r["file"])) n = len(items) sheet_rows = (n + COLS - 1) // COLS H = THUMB * 2 + 22 sheet = np.full((sheet_rows * H, COLS * THUMB, 3), 25, np.uint8) for j, r in enumerate(items): rr, cc = divmod(j, COLS) y, x = rr * H, cc * THUMB ctx = cv2.imread(r["path"]) if ctx is not None: sheet[y:y + THUMB, x:x + THUMB] = cv2.resize(ctx, (THUMB, THUMB)) sheet[y + THUMB:y + 2 * THUMB, x:x + THUMB] = cv2.resize(r["aligned"], (THUMB, THUMB)) cv2.putText(sheet, f"{r['clip'][-3:]} {int(r['px'])}px", (x + 3, y + 2 * THUMB + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.38, (150, 220, 150), 1) cv2.imwrite(f"labelling/verify_{p}.jpg", sheet) print(f" verify_{p}.jpg: {n} crops (top row context, bottom row what the embedder sees)") print(f"\n{'PASS' if fail == 0 else f'{fail} FAILURES'}") sys.exit(1 if fail else 0)