Suppress duplicate YOLO tile detections
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-07-09 10:55:45 +02:00
parent 638534f011
commit 3f5ca4c85e
24 changed files with 266 additions and 39 deletions
+13
View File
@@ -110,6 +110,7 @@ Environment variables:
- `YOLO_IMAGE_SIZE`
- `YOLO_MAX_TILES`
- `YOLO_MAX_DETECTIONS`
- `YOLO_DUPLICATE_IOU_THRESHOLD`
- `YOLO_BATCH_SIZE`
`YOLO_MAX_DETECTIONS` is forwarded to Ultralytics as `max_det` for each
@@ -119,6 +120,15 @@ the upstream default would cap recall before QA/QC begins. Operators may lower
the value for small rasters or raise it for dense urban tiles after reviewing
runtime and false-positive behavior.
After YOLO boxes are georeferenced, configured-YOLO runs apply a GeoIntel
cross-tile duplicate suppression pass before persistence. Candidates are grouped
by canonical class and sorted by confidence; lower-confidence same-class
candidates with EPSG:4326 geometry IoU greater than or equal to
`YOLO_DUPLICATE_IOU_THRESHOLD` are suppressed. The default is `0.5`; set it to
`0` to disable this post-processing for operator debugging. Run summaries record
raw, persisted and suppressed detection counts so calibration evidence remains
auditable.
### Local model asset catalog
GeoIntel can list local runtime model files mounted into the backend model
@@ -205,6 +215,9 @@ persisted project quality-check list to build `calibration_summary.json`.
Results are honest QA/QC evidence from persisted detections and persisted
reference `vector_features`; no demo detections, live provider fetches or model
downloads are introduced by the calibration tool.
Summaries include raw detection candidate count, persisted detection count and
suppressed duplicate count so operators can distinguish model output volume from
GeoIntel post-processing.
For model/tile/threshold selection, use the quality matrix wrapper:
+6
View File
@@ -785,6 +785,12 @@ Validation errors:
- Configured YOLO inference forwards `YOLO_MAX_DETECTIONS` to Ultralytics
`max_det` and defaults to `1000` so dense building AOIs are not silently
limited by the upstream default of 300 detections before persisted QA/QC.
- Configured YOLO applies cross-tile duplicate suppression after pixel boxes are
converted to EPSG:4326 geometries and before `Detection` rows are persisted.
Same-class candidates are confidence-sorted and lower-confidence candidates
with geometry IoU greater than or equal to
`YOLO_DUPLICATE_IOU_THRESHOLD` are suppressed. The default is `0.5`; `0`
disables this GeoIntel-side post-processing for debugging.
- `DETECTION_DEPENDENCY_UNAVAILABLE` when YOLO dependencies are not installed.
- `DETECTION_MODEL_LOAD_FAILED` when the local model file exists but cannot be loaded.
+32
View File
@@ -1,3 +1,35 @@
## Sprint 149 YOLO duplicate suppression evidence (2026-07-09)
Changed:
- Added configured-YOLO cross-tile duplicate suppression after pixel boxes are converted to EPSG:4326 geometries and before `Detection` rows are persisted.
- Added backend setting `YOLO_DUPLICATE_IOU_THRESHOLD` with default `0.5`; `0` disables the GeoIntel-side pass for debugging.
- Detection run `result_json` now records:
- `raw_detection_count`
- `suppressed_detection_count`
- `duplicate_iou_threshold`
- Updated `.env.example`, Docker Compose, Unraid env examples and the Dockerman run script.
- Updated calibration and quality matrix scripts to fetch detection run details and include raw/suppressed counts in per-run and aggregate summaries.
- Updated backend/API/AI/pipeline documentation.
Why:
- Dense overlapping tile inference can produce duplicate candidate buildings, which inflates persisted false positives before QA/QC.
- The previous Sprint 148 cap fix allowed dense AOIs to persist more candidates, but made duplicate pressure more visible.
- This pass keeps all outputs honest: no model activation, no fake detections and no migration. It only removes lower-confidence same-class geometric duplicates before persistence.
Tested:
- Red step: `python -m pytest backend\tests\test_sprint8b_yolo_foundation.py::test_yolo_run_suppresses_cross_tile_duplicate_detections -q` failed with `detection_count == 2`.
- `python -m pytest backend\tests\test_sprint8b_yolo_foundation.py::test_yolo_run_suppresses_cross_tile_duplicate_detections -q` (`1 passed`)
- Red step: Docker runtime config tests failed before `.env.example` and Unraid runner exposed `YOLO_DUPLICATE_IOU_THRESHOLD`.
- `python -m pytest backend\tests\test_sprint8b_yolo_foundation.py backend\tests\test_docker_runtime_config.py::test_env_example_uses_runtime_env_names_read_by_backend_and_frontend backend\tests\test_docker_runtime_config.py::test_unraid_deploy_passes_ai_build_arg_and_yolo_runtime_env -q` (`17 passed`)
- Red step: calibration/matrix script tests failed before detection-run raw/suppressed metadata was included.
- `python -m pytest backend\tests\test_sprint124_detection_calibration_sweep.py backend\tests\test_sprint126_detection_quality_matrix.py backend\tests\test_sprint127_operator_sample_quality_matrix.py::test_multi_sample_detection_quality_matrix_runs_existing_matrix_for_each_sample -q` (`3 passed`)
- `bash -n scripts/run_detection_calibration_sweep.sh`
- `bash -n scripts/run_detection_quality_matrix.sh`
- `bash -n scripts/run_multi_sample_detection_quality_matrix.sh`
Next:
- Run full readiness, then deploy Tower and rerun the dense Westerlo/Turnhout sweeps with duplicate suppression enabled to quantify quality impact versus the Sprint 148 uncapped baseline.
## Sprint 148 YOLO max-detection cap hardening (2026-07-09)
Changed:
+3
View File
@@ -110,6 +110,9 @@ Default method:
- calculate IoU between overlapping geospatial bboxes
- apply non-maximum suppression by confidence
- default IoU merge threshold: 0.5
- implemented for configured-YOLO as EPSG:4326 cross-tile duplicate suppression
before `Detection` persistence; `YOLO_DUPLICATE_IOU_THRESHOLD=0` disables the
GeoIntel-side pass for debugging.
## Step 6 — Storage
+1
View File
@@ -22,6 +22,7 @@ YOLO_MODEL_PATH=
YOLO_MODEL_VERSION=
YOLO_MAX_TILES=100
YOLO_MAX_DETECTIONS=1000
YOLO_DUPLICATE_IOU_THRESHOLD=0.5
ENABLE_GRB_WFS=false
GRB_WFS_URL=
OSM_OVERPASS_URL=https://overpass-api.de/api/interpreter
+2 -1
View File
@@ -112,10 +112,11 @@ This file now starts with the current implementation status. Older preparation/b
- [x] Train and gate an AOI-scale `aoi512e80` YOLOv8s candidate to test the 160px training-scale hypothesis.
- [x] Raise configured-YOLO `max_det` through `YOLO_MAX_DETECTIONS` so dense AOIs are not capped at 300 detections before QA/QC.
- [x] Rerun live dense-AOI calibration after redeploy with `YOLO_MAX_DETECTIONS=1000`; Westerlo reached 523/1000 detections at lower thresholds and Turnhout reached 822/1000, confirming the old 300 cap is removed.
- [x] Add configured-YOLO cross-tile duplicate suppression and raw/suppressed calibration evidence fields.
- [ ] Find or train a materially stronger aerial/Kempen building model candidate; `geointel-building-yolov8n-expanded160e50-pt` is the best current dense-AOI candidate but still too weak and too noisy for a V1 default.
- [ ] Train a higher-capacity local aerial-building detector with stronger positive recall while preserving the hard-negative false-positive gate.
- [ ] Add more diverse positive AOIs and revisit geometry-to-box label strategy before the next default-model training attempt.
- [ ] Add operator-side duplicate suppression/post-processing analysis for dense overlapping tile detections before the next promotion gate.
- [ ] Rerun live dense-AOI calibration after redeploy with `YOLO_DUPLICATE_IOU_THRESHOLD=0.5` to measure post-processing impact versus the Sprint 148 uncapped baseline.
## Sprint 8 status