Initial commit: scene-actor-extraction pipeline
Source (KPN++ pipeline nodes, ArcFace embedders, SCRFD/YuNet detectors, gallery builder), build scripts, and eval artifacts. - external/KPN as a git submodule (gitea.tourolle.paris/dtourolle/KPN) - ONNX models tracked via Git LFS (models/*.onnx) - generated outputs, TensorRT engines, reference repos, and media ignored
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "types.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
// ── SceneTrackerFunc ──────────────────────────────────────────────────────────
|
||||
// KPN node: maintains an extinction-timer state machine per identified actor.
|
||||
//
|
||||
// On each MatchedSceneFrame:
|
||||
// 1. Update last_seen for every matched known actor.
|
||||
// 2. Expire actors whose last_seen is older than extinction_sec.
|
||||
// 3. Emit SceneAnnotation with all currently active (non-expired) actors,
|
||||
// including their most recently seen bbox and best similarity score.
|
||||
//
|
||||
// Unknown faces (actor_idx == -1) are passed through per-frame but are NOT
|
||||
// tracked across frames — each frame reports its own unknowns independently.
|
||||
|
||||
struct SceneTrackerFunc {
|
||||
static constexpr std::string_view label() { return "scene_tracker"; }
|
||||
|
||||
explicit SceneTrackerFunc(const Config& cfg)
|
||||
: extinction_sec_(cfg.extinction_sec)
|
||||
{
|
||||
std::cerr << "[scene_tracker] extinction_sec=" << extinction_sec_ << "\n";
|
||||
}
|
||||
|
||||
SceneAnnotation operator()(MatchedSceneFrame mf) {
|
||||
if (mf.source.eof) return {0.0, {}, /*eof=*/true};
|
||||
|
||||
double now = mf.source.timestamp_sec;
|
||||
|
||||
// Update known actors
|
||||
for (const auto& ia : mf.actors) {
|
||||
if (ia.actor_idx < 0) continue; // skip unknowns
|
||||
|
||||
auto& slot = active_[ia.actor_idx];
|
||||
slot.last_seen = now;
|
||||
slot.last_bbox = ia.bbox;
|
||||
slot.last_crop = ia.crop;
|
||||
slot.name = ia.name;
|
||||
slot.imdb_id = ia.imdb_id;
|
||||
// Keep the best (highest) similarity seen in this window
|
||||
if (ia.similarity > slot.best_similarity)
|
||||
slot.best_similarity = ia.similarity;
|
||||
}
|
||||
|
||||
// Expire stale actors
|
||||
for (auto it = active_.begin(); it != active_.end(); ) {
|
||||
if ((now - it->second.last_seen) > extinction_sec_)
|
||||
it = active_.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// Build annotation: active known actors
|
||||
std::vector<IdentifiedActor> visible;
|
||||
visible.reserve(active_.size() + mf.actors.size());
|
||||
|
||||
for (const auto& [actor_idx, slot] : active_) {
|
||||
IdentifiedActor ia;
|
||||
ia.actor_idx = actor_idx;
|
||||
ia.name = slot.name;
|
||||
ia.imdb_id = slot.imdb_id;
|
||||
ia.similarity = slot.best_similarity;
|
||||
ia.bbox = slot.last_bbox;
|
||||
ia.crop = slot.last_crop;
|
||||
visible.push_back(ia);
|
||||
}
|
||||
|
||||
// Append per-frame unknowns (actor_idx == -1) directly
|
||||
for (const auto& ia : mf.actors) {
|
||||
if (ia.actor_idx < 0) visible.push_back(ia);
|
||||
}
|
||||
|
||||
return {now, std::move(visible)};
|
||||
}
|
||||
|
||||
private:
|
||||
struct Slot {
|
||||
double last_seen{0.0};
|
||||
float best_similarity{0.f};
|
||||
cv::Rect2f last_bbox;
|
||||
cv::Mat last_crop;
|
||||
std::string name;
|
||||
std::string imdb_id;
|
||||
};
|
||||
|
||||
double extinction_sec_;
|
||||
std::map<int, Slot> active_; // actor_idx → state
|
||||
};
|
||||
Reference in New Issue
Block a user