The parser reads a tag up to end of line, so `# TRACES: GR-004 | SR-001 — prose` swallowed the prose into the tag and the row went unmatched. Splitting the comment leaves the tag greppable by the same pattern as the code tags and the commit trailers, which is the point of the house format. Mechanical throughout; no logic touched. The regenerated report reflects this session's new tags: 137 -> 148 found, and one more tagged-but-unexecuted, which is the SuperHero accuracy assertion that is documented but not yet a test.
176 lines
7.4 KiB
Python
176 lines
7.4 KiB
Python
#!/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_<person>.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)
|