feat: associate face results with person tracks

This commit is contained in:
tian 2026-04-15 14:36:57 +08:00
parent a0bf3f04d5
commit d8d1fb9331
5 changed files with 98 additions and 0 deletions

View File

@ -41,6 +41,7 @@ inline std::string BuildFaceRecogDebugSummaryLine(
<< " id=" << node_id
<< " frame=" << frame_id
<< " status=" << status
<< " person_track_id=" << item.person_track_id
<< " candidate=" << candidate
<< " candidate_id=" << item.candidate_person_id
<< " best_sim=" << std::fixed << std::setprecision(2) << item.best_sim

View File

@ -31,6 +31,7 @@ struct FaceDetResult {
struct FaceRecogItem {
Rect bbox{};
int person_track_id = -1;
int best_person_id = -1;
std::string best_name;

View File

@ -44,6 +44,41 @@ bool ReadFileToString(const std::string& path, std::string& out) {
return ifs.good();
}
inline float IntersectionArea(const Rect& a, const Rect& b) {
const float left = std::max(a.x, b.x);
const float top = std::max(a.y, b.y);
const float right = std::min(a.x + a.w, b.x + b.w);
const float bottom = std::min(a.y + a.h, b.y + b.h);
const float w = right - left;
const float h = bottom - top;
if (w <= 0.0f || h <= 0.0f) return 0.0f;
return w * h;
}
inline bool ContainsPoint(const Rect& r, float x, float y) {
return x >= r.x && y >= r.y && x <= (r.x + r.w) && y <= (r.y + r.h);
}
int AssociateFaceToPersonTrack(const Rect& face_bbox, const std::vector<Detection>& dets) {
const float center_x = face_bbox.x + face_bbox.w * 0.5f;
const float center_y = face_bbox.y + face_bbox.h * 0.5f;
int best_track_id = -1;
float best_overlap = -1.0f;
for (const auto& det : dets) {
if (det.track_id < 0) continue;
if (!ContainsPoint(det.bbox, center_x, center_y)) continue;
const float overlap = IntersectionArea(face_bbox, det.bbox);
if (overlap > best_overlap) {
best_overlap = overlap;
best_track_id = det.track_id;
}
}
return best_track_id;
}
struct GalleryEntry {
int person_id = -1;
std::string name;
@ -958,6 +993,7 @@ private:
FaceRecogItem item;
item.bbox = face.bbox;
item.person_track_id = frame->det ? AssociateFaceToPersonTrack(face.bbox, frame->det->items) : -1;
item.has_landmarks = face.has_landmarks;
item.landmarks = face.landmarks;

View File

@ -48,6 +48,7 @@ add_executable(rk3588_gtests
test_alarm_behavior_events.cpp
test_log_action.cpp
test_face_recog_debug.cpp
test_face_track_association.cpp
test_infer_backend.cpp
test_image_processor.cpp
test_codec_backend.cpp

View File

@ -0,0 +1,59 @@
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include "face/face_recog_debug.h"
#include "../plugins/ai_face_recog/ai_face_recog_node.cpp"
namespace rk3588 {
namespace {
Detection MakePersonDetection(int track_id, float x, float y, float w, float h) {
Detection det;
det.cls_id = 0;
det.track_id = track_id;
det.score = 0.95f;
det.bbox = Rect{x, y, w, h};
return det;
}
TEST(FaceTrackAssociationTest, PicksContainingTrackedPerson) {
const Rect face{50.0f, 50.0f, 20.0f, 20.0f};
const std::vector<Detection> dets = {
MakePersonDetection(101, 45.0f, 45.0f, 20.0f, 20.0f),
MakePersonDetection(202, 40.0f, 40.0f, 40.0f, 40.0f),
MakePersonDetection(303, 10.0f, 10.0f, 15.0f, 15.0f),
};
EXPECT_EQ(AssociateFaceToPersonTrack(face, dets), 202);
}
TEST(FaceTrackAssociationTest, RejectsWhenNoTrackedPersonMatches) {
const Rect face{50.0f, 50.0f, 20.0f, 20.0f};
const std::vector<Detection> dets = {
MakePersonDetection(-1, 45.0f, 45.0f, 20.0f, 20.0f),
MakePersonDetection(404, 200.0f, 200.0f, 20.0f, 20.0f),
};
EXPECT_EQ(AssociateFaceToPersonTrack(face, dets), -1);
}
TEST(FaceTrackAssociationTest, DebugLineIncludesPersonTrackId) {
FaceRecogItem item;
item.person_track_id = 88;
item.candidate_person_id = 7;
item.candidate_name = "alice";
item.best_sim = 0.89f;
item.second_sim = 0.74f;
item.unknown = false;
const std::string line = BuildFaceRecogDebugSummaryLine("face_recog", 42, item);
EXPECT_NE(line.find("person_track_id=88"), std::string::npos);
EXPECT_NE(line.find("candidate=alice"), std::string::npos);
}
} // namespace
} // namespace rk3588