Harden operator tile background metadata
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-07-11 15:52:45 +02:00
parent 5d24242fb4
commit b3bd34c384
4 changed files with 97 additions and 2 deletions
+31
View File
@@ -367,9 +367,40 @@ negative tiles, and records `yolo_tile_dataset_summary.json` with
`--min-label-visible-ratio` drops labels where only a small clipped fragment of
the original building bbox is visible inside the tile; this reduces noisy
tile-edge labels in overlapping-tile datasets. Use `0` for legacy behavior.
For legacy operator manifests that predate explicit `background_category`, the
exporter derives the same categories as the split-background evaluator:
background samples with `reference_feature_count == 0` become
`pure_empty_negative`, and background samples with one or more reference
features become `sparse_building_context`.
It remains operator tooling only: no provider fetch, no API mutation and no
automatic model training.
For the current AOI1024 baseline, prefer the stricter clean-label profile before
spending another training run:
```bash
docker exec -it geointel python3 /app/scripts/export_operator_yolo_tile_dataset.py \
--manifest-path /app/storage/operator-data/operator-samples-1024/operator_samples_manifest.json \
--output-dir /app/storage/operator-data/yolo-building-aoi1024-cleanpx12vis035 \
--tile-size 512 \
--stride 256 \
--negative-keep-ratio 1.0 \
--min-label-px 12 \
--min-label-visible-ratio 0.35 \
--val-samples turnhout,retie,westerlo,arendonk_heide \
--force
```
Then audit with stricter small-box gates:
```bash
docker exec -it geointel python3 /app/scripts/audit_operator_yolo_dataset_quality.py \
--summary-path /app/storage/operator-data/yolo-building-aoi1024-cleanpx12vis035/yolo_tile_dataset_summary.json \
--output-dir /app/artifacts/operator-yolo-dataset-audit/aoi1024-cleanpx12vis035 \
--max-small-box-share 0.25 \
--min-median-box-area 0.001
```
Audit the generated tile dataset before spending another long training run:
```bash
+16 -1
View File
@@ -21,6 +21,9 @@ from typing import Any, Iterable
DEFAULT_MANIFEST_PATH = Path("/app/storage/operator-data/operator_samples_manifest.json")
DEFAULT_OUTPUT_DIR = Path("/app/storage/operator-data/yolo-building-tile-dataset")
REFERENCE_AOI_CATEGORY = "reference_aoi"
PURE_EMPTY_BACKGROUND_CATEGORY = "pure_empty_negative"
SPARSE_BACKGROUND_CATEGORY = "sparse_building_context"
rasterio: Any = None
Window: Any = None
Transformer: Any = None
@@ -165,6 +168,18 @@ def background_negative_repeat_count(
return max(1, background_negative_repeat)
def background_category_for_sample(sample: dict[str, Any]) -> str:
explicit_category = str(sample.get("background_category") or "").strip()
if explicit_category:
return explicit_category
if str(sample.get("sample_role") or "reference") != "background_candidate":
return REFERENCE_AOI_CATEGORY
reference_feature_count = int(sample.get("reference_feature_count") or 0)
if reference_feature_count <= 0:
return PURE_EMPTY_BACKGROUND_CATEGORY
return SPARSE_BACKGROUND_CATEGORY
def resolve_manifest_path(raw: str, manifest_path: Path) -> Path:
path = Path(raw)
if path.exists():
@@ -315,7 +330,7 @@ def export_sample_tiles(
) -> list[dict[str, Any]]:
sample_slug = str(sample["sample_slug"])
sample_role = str(sample.get("sample_role") or "reference")
background_category = str(sample.get("background_category") or "reference_aoi")
background_category = background_category_for_sample(sample)
split = "val" if sample_slug.lower() in val_slugs else "train"
raster_path = resolve_manifest_path(str(sample["raster_path"]), manifest_path)
reference_path = resolve_manifest_path(str(sample["reference_path"]), manifest_path)