GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
62 lines
2.1 KiB
Python
62 lines
2.1 KiB
Python
"""Segmentation must honour the same post-processing configuration as detection.
|
|
|
|
Segmentation reuses the detection suppressor but passed only the IoU threshold,
|
|
so it silently fell back to the hardcoded containment constant while detection
|
|
read a configured one. A deployment tuning containment for a promoted model
|
|
changed detection behaviour and left segmentation on the old value.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from shapely.geometry import box
|
|
|
|
from app.core.config import Settings
|
|
|
|
|
|
def test_segmentation_has_its_own_containment_setting() -> None:
|
|
settings = Settings(_env_file=None)
|
|
|
|
assert settings.segmentation_containment_nms_threshold == pytest.approx(0.85)
|
|
|
|
|
|
def test_the_setting_is_independent_of_the_detection_one() -> None:
|
|
"""Masks and boxes overlap differently; one value need not fit both."""
|
|
|
|
settings = Settings(
|
|
_env_file=None,
|
|
yolo_containment_nms_threshold=0.7,
|
|
segmentation_containment_nms_threshold=0.95,
|
|
)
|
|
|
|
assert settings.yolo_containment_nms_threshold == pytest.approx(0.7)
|
|
assert settings.segmentation_containment_nms_threshold == pytest.approx(0.95)
|
|
|
|
|
|
def test_the_configured_value_reaches_the_suppressor() -> None:
|
|
from app.services.detection_service import DetectionService
|
|
|
|
def candidate(name: str, geometry, confidence: float):
|
|
return {
|
|
"class_name": "building",
|
|
"confidence": confidence,
|
|
"geometry": geometry,
|
|
"bbox": [0.0, 0.0, 1.0, 1.0],
|
|
"source_tile_path": f"/tiles/{name}.tif",
|
|
"properties": {"name": name},
|
|
}
|
|
|
|
outer = candidate("outer", box(0, 0, 10, 10), 0.9)
|
|
# Containment 0.9, IoU 0.09: only the containment rule can act on this pair.
|
|
mostly_nested = candidate("mostly", box(8.2, 1, 10.2, 6), 0.5)
|
|
|
|
strict = DetectionService._suppress_duplicate_candidates(
|
|
[outer, mostly_nested], iou_threshold=0.5, containment_threshold=0.95
|
|
)
|
|
loose = DetectionService._suppress_duplicate_candidates(
|
|
[outer, mostly_nested], iou_threshold=0.5, containment_threshold=0.7
|
|
)
|
|
|
|
assert len(strict) == 2
|
|
assert len(loose) == 1
|