diff --git a/include/face/face_recog_debug.h b/include/face/face_recog_debug.h index ae355f0..3ee4aa1 100644 --- a/include/face/face_recog_debug.h +++ b/include/face/face_recog_debug.h @@ -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 diff --git a/include/face/face_result.h b/include/face/face_result.h index b6ef4f5..60d770d 100644 --- a/include/face/face_result.h +++ b/include/face/face_result.h @@ -31,6 +31,7 @@ struct FaceDetResult { struct FaceRecogItem { Rect bbox{}; + int person_track_id = -1; int best_person_id = -1; std::string best_name; diff --git a/plugins/ai_face_recog/ai_face_recog_node.cpp b/plugins/ai_face_recog/ai_face_recog_node.cpp index 216d590..c3eea98 100644 --- a/plugins/ai_face_recog/ai_face_recog_node.cpp +++ b/plugins/ai_face_recog/ai_face_recog_node.cpp @@ -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& 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; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 53108e4..b955a88 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 diff --git a/tests/test_face_track_association.cpp b/tests/test_face_track_association.cpp new file mode 100644 index 0000000..ffe0b8c --- /dev/null +++ b/tests/test_face_track_association.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include + +#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 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 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