Add operator YOLO dataset quality audit
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 01:45:52 +02:00
parent 65808ac994
commit 5898e548c2
7 changed files with 620 additions and 0 deletions
@@ -0,0 +1,160 @@
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def test_operator_yolo_dataset_quality_audit_reports_dataset_risks(tmp_path: Path) -> None:
script_path = ROOT / "scripts" / "audit_operator_yolo_dataset_quality.py"
assert script_path.exists()
dataset_dir = tmp_path / "yolo-dataset"
labels_train = dataset_dir / "labels" / "train"
labels_val = dataset_dir / "labels" / "val"
labels_train.mkdir(parents=True)
labels_val.mkdir(parents=True)
(labels_train / "geel_000.txt").write_text(
"0 0.500000 0.500000 0.010000 0.010000\n"
"0 0.250000 0.250000 0.100000 0.100000\n",
encoding="utf-8",
)
(labels_train / "postel_bos_000.txt").write_text("", encoding="utf-8")
(labels_train / "postel_bos_000_hn01.txt").write_text("", encoding="utf-8")
(labels_val / "turnhout_000.txt").write_text(
"0 0.600000 0.600000 0.080000 0.080000\n",
encoding="utf-8",
)
summary_path = dataset_dir / "yolo_tile_dataset_summary.json"
summary_path.write_text(
json.dumps(
{
"status": "ready",
"dataset_yaml": str(dataset_dir / "dataset.yaml"),
"output_dir": str(dataset_dir),
"class_names": ["building"],
"tile_size": 160,
"stride": 80,
"negative_keep_ratio": 1.0,
"background_negative_repeat": 2,
"min_label_px": 2,
"source_sample_count": 3,
"tile_count": 4,
"positive_tile_count": 2,
"negative_tile_count": 2,
"skipped_negative_tile_count": 0,
"label_count": 3,
"train_tile_count": 3,
"val_tile_count": 1,
"tiles": [
{
"sample_slug": "geel",
"sample_role": "reference",
"split": "train",
"tile_index": 0,
"repeat_index": 0,
"kept": True,
"label_path": str(labels_train / "geel_000.txt"),
"label_count": 2,
"is_negative": False,
"is_repeated_background_negative": False,
},
{
"sample_slug": "postel_bos",
"sample_role": "background_candidate",
"split": "train",
"tile_index": 1,
"repeat_index": 0,
"kept": True,
"label_path": str(labels_train / "postel_bos_000.txt"),
"label_count": 0,
"is_negative": True,
"is_repeated_background_negative": False,
},
{
"sample_slug": "postel_bos",
"sample_role": "background_candidate",
"split": "train",
"tile_index": 1,
"repeat_index": 1,
"kept": True,
"label_path": str(labels_train / "postel_bos_000_hn01.txt"),
"label_count": 0,
"is_negative": True,
"is_repeated_background_negative": True,
},
{
"sample_slug": "turnhout",
"sample_role": "reference",
"split": "val",
"tile_index": 2,
"repeat_index": 0,
"kept": True,
"label_path": str(labels_val / "turnhout_000.txt"),
"label_count": 1,
"is_negative": False,
"is_repeated_background_negative": False,
},
],
}
),
encoding="utf-8",
)
output_dir = tmp_path / "audit"
result = subprocess.run(
[
sys.executable,
str(script_path),
"--summary-path",
str(summary_path),
"--output-dir",
str(output_dir),
"--min-positive-samples",
"3",
"--min-val-positive-samples",
"2",
"--max-repeated-negative-share",
"0.25",
"--min-median-box-area",
"0.02",
"--max-small-box-share",
"0.25",
],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
)
assert "Operator YOLO dataset quality audit passed" in result.stdout
report = json.loads(
(output_dir / "operator_yolo_dataset_quality_audit.json").read_text(encoding="utf-8")
)
assert report["status"] == "needs_attention"
assert report["sample_count"] == 3
assert report["positive_sample_count"] == 2
assert report["background_sample_count"] == 1
assert report["train_negative_tile_count"] == 2
assert report["repeated_background_negative_tile_count"] == 1
assert report["label_stats"]["parsed_label_count"] == 3
assert report["label_stats"]["invalid_label_count"] == 0
warning_codes = {warning["code"] for warning in report["warnings"]}
assert "positive_sample_count_below_gate" in warning_codes
assert "val_positive_sample_count_below_gate" in warning_codes
assert "repeated_background_negative_share_above_gate" in warning_codes
assert "median_box_area_below_gate" in warning_codes
assert "small_box_share_above_gate" in warning_codes
markdown = (output_dir / "operator_yolo_dataset_quality_audit.md").read_text(encoding="utf-8")
assert "Operator YOLO Dataset Quality Audit" in markdown
assert "Label Quality" in markdown
assert "positive_sample_count_below_gate" in markdown