From 2df11fa07209a10568c459eb6c102ad8e6e66b8b Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 15 Apr 2026 16:44:00 +0800 Subject: [PATCH] Fix face alarm reentry track latch --- plugins/alarm/alarm_node.cpp | 7 ++++-- tests/test_face_track_alarm.cpp | 39 ++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/plugins/alarm/alarm_node.cpp b/plugins/alarm/alarm_node.cpp index 3ea9190..6abed7b 100644 --- a/plugins/alarm/alarm_node.cpp +++ b/plugins/alarm/alarm_node.cpp @@ -49,6 +49,7 @@ struct FaceTrackState { int quality_hits = 0; bool reported_known = false; bool reported_unknown = false; + uint64_t known_reentry_suppressed_until_ms = 0; std::deque known_hit_times; }; @@ -158,8 +159,9 @@ FaceTrackDecision UpdateFaceTrackState( if (it_last != known_identity_last_alarm_ms.end() && now_ms >= it_last->second && (now_ms - it_last->second) < static_cast(cfg.known_reentry_cooldown_ms)) { - state.reported_known = true; - state.reported_unknown = false; + state.known_reentry_suppressed_until_ms = + std::max(state.known_reentry_suppressed_until_ms, + it_last->second + static_cast(cfg.known_reentry_cooldown_ms)); return decision; } } @@ -174,6 +176,7 @@ FaceTrackDecision UpdateFaceTrackState( } if (state.reported_known) return decision; + if (state.known_reentry_suppressed_until_ms > now_ms) return decision; if (IsKnownLeaningTrack(state, cfg)) return decision; ++state.quality_hits; diff --git a/tests/test_face_track_alarm.cpp b/tests/test_face_track_alarm.cpp index 000c292..bde0166 100644 --- a/tests/test_face_track_alarm.cpp +++ b/tests/test_face_track_alarm.cpp @@ -120,8 +120,14 @@ TEST(FaceTrackAlarmTest, SuppressesKnownReentryInsideConfiguredCooldown) { const FaceTrackDecision suppressed = UpdateFaceTrackState( cfg, reentry_state, known_identity_last_alarm_ms, reentry_track, 4500); EXPECT_FALSE(suppressed.trigger_known); - EXPECT_TRUE(reentry_state.reported_known); + EXPECT_FALSE(reentry_state.reported_known); EXPECT_EQ(known_identity_last_alarm_ms.at(1), 1500u); + + const FaceTrackDecision after_cooldown = UpdateFaceTrackState( + cfg, reentry_state, known_identity_last_alarm_ms, reentry_track, 7000); + EXPECT_TRUE(after_cooldown.trigger_known); + EXPECT_TRUE(reentry_state.reported_known); + EXPECT_EQ(known_identity_last_alarm_ms.at(1), 7000u); } TEST(FaceTrackAlarmTest, DoesNotEmitUnknownForKnownPersonScoreWobble) { @@ -368,6 +374,37 @@ TEST(FaceTrackAlarmTest, KnownPersonReEntryInsideCooldownOnNewTrackDoesNotReAlar EXPECT_EQ(node.known_identity_last_alarm_ms_.at(5), first_alarm_it->second); } +TEST(FaceTrackAlarmTest, KnownPersonReEntryTrackCanAlarmAfterCooldownExpires) { + AlarmNode node; + node.track_agg_cfg_.known_min_hits = 2; + node.track_agg_cfg_.known_hit_window_ms = 5000; + node.track_agg_cfg_.known_reentry_cooldown_ms = 1000; + + auto rule = MakeKnownRule(); + rule.cooldown_ms = 0; + node.face_rules_.push_back(rule); + + FaceRecogItem first_track = MakeKnownFace(81, 5, "alice", 0.88f); + first_track.bbox = Rect{0.0f, 0.0f, 20.0f, 20.0f}; + FaceRecogItem second_track = MakeKnownFace(82, 5, "alice", 0.88f); + second_track.bbox = first_track.bbox; + + EXPECT_EQ(node.Process(MakeFaceFrame(1, first_track)), NodeStatus::OK); + EXPECT_EQ(node.Process(MakeFaceFrame(2, first_track)), NodeStatus::OK); + EXPECT_EQ(node.alarm_count_, 1u); + + EXPECT_EQ(node.Process(MakeFaceFrame(3, second_track)), NodeStatus::OK); + EXPECT_EQ(node.Process(MakeFaceFrame(4, second_track)), NodeStatus::OK); + EXPECT_EQ(node.alarm_count_, 1u); + EXPECT_FALSE(node.face_track_states_.at(82).reported_known); + + node.known_identity_last_alarm_ms_[5] = 0; + + EXPECT_EQ(node.Process(MakeFaceFrame(5, second_track)), NodeStatus::OK); + EXPECT_EQ(node.alarm_count_, 2u); + EXPECT_TRUE(node.face_track_states_.at(82).reported_known); +} + TEST(FaceTrackAlarmTest, IgnoresLegacyDisableFlagAndStillUsesTrackAggregation) { AlarmNode node; NodeContext ctx;