Sequence matching can steal a fire's detections after a merged giant bbox (first-match-wins)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 29
- Forks
- 14
- Avg merge
- 9d 1h
- Merged PRs (30d)
- 5
Description
Summary
When two fires are visible on the same camera pose, a single oversized bbox (the model merging a plume into one giant box) can get attached to the wrong sequence. From that point on, the wrong sequence tracks the other fire's detections and the correct sequence starves. Both alerts then end up showing the same fire, while their triangulated positions still point to two different locations.
Observed in production on 2026-07-29, camera croix-augas-02 (id 41), pose 79, alerts 51205 and 51185.
Production timeline
Two sequences ran correctly in parallel on the same pose:
| Sequence | Alert | Fire | Started | bbox center x | Azimuth |
|---|---|---|---|---|---|
| 56126 | 51205 | A | 11:27 | ~0.31 | 224.6° |
| 56161 | 51185 | B | 14:03 | ~0.53 | 238.1° |
- 14:42 fire A fades (conf drops to 0.21 then 0.00). Last detection of 56126 at 14:42:52, last detection of 56161 at 14:42:51.
- 14:42:52 to 14:46:51 no detections at all (4 min gap).
- 14:46:51 detection id 2596275 arrives with a giant bbox
(0.348, 0.070, 0.684, 0.524)covering fire B's whole plume. It is attached to 56126 (fire A's sequence). - 14:46 to 17:21 every fire B bbox (center x ≈ 0.55) lands in 56126. Sequence 56161 never receives anything again (
last_seen_atstuck at 14:42:51).
Result: alert 51205, triangulated with fire A's azimuth (224.6°), displays fire B's images for 2.5 hours.
Root cause
Three weaknesses in the spatial matching combine (create_detection in src/app/api/api_v1/endpoints/detections.py):
- Giant boxes match everything.
_bboxes_overlapmeasures an edge gap with a 0.05 tolerance per axis, and edge contact counts as a match. The giant box had an x-gap of 0.030 with fire A's last box(0.308, 0.524, 0.318, 0.538)and touched it in y at exactly 0.524. The bigger the box, the more sequences it matches. - First match wins, tie-broken by seconds. Candidates are ordered by
last_seen_at DESCand the loop takes the first overlap. 56126 had been seen at 14:42:52 vs 14:42:51 for 56161: one second decided the attachment. The giant box overlapped 56161 at ~100% IoU versus a mere edge contact with 56126, but match quality is never compared. - A sequence's identity is its last bbox only. Once the giant box was in 56126, its anchor moved onto fire B. Every subsequent fire B box then matched 56126 first (always the freshest), so the steal is irreversible and 56161 starved.
Proposed fix
- Best match instead of first match: among candidate sequences within tolerance, pick the highest IoU (or the closest bbox center). This alone would have attached the giant box to 56161.
- Optionally, guard against anchor jumps: reject a match when the new box area is N times the sequence's last real box area, or require actual overlap (not tolerated edge contact) when several sequences are candidates.
- Optionally, add inertia: compare against the median center of the last K real bboxes instead of the single last one, so one aberrant box cannot re-anchor a sequence.
Notes
- Should build on top of #624: matching must compare against the last real bbox (
get_latest_with_bbox), ignoring continuity rows. - The production data above (sequences 56126 and 56161, detections around 14:46:51) can be replayed as a regression test scenario.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/app/api/api_v1/endpoints/detections.py, especially create_detection and _bboxes_overlap, then inspect how get_latest_with_bbox from #624 is used. Replay the production scenario around detection 2596275 and add a regression test covering competing sequences, candidate match quality, and the expected sequence assignment.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fastapi, postgresql, python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100