Files
geointel/backend/tests/test_detection_tile_seam_handling.py
T
Jens faeb58ef6d
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
Initial public release
2026-08-31 21:56:53 +02:00

126 lines
4.3 KiB
Python

"""Detections that straddle a tile seam must not become two half buildings.
Tiling uses a fixed overlap. An object wider than that overlap is truncated by
both tiles, so the two boxes barely intersect and plain IoU suppression keeps
them both: two false positives plus one missed footprint for every seam
building. The suppressor therefore also compares overlap against the smaller
box, and truncated boxes that sit against an interior tile edge are dropped in
favour of the neighbouring tile's complete view.
"""
from __future__ import annotations
from shapely.geometry import box
from app.services.detection_service import DetectionService
def _candidate(name: str, geometry, confidence: float, *, tile_index: int = 0, tile_bounds=None):
return {
"class_name": "building",
"confidence": confidence,
"geometry": geometry,
"bbox": [0.0, 0.0, 1.0, 1.0],
"source_tile_path": f"/tiles/tile_{tile_index:04d}.tif",
"properties": {"tile_index": tile_index, "name": name},
"tile_bounds": tile_bounds,
}
def test_identical_overlapping_predictions_are_still_suppressed() -> None:
kept = DetectionService._suppress_duplicate_candidates(
[
_candidate("a", box(0.0, 0.0, 1.0, 1.0), 0.7),
_candidate("b", box(0.02, 0.02, 1.02, 1.02), 0.9),
],
iou_threshold=0.5,
)
assert [item["properties"]["name"] for item in kept] == ["b"]
def test_a_box_contained_in_a_larger_one_is_suppressed() -> None:
"""A truncated seam half sits inside the complete box from the next tile."""
complete = box(0.0, 0.0, 10.0, 10.0)
truncated_half = box(0.0, 0.0, 4.0, 10.0) # IoU with ``complete`` is 0.4
kept = DetectionService._suppress_duplicate_candidates(
[
_candidate("complete", complete, 0.88),
_candidate("truncated", truncated_half, 0.61),
],
iou_threshold=0.5,
)
assert [item["properties"]["name"] for item in kept] == ["complete"]
def test_genuinely_adjacent_buildings_are_both_kept() -> None:
"""Terraced houses touch but do not contain one another."""
kept = DetectionService._suppress_duplicate_candidates(
[
_candidate("left", box(0.0, 0.0, 10.0, 10.0), 0.9),
_candidate("right", box(10.0, 0.0, 20.0, 10.0), 0.85),
],
iou_threshold=0.5,
)
assert sorted(item["properties"]["name"] for item in kept) == ["left", "right"]
def test_different_classes_are_never_merged() -> None:
first = _candidate("a", box(0.0, 0.0, 10.0, 10.0), 0.9)
second = _candidate("b", box(0.0, 0.0, 10.0, 10.0), 0.8)
second["class_name"] = "solar_panel"
kept = DetectionService._suppress_duplicate_candidates([first, second], iou_threshold=0.5)
assert len(kept) == 2
def test_boxes_clipped_by_an_interior_tile_edge_are_dropped() -> None:
"""The overlapping neighbour tile still sees the whole object."""
tile = box(0.0, 0.0, 10.0, 10.0)
raster = box(0.0, 0.0, 30.0, 10.0)
candidates = [
# Sits against the tile's right edge: truncated by the tile, not real.
_candidate("edge", box(9.0, 2.0, 10.0, 4.0), 0.8, tile_bounds=tile.bounds),
# Comfortably inside the tile.
_candidate("interior", box(2.0, 2.0, 4.0, 4.0), 0.8, tile_bounds=tile.bounds),
]
kept = DetectionService._drop_tile_edge_truncations(
candidates, raster_bounds=raster.bounds, tolerance=0.001
)
assert [item["properties"]["name"] for item in kept] == ["interior"]
def test_boxes_against_the_raster_edge_are_kept() -> None:
"""No neighbouring tile exists there, so the box is all the evidence there is."""
tile = box(0.0, 0.0, 10.0, 10.0)
raster = box(0.0, 0.0, 10.0, 10.0)
candidates = [_candidate("edge", box(9.0, 2.0, 10.0, 4.0), 0.8, tile_bounds=tile.bounds)]
kept = DetectionService._drop_tile_edge_truncations(
candidates, raster_bounds=raster.bounds, tolerance=0.001
)
assert [item["properties"]["name"] for item in kept] == ["edge"]
def test_edge_filter_keeps_candidates_without_tile_bounds() -> None:
candidates = [_candidate("unknown", box(2.0, 2.0, 4.0, 4.0), 0.8, tile_bounds=None)]
kept = DetectionService._drop_tile_edge_truncations(
candidates, raster_bounds=(0.0, 0.0, 30.0, 10.0), tolerance=0.001
)
assert len(kept) == 1