FEAT: Déduplication des événements — architecture 4 niveaux (motion gate, IoU, cooldown, embeddings) #5

Closed
opened 2026-08-10 21:02:17 +02:00 by nonobis · 0 comments
Owner

🎯 Contexte

Sans déduplication, un objet stationnaire génère des milliers d'événements/notifications (ex. 3000 snapshots pour 10 min). Aucun des 4 niveaux de la spec n'est implémenté.

📚 Référence spec (MASTER_SPECIFICATION.md v2.0)

Partie 3 — AI Pipeline et Dédoublonnage (3.2) :

  • Niveau 0 — Motion Gate (Agent, pré-YOLO) : pixel diff > MotionThreshold (0.3) → YOLO ; sinon SKIP (économie 90% CPU)
  • Niveau 1 — Spatial Dedup (Agent, post-YOLO) : IoU(bbox, précédente) > 0.6 → même objet ; cooldown actif → pas d'envoi ; DedupCooldownSec écoulé → nouvel événement
  • Niveau 2 — Temporal Cooldown (Central) : IMemoryCache par (cameraId, trackId, objectType) ; clé présente → IsSuppressed=true ; cooldowns : personne 30s, véhicule 60s, inconnu 300s
  • Niveau 3 — Embedding Dedup (Central, visages) : cache LRU d'embeddings faciaux (float[512], 10K entries), distance L2 < 0.6 → même personne ; inconnu ajouté avec TTL 5 min
  • Niveau 4 — Suppression Hiérarchique : FaceRecognized → pas de PersonDetected ; LicensePlateDetected → pas de VehicleDetected ; MotionDetected seul → silencieux
    3.4 CameraDetectionProfile : RequireMotionForAI=true, IoUThreshold=0.6, PersonCooldownSec=30, VehicleCooldownSec=60, AnimalCooldownSec=120, UnknownPersonCooldownSec=300, MaxEventsPerMinute=20

🔍 État actuel (vérifié dans le code)

CameraEventService.CreateAsync : création directe sans dedup. NotificationRateLimiter (max 20/min/camera/type, IMemoryCache) existe déjà — couvre partiellement le rate limit. KnownFace stocke des embeddings mais aucune comparaison L2 n'est faite. Pas de motion gate ni d'object tracker côté agent.

🧩 À faire

  • Agent : motion gate (diff pixels entre frames) + RequireMotionForAI
  • Agent : object tracker avec TrackId persistant (Guid) + IoU
  • Central : cooldown temporal (IMemoryCache, clé cameraId:objectType:trackId) → IsSuppressed
  • Central : embedding dedup (LRU, L2 < 0.6, TTL 5 min) — s'appuie sur KnownFace
  • Central : suppression hiérarchique (FaceRecognized/LicensePlateDetected/MotionDetected)
  • Respecter MaxEventsPerMinute dans la création d'événement
  • Tests critiques 12.2 (MotionGate, SpatialDedup, TemporalCooldown, EmbeddingDedup, HierarchicalSuppression)

🧪 Critères d'acceptation

  • Scénario spec 3.3 : personne immobile 5s → 1 seul événement (2 au lieu de 25)
  • Visage inconnu : 1 notification max / 5 min
  • MotionDetected seul → notification silencieuse

📁 Fichiers concernés

src/Heimdall.Agent/Services/MotionGateService.cs (nouveau), src/Heimdall.Agent/Services/ObjectTracker.cs (nouveau), src/Heimdall.Infrastructure/Services/CameraEventService.cs, src/Heimdall.Infrastructure/Services/EmbeddingDedupService.cs (nouveau)

## 🎯 Contexte Sans déduplication, un objet stationnaire génère des milliers d'événements/notifications (ex. 3000 snapshots pour 10 min). Aucun des 4 niveaux de la spec n'est implémenté. ## 📚 Référence spec (MASTER_SPECIFICATION.md v2.0) **Partie 3 — AI Pipeline et Dédoublonnage (3.2)** : - Niveau 0 — **Motion Gate** (Agent, pré-YOLO) : pixel diff > `MotionThreshold` (0.3) → YOLO ; sinon SKIP (économie 90% CPU) - Niveau 1 — **Spatial Dedup** (Agent, post-YOLO) : IoU(bbox, précédente) > 0.6 → même objet ; cooldown actif → pas d'envoi ; `DedupCooldownSec` écoulé → nouvel événement - Niveau 2 — **Temporal Cooldown** (Central) : `IMemoryCache` par `(cameraId, trackId, objectType)` ; clé présente → `IsSuppressed=true` ; cooldowns : personne 30s, véhicule 60s, inconnu 300s - Niveau 3 — **Embedding Dedup** (Central, visages) : cache LRU d'embeddings faciaux (float[512], 10K entries), distance L2 < 0.6 → même personne ; inconnu ajouté avec TTL 5 min - Niveau 4 — **Suppression Hiérarchique** : FaceRecognized → pas de PersonDetected ; LicensePlateDetected → pas de VehicleDetected ; MotionDetected seul → silencieux **3.4 CameraDetectionProfile** : `RequireMotionForAI=true`, `IoUThreshold=0.6`, `PersonCooldownSec=30`, `VehicleCooldownSec=60`, `AnimalCooldownSec=120`, `UnknownPersonCooldownSec=300`, `MaxEventsPerMinute=20` ## 🔍 État actuel (vérifié dans le code) `CameraEventService.CreateAsync` : création directe sans dedup. `NotificationRateLimiter` (max 20/min/camera/type, IMemoryCache) existe déjà — couvre partiellement le rate limit. `KnownFace` stocke des embeddings mais aucune comparaison L2 n'est faite. Pas de motion gate ni d'object tracker côté agent. ## 🧩 À faire - [ ] Agent : motion gate (diff pixels entre frames) + `RequireMotionForAI` - [ ] Agent : object tracker avec TrackId persistant (Guid) + IoU - [ ] Central : cooldown temporal (`IMemoryCache`, clé cameraId:objectType:trackId) → `IsSuppressed` - [ ] Central : embedding dedup (LRU, L2 < 0.6, TTL 5 min) — s'appuie sur `KnownFace` - [ ] Central : suppression hiérarchique (FaceRecognized/LicensePlateDetected/MotionDetected) - [ ] Respecter `MaxEventsPerMinute` dans la création d'événement - [ ] Tests critiques 12.2 (MotionGate, SpatialDedup, TemporalCooldown, EmbeddingDedup, HierarchicalSuppression) ## 🧪 Critères d'acceptation - [ ] Scénario spec 3.3 : personne immobile 5s → 1 seul événement (2 au lieu de 25) - [ ] Visage inconnu : 1 notification max / 5 min - [ ] MotionDetected seul → notification silencieuse ## 📁 Fichiers concernés src/Heimdall.Agent/Services/MotionGateService.cs (nouveau), src/Heimdall.Agent/Services/ObjectTracker.cs (nouveau), src/Heimdall.Infrastructure/Services/CameraEventService.cs, src/Heimdall.Infrastructure/Services/EmbeddingDedupService.cs (nouveau)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nonobis/Heimdall#5
No description provided.