feat: associate face results with person tracks
This commit is contained in:
parent
a0bf3f04d5
commit
d8d1fb9331
@ -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
|
||||
|
||||
@ -31,6 +31,7 @@ struct FaceDetResult {
|
||||
|
||||
struct FaceRecogItem {
|
||||
Rect bbox{};
|
||||
int person_track_id = -1;
|
||||
|
||||
int best_person_id = -1;
|
||||
std::string best_name;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
59
tests/test_face_track_association.cpp
Normal file
59
tests/test_face_track_association.cpp
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user