From 87c24bce05c9e5baa8cf629f278671aef8162353 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 16 Apr 2026 09:18:27 +0800 Subject: [PATCH] Clarify face recognition semantics and API action logging --- include/face/face_recog_debug.h | 2 +- include/face/face_result.h | 21 +++++++++++- plugins/ai_face_recog/ai_face_recog_node.cpp | 4 +-- plugins/alarm/actions/action_base.h | 20 +++++++++++- plugins/alarm/actions/external_api_action.cpp | 28 +++++++++++++++- plugins/alarm/actions/external_api_action.h | 7 ++++ plugins/alarm/actions/log_action.cpp | 2 +- plugins/alarm/alarm_node.cpp | 10 +++--- plugins/osd/osd_node.cpp | 6 ++-- tests/CMakeLists.txt | 1 + tests/test_external_api_action.cpp | 32 +++++++++++++++++++ tests/test_face_recog_debug.cpp | 7 ++-- tests/test_face_track_alarm.cpp | 16 +++++----- tests/test_face_track_association.cpp | 2 +- tests/test_log_action.cpp | 2 +- 15 files changed, 132 insertions(+), 28 deletions(-) create mode 100644 tests/test_external_api_action.cpp diff --git a/include/face/face_recog_debug.h b/include/face/face_recog_debug.h index 3ee4aa1..042ae24 100644 --- a/include/face/face_recog_debug.h +++ b/include/face/face_recog_debug.h @@ -34,7 +34,7 @@ inline std::string BuildFaceRecogDebugSummaryLine( const std::string& node_id, uint64_t frame_id, const FaceRecogItem& item) { const float sim_margin = item.best_sim - item.second_sim; const std::string candidate = item.candidate_name.empty() ? "n/a" : item.candidate_name; - const std::string status = item.unknown ? "unknown" : "known"; + const std::string status = FaceRecogStateName(item.state); std::ostringstream oss; oss << "[ai_face_recog] match" diff --git a/include/face/face_result.h b/include/face/face_result.h index 60d770d..1695415 100644 --- a/include/face/face_result.h +++ b/include/face/face_result.h @@ -9,6 +9,25 @@ namespace rk3588 { +enum class FaceRecogState { + Uncertain, + Known, +}; + +inline const char* FaceRecogStateName(FaceRecogState state) { + switch (state) { + case FaceRecogState::Known: + return "known"; + case FaceRecogState::Uncertain: + default: + return "uncertain"; + } +} + +inline bool FaceRecogStateIsKnown(FaceRecogState state) { + return state == FaceRecogState::Known; +} + struct Point2f { float x = 0.0f; float y = 0.0f; @@ -32,11 +51,11 @@ struct FaceDetResult { struct FaceRecogItem { Rect bbox{}; int person_track_id = -1; + FaceRecogState state = FaceRecogState::Uncertain; int best_person_id = -1; std::string best_name; float best_sim = 0.0f; - bool unknown = true; float second_sim = 0.0f; int candidate_person_id = -1; std::string candidate_name; diff --git a/plugins/ai_face_recog/ai_face_recog_node.cpp b/plugins/ai_face_recog/ai_face_recog_node.cpp index e509842..8539313 100644 --- a/plugins/ai_face_recog/ai_face_recog_node.cpp +++ b/plugins/ai_face_recog/ai_face_recog_node.cpp @@ -1113,10 +1113,10 @@ private: item.candidate_person_id = sr.best_person_id; item.candidate_name = sr.best_name; item.best_person_id = accept ? sr.best_person_id : -1; - item.best_name = accept ? sr.best_name : "unknown"; + item.best_name = accept ? sr.best_name : ""; item.best_sim = sr.best_sim; item.second_sim = sr.second_sim; - item.unknown = !accept; + item.state = accept ? FaceRecogState::Known : FaceRecogState::Uncertain; if (cfg->emit_embedding) item.embedding = emb; rr.items.push_back(std::move(item)); diff --git a/plugins/alarm/actions/action_base.h b/plugins/alarm/actions/action_base.h index 420ec05..32786e5 100644 --- a/plugins/alarm/actions/action_base.h +++ b/plugins/alarm/actions/action_base.h @@ -9,8 +9,26 @@ namespace rk3588 { +enum class FaceAlarmStatus { + Uncertain, + Known, + Unknown, +}; + +inline const char* FaceAlarmStatusName(FaceAlarmStatus status) { + switch (status) { + case FaceAlarmStatus::Known: + return "known"; + case FaceAlarmStatus::Unknown: + return "unknown"; + case FaceAlarmStatus::Uncertain: + default: + return "uncertain"; + } +} + struct FaceAlarmMatch { - bool unknown = true; + FaceAlarmStatus status = FaceAlarmStatus::Uncertain; int candidate_person_id = -1; std::string candidate_name; float best_sim = 0.0f; diff --git a/plugins/alarm/actions/external_api_action.cpp b/plugins/alarm/actions/external_api_action.cpp index 86e6397..6b09403 100644 --- a/plugins/alarm/actions/external_api_action.cpp +++ b/plugins/alarm/actions/external_api_action.cpp @@ -116,6 +116,28 @@ const SimpleJson* FindByDottedPath(const SimpleJson& root, const std::string& pa } // namespace +std::string BuildExternalApiResultLogLine(const std::string& prefix, + const std::string& alarm_content, + const std::string& pic_url, + const std::string& video_url, + long http_code, + const std::string& err) { + std::ostringstream oss; + oss << "[ExternalApiAction] " << prefix + << " http=" << http_code + << " alarm_content=" << alarm_content; + if (!pic_url.empty()) { + oss << " pic_url=" << pic_url; + } + if (!video_url.empty()) { + oss << " video_url=" << video_url; + } + if (!err.empty()) { + oss << " error=" << err; + } + return oss.str(); +} + ExternalApiAction::~ExternalApiAction() { Drain(); Stop(); @@ -256,6 +278,8 @@ void ExternalApiAction::WorkerLoop() { long http_code = 0; std::string send_err; if (SendMessageWithToken(job, token, http_code, send_err)) { + LogInfo(BuildExternalApiResultLogLine( + "send ok", job.alarm_content, job.pic_url, job.video_url, http_code, "")); break; } @@ -275,7 +299,8 @@ void ExternalApiAction::WorkerLoop() { LogWarn("[ExternalApiAction] refresh token failed: " + terr); } - LogWarn("[ExternalApiAction] send failed: " + send_err + " (http=" + std::to_string(http_code) + ")"); + LogWarn(BuildExternalApiResultLogLine( + "send failed", job.alarm_content, job.pic_url, job.video_url, http_code, send_err)); } } @@ -307,6 +332,7 @@ bool ExternalApiAction::EnsureToken(std::string& err) { if (!FetchToken(token, err)) { return false; } + LogInfo("[ExternalApiAction] token fetched successfully"); std::lock_guard tl(token_mu_); token_ = std::move(token); diff --git a/plugins/alarm/actions/external_api_action.h b/plugins/alarm/actions/external_api_action.h index b989b2f..5b3ed56 100644 --- a/plugins/alarm/actions/external_api_action.h +++ b/plugins/alarm/actions/external_api_action.h @@ -12,6 +12,13 @@ namespace rk3588 { +std::string BuildExternalApiResultLogLine(const std::string& prefix, + const std::string& alarm_content, + const std::string& pic_url, + const std::string& video_url, + long http_code, + const std::string& err); + class ExternalApiAction : public IAlarmAction { public: ~ExternalApiAction() override; diff --git a/plugins/alarm/actions/log_action.cpp b/plugins/alarm/actions/log_action.cpp index 31b9947..3e3bcba 100644 --- a/plugins/alarm/actions/log_action.cpp +++ b/plugins/alarm/actions/log_action.cpp @@ -77,7 +77,7 @@ void LogAction::Execute(AlarmEvent& event, std::shared_ptr /*frame*/) { if (i > 0) oss << ", "; const float sim_margin = match.best_sim - match.second_sim; const std::string candidate = match.candidate_name.empty() ? "n/a" : match.candidate_name; - oss << "{status=" << (match.unknown ? "unknown" : "known") + oss << "{status=" << FaceAlarmStatusName(match.status) << " candidate=" << candidate << " candidate_id=" << match.candidate_person_id << " best_sim=" << std::fixed << std::setprecision(2) << match.best_sim diff --git a/plugins/alarm/alarm_node.cpp b/plugins/alarm/alarm_node.cpp index 7cefa40..3217eb0 100644 --- a/plugins/alarm/alarm_node.cpp +++ b/plugins/alarm/alarm_node.cpp @@ -136,7 +136,7 @@ FaceTrackDecision UpdateFaceTrackState( } PruneKnownHitTimes(state, cfg, now_ms); - if (!item.unknown) { + if (FaceRecogStateIsKnown(item.state)) { const bool same_identity = state.best_known_person_id == item.best_person_id && state.best_known_name == item.best_name; @@ -692,10 +692,10 @@ private: } if (rule.kind == FaceRule::Kind::Unknown) { - if (!it.unknown) return false; + if (FaceRecogStateIsKnown(it.state)) return false; if (it.best_sim < rule.min_sim) return false; } else { - if (it.unknown) return false; + if (!FaceRecogStateIsKnown(it.state)) return false; if (it.best_sim < rule.min_sim) return false; if (!rule.persons.empty()) { bool ok = false; @@ -830,7 +830,9 @@ private: dets.push_back(std::move(d)); trigger_keys.push_back(key); FaceAlarmMatch fm; - fm.unknown = it.unknown; + fm.status = (rule.kind == FaceRule::Kind::Unknown) + ? FaceAlarmStatus::Unknown + : (FaceRecogStateIsKnown(it.state) ? FaceAlarmStatus::Known : FaceAlarmStatus::Uncertain); fm.candidate_person_id = it.candidate_person_id; fm.candidate_name = it.candidate_name; fm.best_sim = it.best_sim; diff --git a/plugins/osd/osd_node.cpp b/plugins/osd/osd_node.cpp index 610a13d..2b64afd 100644 --- a/plugins/osd/osd_node.cpp +++ b/plugins/osd/osd_node.cpp @@ -744,7 +744,7 @@ private: rw = Clamp(rw, 1, w - x); rh = Clamp(rh, 1, h - y); im_rect rect{x, y, rw, rh}; - const Color color = it.unknown ? Color{255, 0, 0} : Color{0, 255, 0}; + const Color color = FaceRecogStateIsKnown(it.state) ? Color{0, 255, 0} : Color{255, 0, 0}; if (imrectangleTask(job, dst, rect, PackColorArgb(color), line_width_) <= 0) { ok = false; break; @@ -790,7 +790,7 @@ private: int x2 = static_cast(it.bbox.x + it.bbox.w); int y2 = static_cast(it.bbox.y + it.bbox.h); - const Color color = it.unknown ? Color{255, 0, 0} : Color{0, 255, 0}; + const Color color = FaceRecogStateIsKnown(it.state) ? Color{0, 255, 0} : Color{255, 0, 0}; if (do_cpu_bbox) { DrawRect(data, w, h, stride, fmt, x1, y1, x2, y2, line_width_, color); @@ -798,7 +798,7 @@ private: if (do_cpu_text) { char label[96]; - const char* name = it.best_name.empty() ? (it.unknown ? "unknown" : "") : it.best_name.c_str(); + const char* name = it.best_name.empty() ? FaceRecogStateName(it.state) : it.best_name.c_str(); snprintf(label, sizeof(label), "%s %.2f", name, it.best_sim); int text_y = y1 - 8 * font_scale_; if (text_y < 0) text_y = y1 + 2; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a850409..7820710 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -50,6 +50,7 @@ add_executable(rk3588_gtests test_face_recog_debug.cpp test_face_track_association.cpp test_face_track_alarm.cpp + test_external_api_action.cpp test_infer_backend.cpp test_image_processor.cpp test_codec_backend.cpp diff --git a/tests/test_external_api_action.cpp b/tests/test_external_api_action.cpp new file mode 100644 index 0000000..9ac087c --- /dev/null +++ b/tests/test_external_api_action.cpp @@ -0,0 +1,32 @@ +#include + +#include + +#include "../plugins/alarm/actions/external_api_action.h" + +namespace rk3588 { +namespace { + +TEST(ExternalApiActionTest, BuildsSuccessLogLineWithContext) { + const std::string line = BuildExternalApiResultLogLine( + "send ok", "known_person:reg_001", "http://example/pic.jpg", "http://example/clip.mp4", 200, ""); + + EXPECT_NE(line.find("send ok"), std::string::npos); + EXPECT_NE(line.find("http=200"), std::string::npos); + EXPECT_NE(line.find("alarm_content=known_person:reg_001"), std::string::npos); + EXPECT_NE(line.find("pic_url=http://example/pic.jpg"), std::string::npos); + EXPECT_NE(line.find("video_url=http://example/clip.mp4"), std::string::npos); +} + +TEST(ExternalApiActionTest, BuildsFailureLogLineWithError) { + const std::string line = BuildExternalApiResultLogLine( + "send failed", "unknown_face", "", "", 500, "timeout"); + + EXPECT_NE(line.find("send failed"), std::string::npos); + EXPECT_NE(line.find("http=500"), std::string::npos); + EXPECT_NE(line.find("error=timeout"), std::string::npos); + EXPECT_NE(line.find("alarm_content=unknown_face"), std::string::npos); +} + +} // namespace +} // namespace rk3588 diff --git a/tests/test_face_recog_debug.cpp b/tests/test_face_recog_debug.cpp index 75f0fdc..258d5c9 100644 --- a/tests/test_face_recog_debug.cpp +++ b/tests/test_face_recog_debug.cpp @@ -29,22 +29,21 @@ TEST(FaceRecogDebugTest, ParsesDebugConfigWithDefaultsAndOverrides) { EXPECT_EQ(parsed.min_log_interval_ms, 350); } -TEST(FaceRecogDebugTest, BuildsSummaryLineForUnknownCandidate) { +TEST(FaceRecogDebugTest, BuildsSummaryLineForUncertainCandidate) { FaceRecogItem item; item.bbox = Rect{10.0f, 20.0f, 30.0f, 40.0f}; item.candidate_person_id = 7; item.candidate_name = "alice"; - item.best_name = "unknown"; item.best_sim = 0.44f; item.second_sim = 0.41f; - item.unknown = true; + item.state = FaceRecogState::Uncertain; const std::string line = BuildFaceRecogDebugSummaryLine("face_recog", 42, item); EXPECT_NE(line.find("[ai_face_recog] match"), std::string::npos); EXPECT_NE(line.find("id=face_recog"), std::string::npos); EXPECT_NE(line.find("frame=42"), std::string::npos); - EXPECT_NE(line.find("status=unknown"), std::string::npos); + EXPECT_NE(line.find("status=uncertain"), std::string::npos); EXPECT_NE(line.find("candidate=alice"), std::string::npos); EXPECT_NE(line.find("candidate_id=7"), std::string::npos); EXPECT_NE(line.find("best_sim=0.44"), std::string::npos); diff --git a/tests/test_face_track_alarm.cpp b/tests/test_face_track_alarm.cpp index 1fbfdbd..e43ec52 100644 --- a/tests/test_face_track_alarm.cpp +++ b/tests/test_face_track_alarm.cpp @@ -26,18 +26,18 @@ FaceRecogItem MakeKnownFace(int track_id, int person_id, const std::string& name item.candidate_name = name; item.best_sim = best_sim; item.second_sim = 0.20f; - item.unknown = false; + item.state = FaceRecogState::Known; return item; } -FaceRecogItem MakeUnknownFace(int track_id, int candidate_person_id, const std::string& candidate_name, float best_sim) { +FaceRecogItem MakeUncertainFace(int track_id, int candidate_person_id, const std::string& candidate_name, float best_sim) { FaceRecogItem item; item.person_track_id = track_id; item.candidate_person_id = candidate_person_id; item.candidate_name = candidate_name; item.best_sim = best_sim; item.second_sim = best_sim - 0.02f; - item.unknown = true; + item.state = FaceRecogState::Uncertain; return item; } @@ -75,7 +75,7 @@ TEST(FaceTrackAlarmTest, IgnoresLowQualityUntrackedFace) { FaceTrackState state; std::unordered_map known_identity_last_alarm_ms; - FaceRecogItem item = MakeUnknownFace(-1, -1, "", 0.40f); + FaceRecogItem item = MakeUncertainFace(-1, -1, "", 0.40f); const FaceTrackDecision decision = UpdateFaceTrackState(cfg, state, known_identity_last_alarm_ms, item, 1000); EXPECT_FALSE(decision.trigger_known); @@ -145,7 +145,7 @@ TEST(FaceTrackAlarmTest, DoesNotEmitUnknownForKnownPersonScoreWobble) { EXPECT_FALSE(UpdateFaceTrackState(cfg, state, known_identity_last_alarm_ms, known, 1500).trigger_known); EXPECT_TRUE(UpdateFaceTrackState(cfg, state, known_identity_last_alarm_ms, known, 2000).trigger_known); - const FaceRecogItem wobble = MakeUnknownFace(9, 1, "reg_001", 0.42f); + const FaceRecogItem wobble = MakeUncertainFace(9, 1, "reg_001", 0.42f); const FaceTrackDecision decision = UpdateFaceTrackState( cfg, state, known_identity_last_alarm_ms, wobble, 2600); EXPECT_FALSE(decision.trigger_known); @@ -169,7 +169,7 @@ TEST(FaceTrackAlarmTest, DoesNotEmitUnknownWhileTrackIsKnownLeaning) { EXPECT_EQ(state.best_known_name, "alice"); EXPECT_EQ(state.known_hit_times.size(), 2u); - const FaceRecogItem wobble = MakeUnknownFace(10, 1, "alice", 0.42f); + const FaceRecogItem wobble = MakeUncertainFace(10, 1, "alice", 0.42f); const FaceTrackDecision decision = UpdateFaceTrackState( cfg, state, known_identity_last_alarm_ms, wobble, 2600); EXPECT_FALSE(decision.trigger_known); @@ -268,7 +268,7 @@ TEST(FaceTrackAlarmTest, KnownLeaningTrackDoesNotFireUnknownRule) { FaceRecogItem known = MakeKnownFace(41, 5, "alice", 0.88f); known.bbox = Rect{0.0f, 0.0f, 20.0f, 20.0f}; - FaceRecogItem wobble = MakeUnknownFace(41, 5, "alice", 0.42f); + FaceRecogItem wobble = MakeUncertainFace(41, 5, "alice", 0.42f); wobble.bbox = known.bbox; EXPECT_EQ(node.Process(MakeFaceFrame(1, known)), NodeStatus::OK); @@ -427,7 +427,7 @@ TEST(FaceTrackAlarmTest, SuppressedKnownReentryTrackDoesNotAgeIntoUnknownAfterSu EXPECT_FALSE(UpdateFaceTrackState(cfg, reentry_state, known_identity_last_alarm_ms, reentry_track, 4000).trigger_known); EXPECT_FALSE(UpdateFaceTrackState(cfg, reentry_state, known_identity_last_alarm_ms, reentry_track, 4500).trigger_known); - FaceRecogItem wobble = MakeUnknownFace(92, 5, "alice", 0.42f); + FaceRecogItem wobble = MakeUncertainFace(92, 5, "alice", 0.42f); const FaceTrackDecision wobble_decision = UpdateFaceTrackState( cfg, reentry_state, known_identity_last_alarm_ms, wobble, 7000); EXPECT_FALSE(wobble_decision.trigger_unknown); diff --git a/tests/test_face_track_association.cpp b/tests/test_face_track_association.cpp index 914d32d..46a5197 100644 --- a/tests/test_face_track_association.cpp +++ b/tests/test_face_track_association.cpp @@ -102,7 +102,7 @@ TEST(FaceTrackAssociationTest, DebugLineIncludesPersonTrackId) { item.candidate_name = "alice"; item.best_sim = 0.89f; item.second_sim = 0.74f; - item.unknown = false; + item.state = FaceRecogState::Known; const std::string line = BuildFaceRecogDebugSummaryLine("face_recog", 42, item); diff --git a/tests/test_log_action.cpp b/tests/test_log_action.cpp index fff8e42..2325761 100644 --- a/tests/test_log_action.cpp +++ b/tests/test_log_action.cpp @@ -32,7 +32,7 @@ TEST(LogActionTest, IncludesFaceRecognitionDetailsInAlarmLog) { event.detections.push_back(det); FaceAlarmMatch match; - match.unknown = true; + match.status = FaceAlarmStatus::Unknown; match.candidate_person_id = 7; match.candidate_name = "alice"; match.best_sim = 0.44f;