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
This commit is contained in:
+14
-22
@@ -1,5 +1,5 @@
|
||||
// sae_kpn — run the real downstream pipeline nodes (face_tracker, identity_matcher,
|
||||
// scene_tracker) inside a Python-assembled KPN network, fed by a Python HDF5 replay
|
||||
// frame_annotation) inside a Python-assembled KPN network, fed by a Python HDF5 replay
|
||||
// source. Lets a parameter sweep re-run the exact C++ matching/tracking logic over
|
||||
// dumped embeddings — no video decode, no GPU — with different Config knobs each run.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "gallery/gallery_store.hpp"
|
||||
#include "nodes/face_tracker_node.hpp"
|
||||
#include "nodes/identity_matcher_node.hpp"
|
||||
#include "nodes/scene_tracker_node.hpp"
|
||||
#include "nodes/frame_annotation_node.hpp"
|
||||
|
||||
#include <nanobind/nanobind.h>
|
||||
#include <nanobind/ndarray.h>
|
||||
@@ -171,13 +171,8 @@ static Config config_from_dict(nb::dict d) {
|
||||
// face tracker
|
||||
getf("track_alpha", cfg.track_alpha);
|
||||
getf("track_min_iou", cfg.track_min_iou);
|
||||
getf("track_max_embed_dist", cfg.track_max_embed_dist);
|
||||
geti("track_max_frames_missing", cfg.track_max_frames_missing);
|
||||
getf("cut_revive_sim", cfg.cut_revive_sim);
|
||||
geti("cut_inactive_max_frames", cfg.cut_inactive_max_frames);
|
||||
// scene tracker
|
||||
getd("extinction_sec", cfg.extinction_sec);
|
||||
getd("anneal_sec", cfg.anneal_sec);
|
||||
getf("track_assoc_min_prob", cfg.track_assoc_min_prob);
|
||||
getd("track_extinction_sec", cfg.track_extinction_sec);
|
||||
// gallery expansion (usually off for sweeps; expose so it can be toggled)
|
||||
if (d.contains("expand_gallery")) cfg.expand_gallery = nb::cast<bool>(d["expand_gallery"]);
|
||||
/// TRACES: GR-004 | SR-001
|
||||
@@ -189,7 +184,7 @@ static Config config_from_dict(nb::dict d) {
|
||||
using Net = kpn::python::PyNetwork<SaeVariant>;
|
||||
|
||||
NB_MODULE(sae_kpn, m) {
|
||||
m.doc() = "Real KPN downstream nodes (tracker/matcher/scene_tracker) for Python replay sweeps";
|
||||
m.doc() = "Real KPN downstream nodes (tracker/matcher/frame_annotation) for Python replay sweeps";
|
||||
|
||||
kpn::python::register_py_network<SaeVariant>(m, "Network");
|
||||
|
||||
@@ -270,30 +265,27 @@ NB_MODULE(sae_kpn, m) {
|
||||
}, "net"_a, "name"_a, "gallery"_a, "config"_a, "capacity"_a = 16,
|
||||
"embedder_model"_a = "", "embedder_sha256"_a = "");
|
||||
|
||||
m.def("add_scene_tracker", [](Net& net, std::string name, nb::dict cfg_dict, std::size_t cap) {
|
||||
Config cfg = config_from_dict(cfg_dict);
|
||||
/// TRACES: AR-012, AR-013 | SR-002
|
||||
// Was add_scene_tracker, backed by the extinction-timer state machine. The
|
||||
// node is gone (see frame_annotation_node.hpp) and so is the timer; this
|
||||
// projects a matched frame into the same SceneAnnotation the Python sink
|
||||
// already reads, so the seam's output type is unchanged. It takes no config
|
||||
// because it has no state to configure -- which is the point.
|
||||
m.def("add_frame_annotation", [](Net& net, std::string name, std::size_t cap) {
|
||||
auto node = std::make_shared<kpn::ObjectVariantNodeWrapper<
|
||||
SceneTrackerFunc, SaeVariant, kpn::in<"matched">, kpn::out<"annotation">>>(cap, cfg);
|
||||
FrameAnnotationFunc, SaeVariant, kpn::in<"matched">, kpn::out<"annotation">>>(cap);
|
||||
net.add(std::move(name), std::move(node));
|
||||
}, "net"_a, "name"_a, "config"_a, "capacity"_a = 16);
|
||||
}, "net"_a, "name"_a, "capacity"_a = 16);
|
||||
|
||||
// ── Runtime setters (persistent-pipeline reuse across a threshold sweep) ─────
|
||||
// Build the network once, then change thresholds between replays — no rebuild,
|
||||
// no teardown (which is where the ROCm deadlock lives), no gallery reload.
|
||||
using MatcherWrap = kpn::ObjectVariantNodeWrapper<
|
||||
IdentityMatcherFunc, SaeVariant, kpn::in<"tracked">, kpn::out<"matched">>;
|
||||
using SceneWrap = kpn::ObjectVariantNodeWrapper<
|
||||
SceneTrackerFunc, SaeVariant, kpn::in<"matched">, kpn::out<"annotation">>;
|
||||
|
||||
m.def("set_prob_threshold", [](Net& net, std::string name, float t) {
|
||||
auto* w = dynamic_cast<MatcherWrap*>(net.node_ptr(name));
|
||||
if (!w) throw std::runtime_error("set_prob_threshold: '" + name + "' is not an identity_matcher");
|
||||
w->functor().set_prob_threshold(t);
|
||||
}, "net"_a, "name"_a, "value"_a);
|
||||
|
||||
m.def("set_extinction_sec", [](Net& net, std::string name, double s) {
|
||||
auto* w = dynamic_cast<SceneWrap*>(net.node_ptr(name));
|
||||
if (!w) throw std::runtime_error("set_extinction_sec: '" + name + "' is not a scene_tracker");
|
||||
w->functor().set_extinction_sec(s);
|
||||
}, "net"_a, "name"_a, "value"_a);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user