diff --git a/configs/full_pipeline_1080p_test_alarm.json b/configs/full_pipeline_1080p_test_alarm.json index 4071d9e..d345827 100644 --- a/configs/full_pipeline_1080p_test_alarm.json +++ b/configs/full_pipeline_1080p_test_alarm.json @@ -366,8 +366,7 @@ }, "unknown": { "min_track_age_ms": 2000, - "min_quality_hits": 4, - "reentry_cooldown_ms": 300000 + "min_quality_hits": 4 } }, "face_rules": [ diff --git a/plugins/alarm/alarm_node.cpp b/plugins/alarm/alarm_node.cpp index 6abed7b..7cefa40 100644 --- a/plugins/alarm/alarm_node.cpp +++ b/plugins/alarm/alarm_node.cpp @@ -36,7 +36,6 @@ struct FaceTrackAggregationConfig { int known_reentry_cooldown_ms = 0; int unknown_min_track_age_ms = 2000; int unknown_min_quality_hits = 4; - int unknown_reentry_cooldown_ms = 0; int state_expire_ms = 5000; }; @@ -97,7 +96,6 @@ FaceTrackAggregationConfig ParseFaceTrackAggregationConfig(const SimpleJson& con cfg.known_reentry_cooldown_ms = std::max(0, agg->ValueOr("known_reentry_cooldown_ms", cfg.known_reentry_cooldown_ms)); cfg.unknown_min_track_age_ms = std::max(0, agg->ValueOr("unknown_min_track_age_ms", cfg.unknown_min_track_age_ms)); cfg.unknown_min_quality_hits = std::max(1, agg->ValueOr("unknown_min_quality_hits", cfg.unknown_min_quality_hits)); - cfg.unknown_reentry_cooldown_ms = std::max(0, agg->ValueOr("unknown_reentry_cooldown_ms", cfg.unknown_reentry_cooldown_ms)); cfg.state_expire_ms = std::max(0, agg->ValueOr("state_expire_ms", cfg.state_expire_ms)); if (const SimpleJson* known = agg->Find("known"); known && known->IsObject()) { @@ -108,7 +106,6 @@ FaceTrackAggregationConfig ParseFaceTrackAggregationConfig(const SimpleJson& con if (const SimpleJson* unknown = agg->Find("unknown"); unknown && unknown->IsObject()) { cfg.unknown_min_track_age_ms = std::max(0, unknown->ValueOr("min_track_age_ms", cfg.unknown_min_track_age_ms)); cfg.unknown_min_quality_hits = std::max(1, unknown->ValueOr("min_quality_hits", cfg.unknown_min_quality_hits)); - cfg.unknown_reentry_cooldown_ms = std::max(0, unknown->ValueOr("reentry_cooldown_ms", cfg.unknown_reentry_cooldown_ms)); } return cfg; @@ -176,6 +173,11 @@ FaceTrackDecision UpdateFaceTrackState( } if (state.reported_known) return decision; + if (!state.reported_known && + state.best_known_person_id >= 0 && + state.known_reentry_suppressed_until_ms > 0) { + return decision; + } if (state.known_reentry_suppressed_until_ms > now_ms) return decision; if (IsKnownLeaningTrack(state, cfg)) return decision; diff --git a/tests/test_face_track_alarm.cpp b/tests/test_face_track_alarm.cpp index bde0166..1fbfdbd 100644 --- a/tests/test_face_track_alarm.cpp +++ b/tests/test_face_track_alarm.cpp @@ -405,6 +405,35 @@ TEST(FaceTrackAlarmTest, KnownPersonReEntryTrackCanAlarmAfterCooldownExpires) { EXPECT_TRUE(node.face_track_states_.at(82).reported_known); } +TEST(FaceTrackAlarmTest, SuppressedKnownReentryTrackDoesNotAgeIntoUnknownAfterSuppressionWindowWobble) { + FaceTrackAggregationConfig cfg; + cfg.known_min_hits = 2; + cfg.known_hit_window_ms = 5000; + cfg.known_reentry_cooldown_ms = 5000; + cfg.unknown_min_track_age_ms = 1000; + cfg.unknown_min_quality_hits = 1; + + std::unordered_map known_identity_last_alarm_ms; + const FaceRecogItem initial_track = MakeKnownFace(91, 5, "alice", 0.88f); + + FaceTrackState first_track_state; + EXPECT_FALSE(UpdateFaceTrackState(cfg, first_track_state, known_identity_last_alarm_ms, initial_track, 1000).trigger_known); + EXPECT_TRUE(UpdateFaceTrackState(cfg, first_track_state, known_identity_last_alarm_ms, initial_track, 1500).trigger_known); + + FaceTrackState reentry_state; + FaceRecogItem reentry_track = initial_track; + reentry_track.person_track_id = 92; + + 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); + const FaceTrackDecision wobble_decision = UpdateFaceTrackState( + cfg, reentry_state, known_identity_last_alarm_ms, wobble, 7000); + EXPECT_FALSE(wobble_decision.trigger_unknown); + EXPECT_FALSE(reentry_state.reported_unknown); +} + TEST(FaceTrackAlarmTest, IgnoresLegacyDisableFlagAndStillUsesTrackAggregation) { AlarmNode node; NodeContext ctx; @@ -445,5 +474,38 @@ TEST(FaceTrackAlarmTest, IgnoresLegacyDisableFlagAndStillUsesTrackAggregation) { EXPECT_EQ(node.alarm_count_, 1u); } +TEST(FaceTrackAlarmTest, ParsesTrackAggregationWithoutUnknownReentryCooldownContract) { + AlarmNode node; + NodeContext ctx; + ctx.input_queue = std::make_shared>(4, QueueDropStrategy::DropOldest); + + const SimpleJson config = ParseConfig(R"({ + "id": "alarm", + "face_rules": [ + { + "name": "unknown_face", + "type": "unknown", + "cooldown_ms": 0 + } + ], + "face_track_aggregation": { + "known": { + "min_hits": 2, + "hit_window_ms": 5000, + "reentry_cooldown_ms": 60000 + }, + "unknown": { + "min_track_age_ms": 1200, + "min_quality_hits": 2 + } + } + })"); + + ASSERT_TRUE(node.Init(config, ctx)); + EXPECT_EQ(node.track_agg_cfg_.known_reentry_cooldown_ms, 60000); + EXPECT_EQ(node.track_agg_cfg_.unknown_min_track_age_ms, 1200); + EXPECT_EQ(node.track_agg_cfg_.unknown_min_quality_hits, 2); +} + } // namespace } // namespace rk3588