Suppress unknown alarms for known-leaning tracks

This commit is contained in:
tian 2026-04-15 15:30:59 +08:00
parent afa197fbd5
commit a3f478aa22
2 changed files with 80 additions and 8 deletions

View File

@ -54,6 +54,28 @@ struct FaceTrackDecision {
bool trigger_unknown = false;
};
void PruneKnownHitTimes(
FaceTrackState& state,
const FaceTrackAggregationConfig& cfg,
uint64_t now_ms) {
const uint64_t known_window_ms = static_cast<uint64_t>(std::max(0, cfg.known_hit_window_ms));
if (known_window_ms == 0) return;
while (!state.known_hit_times.empty() &&
now_ms > state.known_hit_times.front() &&
(now_ms - state.known_hit_times.front()) > known_window_ms) {
state.known_hit_times.pop_front();
}
}
bool IsKnownLeaningTrack(
const FaceTrackState& state,
const FaceTrackAggregationConfig& cfg) {
return !state.reported_known &&
!state.known_hit_times.empty() &&
static_cast<int>(state.known_hit_times.size()) < std::max(1, cfg.known_min_hits) &&
(state.best_known_person_id >= 0 || !state.best_known_name.empty());
}
void ResetFaceTrackState(FaceTrackState& state, int track_id, uint64_t now_ms) {
state = FaceTrackState{};
state.track_id = track_id;
@ -107,7 +129,7 @@ FaceTrackDecision UpdateFaceTrackState(
state.last_seen_ms = now_ms;
}
const auto known_window_ms = static_cast<uint64_t>(std::max(0, cfg.known_hit_window_ms));
PruneKnownHitTimes(state, cfg, now_ms);
if (!item.unknown) {
const bool same_identity =
state.best_known_person_id == item.best_person_id &&
@ -118,13 +140,6 @@ FaceTrackDecision UpdateFaceTrackState(
state.best_known_name = item.best_name;
}
if (known_window_ms > 0) {
while (!state.known_hit_times.empty() &&
now_ms > state.known_hit_times.front() &&
(now_ms - state.known_hit_times.front()) > known_window_ms) {
state.known_hit_times.pop_front();
}
}
state.known_hit_times.push_back(now_ms);
state.quality_hits = 0;
@ -138,6 +153,7 @@ FaceTrackDecision UpdateFaceTrackState(
}
if (state.reported_known) return decision;
if (IsKnownLeaningTrack(state, cfg)) return decision;
++state.quality_hits;
if (!state.reported_unknown &&

View File

@ -116,6 +116,28 @@ TEST(FaceTrackAlarmTest, DoesNotEmitUnknownForKnownPersonScoreWobble) {
EXPECT_FALSE(decision.trigger_unknown);
}
TEST(FaceTrackAlarmTest, DoesNotEmitUnknownWhileTrackIsKnownLeaning) {
FaceTrackAggregationConfig cfg;
cfg.known_min_hits = 3;
cfg.known_hit_window_ms = 3000;
cfg.unknown_min_track_age_ms = 1000;
cfg.unknown_min_quality_hits = 1;
FaceTrackState state;
const FaceRecogItem known = MakeKnownFace(10, 1, "alice", 0.63f);
EXPECT_FALSE(UpdateFaceTrackState(cfg, state, known, 1000).trigger_known);
EXPECT_FALSE(UpdateFaceTrackState(cfg, state, known, 1500).trigger_known);
EXPECT_EQ(state.best_known_person_id, 1);
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 FaceTrackDecision decision = UpdateFaceTrackState(cfg, state, wobble, 2600);
EXPECT_FALSE(decision.trigger_known);
EXPECT_FALSE(decision.trigger_unknown);
}
TEST(FaceTrackAlarmTest, ResetsKnownStateWhenIdentityChangesOnSameTrack) {
FaceTrackAggregationConfig cfg;
cfg.known_min_hits = 2;
@ -188,6 +210,40 @@ TEST(FaceTrackAlarmTest, UntrackedQualifiedFaceDoesNotTriggerAlarm) {
EXPECT_EQ(node.alarm_count_, 0u);
}
TEST(FaceTrackAlarmTest, KnownLeaningTrackDoesNotFireUnknownRule) {
AlarmNode node;
node.track_agg_cfg_.known_min_hits = 3;
node.track_agg_cfg_.known_hit_window_ms = 5000;
node.track_agg_cfg_.unknown_min_track_age_ms = 1000;
node.track_agg_cfg_.unknown_min_quality_hits = 1;
auto known_rule = MakeKnownRule();
AlarmNode::FaceRule unknown_rule;
unknown_rule.name = "unknown_face";
unknown_rule.kind = AlarmNode::FaceRule::Kind::Unknown;
unknown_rule.cooldown_ms = 0;
node.face_rules_.push_back(known_rule);
node.face_rules_.push_back(unknown_rule);
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);
wobble.bbox = known.bbox;
EXPECT_EQ(node.Process(MakeFaceFrame(1, known)), NodeStatus::OK);
EXPECT_EQ(node.alarm_count_, 0u);
EXPECT_EQ(node.Process(MakeFaceFrame(2, known)), NodeStatus::OK);
EXPECT_EQ(node.alarm_count_, 0u);
EXPECT_EQ(node.Process(MakeFaceFrame(3, wobble)), NodeStatus::OK);
EXPECT_EQ(node.alarm_count_, 0u);
EXPECT_EQ(node.Process(MakeFaceFrame(4, known)), NodeStatus::OK);
EXPECT_EQ(node.alarm_count_, 1u);
}
TEST(FaceTrackAlarmTest, IgnoresLegacyDisableFlagAndStillUsesTrackAggregation) {
AlarmNode node;
NodeContext ctx;