fix: record and require the walk identity backend
This commit is contained in:
parent
ac315eb48e
commit
6cad638c26
4 changed files with 75 additions and 18 deletions
|
|
@ -15,8 +15,11 @@ store was enrolled; scoring them is training-set accuracy.
|
|||
|
||||
Prints a human count table and writes the same report as JSON (default
|
||||
<recording>/eval_live.json). Missing input exits 2 with {"error": ...} on
|
||||
stdout. --identity gallery uses the same LiveSession path as the server and
|
||||
exits 2 when that gallery cannot load; it does not score the matcher instead.
|
||||
stdout. A recording that names identity_backend is scored as that backend unless
|
||||
--identity repeats it. A recording that does not name one requires --identity;
|
||||
omitting it does not score the matcher. --identity gallery uses the same
|
||||
LiveSession path as the server and exits 2 when that gallery cannot load; it
|
||||
does not score the matcher instead.
|
||||
The report names the backend, whether the head loaded, and the weight pins.
|
||||
The same pinned recording, catalog, weights, and labels produce the
|
||||
same counts; timings are omitted because they are not.
|
||||
|
|
@ -102,9 +105,9 @@ def load_index(recording_dir: str) -> List[Dict[str, Any]]:
|
|||
def recorded_identity(rows: Sequence[Dict[str, Any]]) -> Optional[str]:
|
||||
"""Backend named by the recording, when every present value agrees.
|
||||
|
||||
FrameRecorder does not write this. A later index may, on the first line or
|
||||
on each frame, under identity_backend or identity.backend. Silence means
|
||||
the caller must say --identity. A mix is not a default.
|
||||
FrameRecorder writes identity_backend on each line when the server constructed
|
||||
it with the active backend. Older indexes may omit it, or name it once under
|
||||
identity.backend. Silence is not a backend. A mix is not a default.
|
||||
"""
|
||||
found: List[str] = []
|
||||
for row in rows:
|
||||
|
|
@ -124,13 +127,17 @@ def recorded_identity(rows: Sequence[Dict[str, Any]]) -> Optional[str]:
|
|||
|
||||
|
||||
def resolve_identity(requested: Optional[str], recorded: Optional[str]) -> str:
|
||||
"""CLI wins. A recording may fill the gap. Neither one stays the matcher."""
|
||||
chosen = (requested or recorded or "matcher").strip().lower()
|
||||
"""CLI and recording must name the same backend. Silence is not matcher."""
|
||||
chosen = (requested or recorded or "").strip().lower()
|
||||
if not chosen:
|
||||
raise EvalInputError(
|
||||
"recording does not name an identity backend; pass --identity matcher or --identity gallery"
|
||||
)
|
||||
if chosen not in ("matcher", "gallery"):
|
||||
raise EvalInputError(f"identity must be matcher or gallery, not {requested or recorded}")
|
||||
if recorded and requested and recorded != chosen:
|
||||
raise EvalInputError(
|
||||
f"recording identity is {recorded}; pass --identity {recorded} or omit --identity"
|
||||
f"recording identity is {recorded}; pass --identity {recorded}"
|
||||
)
|
||||
return chosen
|
||||
|
||||
|
|
@ -219,7 +226,7 @@ def catalog_fingerprint(stock_dir: str, stock_csv: str = "stock.csv") -> Dict[st
|
|||
def model_fingerprint(
|
||||
weights_path: str,
|
||||
*,
|
||||
identity: str = "matcher",
|
||||
identity: str,
|
||||
gallery: Any = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""YOLO pin plus, for gallery, the backbone and head that actually loaded."""
|
||||
|
|
@ -545,7 +552,7 @@ def score_recording(
|
|||
labels_path: Optional[str] = None,
|
||||
iou_min: float = 0.5,
|
||||
live_frames: Optional[Sequence[Dict[str, Any]]] = None,
|
||||
identity: str = "matcher",
|
||||
identity: str,
|
||||
gallery: Any = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Score one session directory. live_frames, when given, are the replayed results.
|
||||
|
|
@ -564,7 +571,9 @@ def score_recording(
|
|||
source = list(live_frames) if live_frames is not None else rows
|
||||
tracks = [track for frame in source for track in (frame.get("tracks") or [])]
|
||||
decisions = score_tracks(tracks)
|
||||
backend = (identity or "matcher").strip().lower()
|
||||
backend = identity.strip().lower()
|
||||
if backend not in ("matcher", "gallery"):
|
||||
raise EvalInputError(f"identity must be matcher or gallery, not {identity}")
|
||||
report = {
|
||||
"recording": recording_dir,
|
||||
"source": "live_replay" if live_frames is not None else "recorded_index",
|
||||
|
|
@ -678,7 +687,7 @@ def format_report(report: Dict[str, Any]) -> str:
|
|||
head_note = f" head {head}" if head else ""
|
||||
lines = [
|
||||
f"source: {report['source']}",
|
||||
f"identity: {identity.get('backend', 'matcher')}{head_note}",
|
||||
f"identity: {identity.get('backend')}{head_note}",
|
||||
(
|
||||
f"decisions: accepted {decisions['accepted']} ambiguous {decisions['ambiguous']} "
|
||||
f"rejected {decisions['rejected']} unknown {decisions['unknown']} "
|
||||
|
|
@ -782,7 +791,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
"--identity",
|
||||
default=None,
|
||||
choices=["matcher", "gallery"],
|
||||
help="which mechanism names products (default: recorded backend, else matcher)",
|
||||
help="which mechanism names products (required when the recording omits identity_backend)",
|
||||
)
|
||||
parser.add_argument("--device", default=None, help="cpu | mps | cuda | cuda:0")
|
||||
args = parser.parse_args(argv)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue