Suppress unknown alarms for known-leaning tracks
This commit is contained in:
parent
afa197fbd5
commit
a3f478aa22
@ -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 &&
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user