diff --git a/plugins/alarm/alarm_node.cpp b/plugins/alarm/alarm_node.cpp index b8ec961..b09d6f7 100644 --- a/plugins/alarm/alarm_node.cpp +++ b/plugins/alarm/alarm_node.cpp @@ -30,8 +30,6 @@ namespace rk3588 { namespace { struct FaceTrackAggregationConfig { - bool enable = true; - bool require_person_track = true; int known_min_hits = 3; int known_hit_window_ms = 3000; int unknown_min_track_age_ms = 2000; @@ -68,8 +66,6 @@ FaceTrackAggregationConfig ParseFaceTrackAggregationConfig(const SimpleJson& con const SimpleJson* agg = config.Find("face_track_aggregation"); if (!agg || !agg->IsObject()) return cfg; - cfg.enable = agg->ValueOr("enable", cfg.enable); - cfg.require_person_track = agg->ValueOr("require_person_track", cfg.require_person_track); cfg.known_min_hits = std::max(1, agg->ValueOr("known_min_hits", cfg.known_min_hits)); cfg.known_hit_window_ms = std::max(0, agg->ValueOr("known_hit_window_ms", cfg.known_hit_window_ms)); cfg.unknown_min_track_age_ms = std::max(0, agg->ValueOr("unknown_min_track_age_ms", cfg.unknown_min_track_age_ms)); @@ -515,7 +511,6 @@ public: track_agg_cfg_ = new_track_agg_cfg; face_last_trigger_.clear(); face_person_last_trigger_.clear(); - face_vote_history_.clear(); face_track_states_.clear(); packet_buffer_ = std::move(new_packet_buffer); old_actions = std::move(actions_); @@ -636,7 +631,6 @@ private: face_rules_ = ParseFaceRulesFromConfig(config); face_last_trigger_.clear(); face_person_last_trigger_.clear(); - face_vote_history_.clear(); face_track_states_.clear(); } @@ -649,7 +643,7 @@ private: const int img_w = frame->face_recog->img_w > 0 ? frame->face_recog->img_w : frame->width; const int img_h = frame->face_recog->img_h > 0 ? frame->face_recog->img_h : frame->height; const double img_area = (img_w > 0 && img_h > 0) ? static_cast(img_w) * static_cast(img_h) : 0.0; - if (track_agg_cfg_.enable && track_agg_cfg_.state_expire_ms > 0) { + if (track_agg_cfg_.state_expire_ms > 0) { const uint64_t expire_ms = static_cast(track_agg_cfg_.state_expire_ms); for (auto it = face_track_states_.begin(); it != face_track_states_.end();) { const auto& state = it->second; @@ -717,15 +711,9 @@ private: } } - const bool can_use_track_aggregation = - track_agg_cfg_.enable && it.person_track_id >= 0; - if (track_agg_cfg_.enable && track_agg_cfg_.require_person_track && it.person_track_id < 0) { - continue; - } + if (it.person_track_id < 0) continue; - const std::string key = can_use_track_aggregation - ? BuildTrackAwareFaceVoteKey(rule, it) - : BuildFaceVoteKey(rule, it); + const std::string key = BuildFaceTrackKey(rule, it.person_track_id); if (rule.per_person_cooldown_ms > 0) { auto it_last = face_person_last_trigger_.find(key); if (it_last != face_person_last_trigger_.end()) { @@ -736,14 +724,10 @@ private: } } - if (can_use_track_aggregation) { - auto& state = face_track_states_[it.person_track_id]; - const FaceTrackDecision decision = UpdateFaceTrackState(track_agg_cfg_, state, it, now_epoch_ms); - if (rule.kind == FaceRule::Kind::Person && !decision.trigger_known) continue; - if (rule.kind == FaceRule::Kind::Unknown && !decision.trigger_unknown) continue; - } else { - if (!CheckFaceVote(rule, key, now)) continue; - } + auto& state = face_track_states_[it.person_track_id]; + const FaceTrackDecision decision = UpdateFaceTrackState(track_agg_cfg_, state, it, now_epoch_ms); + if (rule.kind == FaceRule::Kind::Person && !decision.trigger_known) continue; + if (rule.kind == FaceRule::Kind::Unknown && !decision.trigger_unknown) continue; matched = true; if (rule.kind == FaceRule::Kind::Person) matched_name = it.best_name; @@ -793,40 +777,8 @@ private: } } - static std::string BuildTrackAwareFaceVoteKey(const FaceRule& rule, const FaceRecogItem& item) { - if (item.person_track_id >= 0) { - return rule.name + "#track:" + std::to_string(item.person_track_id); - } - return BuildFaceVoteKey(rule, item); - } - - static std::string BuildFaceVoteKey(const FaceRule& rule, const FaceRecogItem& item) { - if (rule.kind == FaceRule::Kind::Person) { - if (item.best_person_id >= 0) { - return rule.name + "#" + std::to_string(item.best_person_id); - } - return rule.name + "#" + item.best_name; - } - return rule.name + "#unknown"; - } - - static std::string BuildFaceVoteKey(const FaceRule& rule, int person_id, const std::string& name) { - if (rule.kind == FaceRule::Kind::Person) { - if (person_id >= 0) return rule.name + "#" + std::to_string(person_id); - return rule.name + "#" + name; - } - return rule.name + "#unknown"; - } - - bool CheckFaceVote(const FaceRule& rule, const std::string& key, const std::chrono::steady_clock::time_point& now) { - if (rule.min_hits <= 1 || rule.hit_window_ms <= 0) return true; - auto& dq = face_vote_history_[key]; - const auto window = std::chrono::milliseconds(rule.hit_window_ms); - while (!dq.empty() && (now - dq.front()) > window) { - dq.pop_front(); - } - dq.push_back(now); - return static_cast(dq.size()) >= rule.min_hits; + static std::string BuildFaceTrackKey(const FaceRule& rule, int track_id) { + return rule.name + "#track:" + std::to_string(track_id); } void WorkerLoop() { while (worker_running_.load()) { @@ -874,7 +826,6 @@ private: std::vector face_rules_; std::map face_last_trigger_; std::map face_person_last_trigger_; - std::map> face_vote_history_; FaceTrackAggregationConfig track_agg_cfg_; std::map face_track_states_; std::shared_ptr packet_buffer_; diff --git a/tests/test_face_track_alarm.cpp b/tests/test_face_track_alarm.cpp index d19ece9..9baa930 100644 --- a/tests/test_face_track_alarm.cpp +++ b/tests/test_face_track_alarm.cpp @@ -52,6 +52,14 @@ FramePtr MakeFaceFrame(uint64_t frame_id, const FaceRecogItem& item, int img_w = return frame; } +SimpleJson ParseConfig(const std::string& text) { + SimpleJson config; + std::string err; + const bool ok = ParseSimpleJson(text, config, err); + EXPECT_TRUE(ok) << err; + return config; +} + AlarmNode::FaceRule MakeKnownRule() { AlarmNode::FaceRule rule; rule.name = "known_person"; @@ -61,16 +69,6 @@ AlarmNode::FaceRule MakeKnownRule() { return rule; } -AlarmNode::FaceRule MakeLegacyKnownRule() { - AlarmNode::FaceRule rule; - rule.name = "known_person"; - rule.kind = AlarmNode::FaceRule::Kind::Person; - rule.cooldown_ms = 0; - rule.min_hits = 2; - rule.hit_window_ms = 5000; - return rule; -} - TEST(FaceTrackAlarmTest, IgnoresLowQualityUntrackedFace) { FaceTrackAggregationConfig cfg; FaceTrackState state; @@ -152,7 +150,6 @@ TEST(FaceTrackAlarmTest, ExpiresStateAfterTrackInactivity) { TEST(FaceTrackAlarmTest, DisqualifiedFramesDoNotAdvanceTrackAggregation) { AlarmNode node; - node.track_agg_cfg_.enable = true; node.track_agg_cfg_.known_min_hits = 2; node.track_agg_cfg_.known_hit_window_ms = 5000; auto rule = MakeKnownRule(); @@ -174,20 +171,58 @@ TEST(FaceTrackAlarmTest, DisqualifiedFramesDoNotAdvanceTrackAggregation) { EXPECT_EQ(node.alarm_count_, 1u); } -TEST(FaceTrackAlarmTest, LegacyFallbackKeepsPersonVoteAcrossTrackChanges) { +TEST(FaceTrackAlarmTest, UntrackedQualifiedFaceDoesNotTriggerAlarm) { AlarmNode node; - node.track_agg_cfg_.enable = false; - node.face_rules_.push_back(MakeLegacyKnownRule()); + node.track_agg_cfg_.known_min_hits = 2; + node.track_agg_cfg_.known_hit_window_ms = 5000; + node.face_rules_.push_back(MakeKnownRule()); - FaceRecogItem first = MakeKnownFace(21, 5, "alice", 0.88f); + FaceRecogItem first = MakeKnownFace(-1, 5, "alice", 0.88f); first.bbox = Rect{0.0f, 0.0f, 20.0f, 20.0f}; FaceRecogItem second = first; - second.person_track_id = 22; EXPECT_EQ(node.Process(MakeFaceFrame(1, first)), NodeStatus::OK); EXPECT_EQ(node.alarm_count_, 0u); EXPECT_EQ(node.Process(MakeFaceFrame(2, second)), NodeStatus::OK); + EXPECT_EQ(node.alarm_count_, 0u); +} + +TEST(FaceTrackAlarmTest, IgnoresLegacyDisableFlagAndStillUsesTrackAggregation) { + AlarmNode node; + NodeContext ctx; + ctx.input_queue = std::make_shared>(4, QueueDropStrategy::DropOldest); + + const SimpleJson config = ParseConfig(R"({ + "id": "alarm", + "face_rules": [ + { + "name": "known_person", + "type": "person", + "persons": ["alice"], + "cooldown_ms": 0 + } + ], + "face_track_aggregation": { + "enable": false, + "known": { + "min_hits": 2, + "hit_window_ms": 5000 + } + } + })"); + + ASSERT_TRUE(node.Init(config, ctx)); + EXPECT_EQ(node.track_agg_cfg_.known_min_hits, 2); + EXPECT_EQ(node.track_agg_cfg_.known_hit_window_ms, 5000); + + FaceRecogItem item = MakeKnownFace(31, 5, "alice", 0.88f); + item.bbox = Rect{0.0f, 0.0f, 20.0f, 20.0f}; + + EXPECT_EQ(node.Process(MakeFaceFrame(1, item)), NodeStatus::OK); + EXPECT_EQ(node.alarm_count_, 0u); + + EXPECT_EQ(node.Process(MakeFaceFrame(2, item)), NodeStatus::OK); EXPECT_EQ(node.alarm_count_, 1u); }