Filter low variance YOLO negative tiles
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 22:57:59 +02:00
parent 2a3472b919
commit a159370a11
7 changed files with 306 additions and 2 deletions
@@ -5,6 +5,8 @@ from pathlib import Path
import subprocess
import sys
import numpy as np
ROOT = Path(__file__).resolve().parents[2]
@@ -39,6 +41,8 @@ def test_operator_yolo_tile_dataset_export_script_contract() -> None:
assert "stride" in script
assert "negative_keep_ratio" in script
assert "min_label_visible_ratio" in script
assert "drop_low_variance_negatives" in script
assert "skipped_low_variance_negative_tile_count" in script
assert "positive_tile_count" in script
assert "negative_tile_count" in script
assert "skipped_negative_tile_count" in script
@@ -67,6 +71,8 @@ def test_operator_yolo_tile_dataset_export_help_does_not_require_gis_dependencie
assert "--negative-keep-ratio" in result.stdout
assert "--min-label-visible-ratio" in result.stdout
assert "--background-negative-repeat" in result.stdout
assert "--drop-low-variance-negatives" in result.stdout
assert "--blank-range-threshold" in result.stdout
def test_iter_tile_windows_covers_edges_without_duplicates() -> None:
@@ -171,3 +177,86 @@ def test_background_category_is_derived_for_legacy_operator_manifests() -> None:
assert module.background_category_for_sample(pure_empty_sample) == "pure_empty_negative"
assert module.background_category_for_sample(sparse_context_sample) == "sparse_building_context"
assert module.background_category_for_sample(reference_sample) == "reference_aoi"
def test_export_can_skip_low_variance_negative_tiles(tmp_path: Path, monkeypatch) -> None:
module = load_tile_exporter()
raster_path = tmp_path / "sample.tif"
reference_path = tmp_path / "reference.geojson"
raster_path.write_bytes(b"fake-raster")
reference_path.write_text('{"type": "FeatureCollection", "features": []}', encoding="utf-8")
class FakeDataset:
width = 256
height = 128
crs = "EPSG:31370"
def __enter__(self):
return self
def __exit__(self, exc_type, exc, traceback):
return False
class FakeRasterio:
@staticmethod
def open(path):
assert Path(path) == raster_path
return FakeDataset()
class FakeImageObject:
def __init__(self, array):
self.array = array
def save(self, path):
Path(path).write_bytes(b"png")
class FakeImage:
@staticmethod
def fromarray(array):
return FakeImageObject(array)
def fake_image_array_from_raster_window(dataset, tile_window):
if tile_window.col_off == 0:
return np.full((128, 128, 3), 255, dtype=np.uint8)
image = np.zeros((128, 128, 3), dtype=np.uint8)
image[:, 64:, :] = 80
return image
monkeypatch.setattr(module, "rasterio", FakeRasterio)
monkeypatch.setattr(module, "Image", FakeImage)
monkeypatch.setattr(module, "load_reference_pixel_boxes", lambda reference_path, dataset, min_label_px: [])
monkeypatch.setattr(module, "image_array_from_raster_window", fake_image_array_from_raster_window)
records = module.export_sample_tiles(
sample={
"sample_slug": "blank_negative",
"sample_role": "background_candidate",
"background_category": "pure_empty_negative",
"raster_path": str(raster_path),
"reference_path": str(reference_path),
},
manifest_path=tmp_path / "operator_samples_manifest.json",
output_dir=tmp_path / "dataset",
val_slugs=set(),
tile_size=128,
stride=128,
negative_keep_ratio=1.0,
min_label_px=4,
min_label_visible_ratio=0.0,
background_negative_repeat=1,
drop_low_variance_negatives=True,
blank_range_threshold=3,
)
skipped = [record for record in records if not record["kept"]]
kept = [record for record in records if record["kept"]]
assert len(skipped) == 1
assert skipped[0]["skip_reason"] == "low_visual_variance_negative"
assert skipped[0]["low_visual_variance"] is True
assert skipped[0]["is_negative"] is True
assert skipped[0]["tile_index"] == 0
assert len(kept) == 1
assert kept[0]["tile_index"] == 1
assert kept[0]["low_visual_variance"] is False
assert Path(kept[0]["image_path"]).exists()