diff --git a/src/track_registry.hpp b/src/track_registry.hpp index 6b69b59..f3d20e9 100644 --- a/src/track_registry.hpp +++ b/src/track_registry.hpp @@ -218,9 +218,21 @@ public: const double clock = reg_.awaits_evidence_ ? reg_.evidence_through_ : reg_.now_; for (auto& [id, t] : reg_.tracks_) { - if (t.last_seen && - (clock - *t.last_seen) > reg_.cfg_.track_extinction_sec) - continue; // retired from association; still awaiting evidence + // On-screen tracks are always candidates (actively tracked this + // frame). A dormant (off-screen) track is only worth keeping alive + // for re-association if it was actually IDENTIFIED: an unowned + // dormant track has no actor to re-attach to, so holding it in the + // pool only bloats the matcher's per-frame comparison set (every + // candidate is a GEMM row) and invites a new face re-associating + // onto an anonymous stub. Gating dormant tracks on t.actor keeps + // the pool bounded regardless of how large track_extinction_sec is + // — which is what makes a long re-association window affordable. + if (t.last_seen) { // dormant + if (!t.actor.has_value()) + continue; // never identified: not worth re-associating + if ((clock - *t.last_seen) > reg_.cfg_.track_extinction_sec) + continue; // past the re-association horizon + } out.push_back(&t); } return out;