Fallback face track association to shared tracker state
This commit is contained in:
parent
ce06126ecd
commit
2ed61f5c51
@ -92,6 +92,8 @@
|
||||
"emit_embedding": false,
|
||||
"max_faces": 50,
|
||||
"person_class_id": 0,
|
||||
"track_state_key": "full_pipeline_1080p_workshoe",
|
||||
"track_state_max_age_ms": 1000,
|
||||
"input_format": "rgb",
|
||||
"input_dtype": "uint8",
|
||||
"threshold": {
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include "node.h"
|
||||
#include "utils/dma_alloc.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/shared_state.h"
|
||||
|
||||
#if defined(RK3588_ENABLE_SQLITE3)
|
||||
#include <sqlite3.h>
|
||||
@ -64,6 +65,7 @@ inline bool IsPersonDetection(const Detection& det, int person_class_id) {
|
||||
}
|
||||
|
||||
struct FaceTrackAssociationDiag {
|
||||
std::string source = "frame_det";
|
||||
int total_dets = 0;
|
||||
int person_dets = 0;
|
||||
int tracked_person_dets = 0;
|
||||
@ -72,28 +74,31 @@ struct FaceTrackAssociationDiag {
|
||||
float best_overlap = -1.0f;
|
||||
};
|
||||
|
||||
int AssociateFaceToPersonTrack(const Rect& face_bbox, const std::vector<Detection>& dets, int person_class_id,
|
||||
FaceTrackAssociationDiag* diag = nullptr) {
|
||||
template <typename ObjT, typename ClassFn, typename TrackFn, typename BboxFn>
|
||||
int AssociateFaceToPersonTrackImpl(const Rect& face_bbox, const std::vector<ObjT>& objs, int person_class_id,
|
||||
FaceTrackAssociationDiag* diag, ClassFn class_fn, TrackFn track_fn,
|
||||
BboxFn bbox_fn) {
|
||||
const float center_x = face_bbox.x + face_bbox.w * 0.5f;
|
||||
const float center_y = face_bbox.y + face_bbox.h * 0.5f;
|
||||
|
||||
if (diag) *diag = FaceTrackAssociationDiag{};
|
||||
if (diag) diag->total_dets = static_cast<int>(dets.size());
|
||||
if (diag) diag->total_dets = static_cast<int>(objs.size());
|
||||
|
||||
int best_track_id = -1;
|
||||
float best_overlap = -1.0f;
|
||||
for (const auto& det : dets) {
|
||||
if (diag && IsPersonDetection(det, person_class_id)) ++diag->person_dets;
|
||||
if (!IsPersonDetection(det, person_class_id)) continue;
|
||||
if (det.track_id < 0) continue;
|
||||
for (const auto& obj : objs) {
|
||||
if (diag && class_fn(obj) == person_class_id) ++diag->person_dets;
|
||||
if (class_fn(obj) != person_class_id) continue;
|
||||
if (track_fn(obj) < 0) continue;
|
||||
if (diag) ++diag->tracked_person_dets;
|
||||
if (!ContainsPoint(det.bbox, center_x, center_y)) continue;
|
||||
const Rect bbox = bbox_fn(obj);
|
||||
if (!ContainsPoint(bbox, center_x, center_y)) continue;
|
||||
if (diag) ++diag->containing_tracked_person_dets;
|
||||
|
||||
const float overlap = IntersectionArea(face_bbox, det.bbox);
|
||||
const float overlap = IntersectionArea(face_bbox, bbox);
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
best_track_id = det.track_id;
|
||||
best_track_id = track_fn(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,12 +109,63 @@ int AssociateFaceToPersonTrack(const Rect& face_bbox, const std::vector<Detectio
|
||||
return best_track_id;
|
||||
}
|
||||
|
||||
int AssociateFaceToPersonTrack(const Rect& face_bbox, const std::vector<Detection>& dets, int person_class_id,
|
||||
FaceTrackAssociationDiag* diag = nullptr) {
|
||||
return AssociateFaceToPersonTrackImpl(
|
||||
face_bbox, dets, person_class_id, diag, [](const Detection& det) { return det.cls_id; },
|
||||
[](const Detection& det) { return det.track_id; }, [](const Detection& det) { return det.bbox; });
|
||||
}
|
||||
|
||||
int AssociateFaceToTrackedObjects(const Rect& face_bbox, const std::vector<TrackedObject>& objs, int person_class_id,
|
||||
FaceTrackAssociationDiag* diag = nullptr) {
|
||||
return AssociateFaceToPersonTrackImpl(
|
||||
face_bbox, objs, person_class_id, diag, [](const TrackedObject& obj) { return obj.cls_id; },
|
||||
[](const TrackedObject& obj) { return obj.track_id; }, [](const TrackedObject& obj) { return obj.bbox; });
|
||||
}
|
||||
|
||||
int AssociateFaceToPersonTrackWithFallback(const Rect& face_bbox, const DetectionResult* det_result,
|
||||
int person_class_id, const std::string& track_state_key,
|
||||
int64_t track_state_max_age_ms,
|
||||
FaceTrackAssociationDiag* diag = nullptr) {
|
||||
if (det_result) {
|
||||
FaceTrackAssociationDiag det_diag;
|
||||
const int track_id = AssociateFaceToPersonTrack(face_bbox, det_result->items, person_class_id,
|
||||
diag ? &det_diag : nullptr);
|
||||
if (diag) {
|
||||
*diag = det_diag;
|
||||
diag->source = "frame_det";
|
||||
}
|
||||
if (track_id >= 0) return track_id;
|
||||
}
|
||||
|
||||
if (track_state_key.empty()) return -1;
|
||||
|
||||
auto snap = SharedState::Instance().GetTargets(track_state_key);
|
||||
if (!snap) return -1;
|
||||
|
||||
if (track_state_max_age_ms > 0) {
|
||||
const uint64_t now_us = NowSteadyUs();
|
||||
const uint64_t age_us = (now_us > snap->update_steady_us) ? (now_us - snap->update_steady_us) : 0;
|
||||
if (age_us > static_cast<uint64_t>(track_state_max_age_ms) * 1000ULL) return -1;
|
||||
}
|
||||
|
||||
FaceTrackAssociationDiag state_diag;
|
||||
const int track_id = AssociateFaceToTrackedObjects(face_bbox, snap->objects, person_class_id,
|
||||
diag ? &state_diag : nullptr);
|
||||
if (diag) {
|
||||
*diag = state_diag;
|
||||
diag->source = "shared_state";
|
||||
}
|
||||
return track_id;
|
||||
}
|
||||
|
||||
std::string BuildFaceTrackAssociationDiagLine(const std::string& node_id, uint64_t frame_id, const Rect& face_bbox,
|
||||
int person_class_id, const FaceTrackAssociationDiag& diag) {
|
||||
std::ostringstream oss;
|
||||
oss << "[ai_face_recog] track_assoc"
|
||||
<< " id=" << node_id
|
||||
<< " frame=" << frame_id
|
||||
<< " source=" << diag.source
|
||||
<< " person_class_id=" << person_class_id
|
||||
<< " face_bbox=(" << static_cast<int>(std::lround(face_bbox.x))
|
||||
<< "," << static_cast<int>(std::lround(face_bbox.y))
|
||||
@ -637,6 +693,8 @@ struct FaceRecogConfigSnapshot {
|
||||
float thr_accept = 0.45f;
|
||||
float thr_margin = 0.05f;
|
||||
int person_class_id = 0;
|
||||
std::string track_state_key;
|
||||
int64_t track_state_max_age_ms = 1000;
|
||||
|
||||
std::string model_input_format = "rgb";
|
||||
std::string input_dtype = "uint8";
|
||||
@ -667,6 +725,10 @@ static bool BuildFaceRecogConfigSnapshot(const SimpleJson& config,
|
||||
snap->emit_embedding = config.ValueOr<bool>("emit_embedding", snap->emit_embedding);
|
||||
snap->max_faces = std::max(1, config.ValueOr<int>("max_faces", snap->max_faces));
|
||||
snap->person_class_id = config.ValueOr<int>("person_class_id", snap->person_class_id);
|
||||
snap->track_state_key = config.ValueOr<std::string>("track_state_key", snap->track_state_key);
|
||||
snap->track_state_max_age_ms =
|
||||
std::max<int64_t>(0, static_cast<int64_t>(config.ValueOr<int>("track_state_max_age_ms",
|
||||
static_cast<int>(snap->track_state_max_age_ms))));
|
||||
|
||||
if (const SimpleJson* th = config.Find("threshold"); th && th->IsObject()) {
|
||||
snap->thr_accept = th->ValueOr<float>("accept", snap->thr_accept);
|
||||
@ -1042,9 +1104,9 @@ private:
|
||||
|
||||
FaceRecogItem item;
|
||||
item.bbox = face.bbox;
|
||||
item.person_track_id = frame->det
|
||||
? AssociateFaceToPersonTrack(face.bbox, frame->det->items, cfg->person_class_id, &assoc_diag)
|
||||
: -1;
|
||||
item.person_track_id = AssociateFaceToPersonTrackWithFallback(
|
||||
face.bbox, frame->det.get(), cfg->person_class_id, cfg->track_state_key,
|
||||
cfg->track_state_max_age_ms, &assoc_diag);
|
||||
item.has_landmarks = face.has_landmarks;
|
||||
item.landmarks = face.landmarks;
|
||||
|
||||
|
||||
@ -84,6 +84,7 @@ target_compile_definitions(rk3588_gtests PRIVATE RK3588_TEST_BUILD)
|
||||
|
||||
target_link_libraries(rk3588_gtests PRIVATE
|
||||
project_options
|
||||
rk_shared_state
|
||||
Threads::Threads
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "face/face_recog_debug.h"
|
||||
#include "utils/shared_state.h"
|
||||
|
||||
#include "../plugins/ai_face_recog/ai_face_recog_node.cpp"
|
||||
|
||||
@ -62,6 +63,38 @@ TEST(FaceTrackAssociationTest, HonorsConfiguredPersonClassId) {
|
||||
EXPECT_EQ(AssociateFaceToPersonTrack(face, dets, 1), 808);
|
||||
}
|
||||
|
||||
TEST(FaceTrackAssociationTest, FallsBackToSharedTrackedPersonsWhenFrameDetectionsMissing) {
|
||||
const std::string state_key = "face_track_assoc_test";
|
||||
SharedState::Instance().ClearTargets(state_key);
|
||||
|
||||
TargetsSnapshot snap;
|
||||
snap.img_w = 1920;
|
||||
snap.img_h = 1080;
|
||||
snap.model_name = "person_det";
|
||||
TrackedObject obj;
|
||||
obj.cls_id = 0;
|
||||
obj.track_id = 321;
|
||||
obj.score = 0.0f;
|
||||
obj.bbox = Rect{40.0f, 40.0f, 60.0f, 80.0f};
|
||||
obj.confirmed = true;
|
||||
snap.objects.push_back(obj);
|
||||
SharedState::Instance().SetTargets(state_key, std::move(snap));
|
||||
|
||||
const Rect face{50.0f, 50.0f, 20.0f, 20.0f};
|
||||
FaceTrackAssociationDiag diag;
|
||||
const int track_id = AssociateFaceToPersonTrackWithFallback(face, nullptr, 0, state_key, 1000, &diag);
|
||||
|
||||
EXPECT_EQ(track_id, 321);
|
||||
EXPECT_EQ(diag.source, "shared_state");
|
||||
EXPECT_EQ(diag.total_dets, 1);
|
||||
EXPECT_EQ(diag.person_dets, 1);
|
||||
EXPECT_EQ(diag.tracked_person_dets, 1);
|
||||
EXPECT_EQ(diag.containing_tracked_person_dets, 1);
|
||||
EXPECT_EQ(diag.best_track_id, 321);
|
||||
|
||||
SharedState::Instance().ClearTargets(state_key);
|
||||
}
|
||||
|
||||
TEST(FaceTrackAssociationTest, DebugLineIncludesPersonTrackId) {
|
||||
FaceRecogItem item;
|
||||
item.person_track_id = 88;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user