master
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
48332d2041 |
feat(replay): the whole replay chain is C++, including the sink
The sae_kpn module has not compiled since the AR-007/AR-008 tracker redesign, and was switched off at the build rather than patched because the fix is a restructuring. Two failures, one cause. It did not compile: `add_face_tracker` built FaceTrackerFunc from a Config alone, and the tracker has required a TrackRegistry and a calibration since association moved into probability space. And presence was rebuilt in Python. `replay.py::build_minimal` merged per-frame detections into windows by annealing gaps, which is what the pipeline did before AR-012. The sink builds a window from a TrackRegistry claim instead — the extent of a track an actor owned, starting when they appeared rather than when recognition first succeeded. Those answer different questions, so every sweep was tuning against a contract the shipped code had stopped honouring. Both follow from the seam being a factory per node. The chain has a construction order — the matcher fits the calibration, the registry needs a discounter built from it, the tracker needs both, and the sink needs the registry's claims — and independent factories cannot express it, so the tracker kept being built against a signature that no longer existed. One `add_pipeline` mirrors main.cpp exactly and is now the only way to build the chain, so the ordering cannot be got wrong again from Python. DP-001 is the requirement behind it: a replay harness is a front-end, and its job is to supply frames and read the result, not to re-derive presence. Lifetimes needed a home. ResultSinkFunc holds `const Config&` and `std::atomic<bool>&`, which under main() are locals in a frame outliving the pipeline; there is no such frame when the network is built and torn down from Python. ReplaySession owns both for the network's lifetime, keyed by network and released explicitly — a sweep builds one network per replay and the sink retains every annotation, so holding them forever would grow with films x configs. Getting this wrong presented as an empty output_path: the sink announced `[result_sink] writing ` and wrote nothing. test_sae_kpn.py is ported rather than left behind. It called all three removed factories and asserted on SceneAnnotations read back per frame; neither half survives, so it now waits on pipeline_done and asserts on the file the sink writes. Verified against gallery_lvface.h5: three frames through the real chain, timestamps 0/1/2, truth file written. EOF is a control token the sink flushes on and does not record, so three inputs give three frames, never four. SAE_BUILD_KPN_BINDINGS goes back to ON. TRACES: VR-011, VR-002 | DP-001 | PR-002 |
||
|
|
7c7d4934ae |
refactor(presence): execute the extinction_sec/anneal_sec withdrawal
docs/SPEC.md specified this removal, listed its parts, and ended "grep
for both names and expect no survivors". There were about forty.
docs/requirements.md meanwhile recorded both constants as Withdrawn and
"deleted rather than retained at zero", on the grounds that a field
naming a mechanism the pipeline no longer has is actively misleading.
Neither statement was true of the code: Config still carried
extinction_sec 57.4 and anneal_sec 35.5, --extinction and --anneal still
parsed, and SceneTrackerFunc still ran its keep-alive in both shipped
pipelines, announcing its timeout at every startup.
SceneTrackerFunc is replaced by FrameAnnotationFunc, which is stateless:
same ports, same output type, no keep-alive. Presence belongs to
TrackRegistry (AR-012), where a window is the extent of a track an actor
owned and ends at the last sighting (AR-013). The keep-alive answered
that question a second time and answered it worse, by re-opening exactly
the trailing cool-down AR-013 refuses.
Visible change: --verbosity standard's frames[].identified listed every
actor inside the keep-alive, including ones absent from the frame. It
now lists what was matched in that frame. Minimal and xray output is
untouched -- both were already built from registry claims and never
consulted this node. No schema bump: the published extraction block
reports track_extinction_sec, a different knob that bounds
re-association and never extends a claim.
TrackRegistry::Config::extinction_sec is renamed track_extinction_sec to
match the Config field feeding it, so the grep SPEC.md asks for now
returns nothing rather than one confusing false positive.
Two targets turned out to have been silently dead, both since the
AR-007/AR-008 tracker redesign, and both for the same reason -- they
construct FaceTrackerFunc from a Config alone, a signature that stopped
existing when association moved into probability space:
- scene_preview is fixed here. It now mirrors main.cpp's construction
order exactly (matcher, then registry, then tracker) and wires the
registry's claims into the sink, which it was not doing. DP-001 says
modes are front-ends that must not fork pipeline logic; this one had
forked it and then rotted.
- sae_kpn is not fixed. Restructuring the seam so the tracker can reach
a calibration that only exists once the matcher is built is VR-011's
rewrite, not a patch, and presence claims do not cross the seam at all
today. It is now behind SAE_BUILD_KPN_BINDINGS=OFF with the reason
recorded, so `cmake --build` succeeds and the breakage is attributed
rather than rediscovered.
That second one is worth stating plainly: VR-002 ("replay drives the
real KPN nodes, not a reimplementation") is marked Done, and the module
that makes replay possible has not compiled for some time. The .so in a
stale build/ predates the change.
Python side: the two names are gone from optimize.py, replay.py and
run_holdout_all_models.py as Config keys. anneal_sec survives as
REPLAY_LOCAL_KEYS -- it still configures replay.py's own windowing,
which is a Python reimplementation that no longer matches the sink and
is documented as such. That divergence is VR-011's.
TRACES: AR-012, AR-013 | DP-001 | SR-002
|
||
|
|
6f0ad83a55 |
feat(tooling): X-Ray threshold optimizer, gallery utilities, artifact registry, docs build
Optimizer (scripts/optimizer/): replay.py runs the real C++ tracker/matcher/ scene_tracker chain over a dumped-embeddings HDF5 via sae_kpn, so a threshold sweep never re-decodes video or re-embeds faces. optimize.py drives scipy's differential_evolution over the knob space, with DE-level parallelism (multiple population candidates evaluated concurrently via a ThreadPoolExecutor) on top of per-film replay parallelism. second_score.py is the per-second X-Ray scoring metric (TPI/FPI/FN, out-of-cast misID weighted 10x, fair recall masked to gallery-known cast) that superseded an earlier scene-union metric. dump_error_frames.py / dump_scene_montage.py extract annotated video frames (bounding boxes, TPI/FPI/FN captions, onscreen-vs-offscreen split) for visual review of a replay against ground truth. Gallery utilities: cast_restrict.py, gallery_membership.py, fetch_missing_actors.py, reembed_gallery.py. scripts/validation/: X-Ray ground-truth loading and provider-agnostic identity matching (identity.py's keys_for — an actor is the union of every id we can derive, since pipeline output and ground truth don't share one id space). scripts/artifacts/: push/pull scripts for the Gitea generic package registry — galleries, montage frames, and experiment data (manifests/trajectories/results) are pushed there instead of committed, since none are needed to run the app, only benchmarks. Versioned by git short-SHA. scripts/docs/: MkDocs site build (build_site.sh) and the calibration-curve comparison chart (calibration_chart.py, matplotlib, reads each gallery's embedded calibration). Gallery-building scripts (make_jellyfin_gallery.py, make_gallery.py, filter_gallery.py, run_from_jellyfin.py, movienet_eval.py, movienet_prep.py, sae_gallery.py) updated to read/write HDF5 galleries exclusively, matching the engine-side format switch. run_from_jellyfin.py and the optimizer no longer carry movie source paths in shared manifests (some source filenames include scene-release tags) — resolved locally via a gitignored file-lut.json instead. |