feat(lidar): admit and benchmark GOOSE baseline

This commit is contained in:
DCCONSTRUCTIONS
2026-07-25 14:14:20 +03:00
parent 881e97312b
commit 951b40c870
20 changed files with 2871 additions and 241 deletions
+259 -1
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
import json
import zipfile
from pathlib import Path
import numpy as np
@@ -8,10 +10,18 @@ from fastapi import APIRouter
from fastapi.routing import APIRoute
from k1link.datasets import (
DatasetAdmissionError,
DatasetFrameError,
dataset_gateway_catalog,
goose_admission,
goose_benchmark,
read_dataset_admission_manifest,
read_dataset_ground_preview,
read_dataset_native_scan_preview,
read_semantic_kitti_frame,
)
from k1link.datasets.goose_admission import admit_goose_validation
from k1link.datasets.goose_benchmark import benchmark_goose_current_ground
from k1link.web.lidar_api import build_lidar_router
@@ -97,7 +107,7 @@ def test_dataset_gateway_api_is_read_only_and_path_free(
route = _endpoint(build_lidar_router(), "/api/v1/lidar/dataset-gateway")
response = route() # type: ignore[operator]
assert response["schema_version"] == "missioncore.dataset-gateway-catalog/v1"
assert response["schema_version"] == "missioncore.dataset-gateway-catalog/v2"
assert response["access"] == "read-only"
assert response["storage"]["admitted"] is True
assert response["storage"]["path_exposed"] is False
@@ -105,3 +115,251 @@ def test_dataset_gateway_api_is_read_only_and_path_free(
response["storage"]["required_wsl_root"], # type: ignore[index]
"",
)
def _downloading_manifest() -> dict[str, object]:
return {
"schema_version": "missioncore.dataset-admission/v1",
"source_id": "goose-3d/v2025-08-22",
"observed_at_utc": "2026-07-25T10:00:00Z",
"status": "downloading",
"storage": {
"policy": "worker-d-only",
"admitted": True,
"canonical_root": True,
"path_exposed": False,
},
"archive": {
"filename": "goose_3d_val.zip",
"source_url": "https://goose-dataset.de/storage/goose_3d_val.zip",
"bytes_transferred": 512,
"total_bytes": 1024,
"size_bytes": None,
"sha256": None,
"integrity": "pending",
"vendor_checksum_available": False,
},
"license": {
"spdx": "CC-BY-SA-4.0",
"artifact_present": False,
},
"frame": None,
"next_action": "complete-download-and-admit",
}
def test_dataset_gateway_uses_worker_manifest_instead_of_static_download_state(
tmp_path: Path,
) -> None:
manifest_path = tmp_path / "admission.json"
manifest_path.write_text(json.dumps(_downloading_manifest()), encoding="utf-8")
catalog = dataset_gateway_catalog(
tmp_path / "not-worker-d",
admission_manifest_path=manifest_path,
)
assert catalog["storage"]["admitted"] is True # type: ignore[index]
assert catalog["storage"]["attestation"] == "worker-manifest" # type: ignore[index]
source = catalog["sources"][0] # type: ignore[index]
assert source["admission"]["status"] == "downloading" # type: ignore[index]
assert source["admission"]["archive"]["bytes_transferred"] == 512 # type: ignore[index]
assert catalog["next_action"] == "complete-download-and-admit"
def test_dataset_admission_manifest_fails_closed_on_forged_storage(
tmp_path: Path,
) -> None:
manifest = _downloading_manifest()
manifest["storage"]["path_exposed"] = True # type: ignore[index]
path = tmp_path / "admission.json"
path.write_text(json.dumps(manifest), encoding="utf-8")
with pytest.raises(DatasetAdmissionError, match="storage admission"):
read_dataset_admission_manifest(path)
def test_goose_admission_extracts_one_aligned_frame_and_bounded_preview(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
root = tmp_path / "datasets"
archive = root / "goose-3d/v2025-08-22/archives/goose_3d_val.zip"
archive.parent.mkdir(parents=True)
points = np.asarray(
[
[1.0, 2.0, 3.0, 0.25],
[-4.0, 5.0, 0.5, 0.75],
[2.0, 1.0, -0.2, 0.5],
],
dtype="<f4",
)
packed_labels = np.asarray([23, 38, 31], dtype="<u4")
mapping = (
"class_name,label_key,has_instance,hex,challege_category_id,"
"challenge_category_name\n"
"asphalt,23,0,#ff2f80,2,artificial_ground\n"
"soil,31,0,#d16100,3,natural_ground\n"
"building,38,0,#013349,1,artificial_structures\n"
)
with zipfile.ZipFile(archive, "w") as target:
target.writestr(
"goose/velodyne/val/scene/2026_frame_vls128.bin",
points.tobytes(),
)
target.writestr(
"goose/labels/val/scene/2026_frame_goose.label",
packed_labels.tobytes(),
)
target.writestr("goose/goose_label_mapping.csv", mapping)
target.writestr(
"goose/LICENSE",
"Creative Commons Attribution-ShareAlike 4.0 International",
)
monkeypatch.setattr(goose_admission, "GOOSE_ARCHIVE_OBSERVED_BYTES", archive.stat().st_size)
monkeypatch.setattr(goose_admission, "_is_canonical_worker_root", lambda _: True)
manifest = admit_goose_validation(root, archive_path=archive, preview_points=2)
assert manifest["status"] == "frame-ready"
assert manifest["frame"]["frame_id"] == "2026_frame"
assert manifest["frame"]["point_count"] == 3
assert manifest["frame"]["ground_truth_ground_points"] == 2
state_path = root / "state/goose-3d-v2025-08-22.json"
assert read_dataset_admission_manifest(state_path)["status"] == "frame-ready"
digest = manifest["archive"]["sha256"]
preview_path = root / f"goose-3d/v2025-08-22/installs/{digest}/previews/2026_frame.json"
preview = read_dataset_native_scan_preview(preview_path)
assert preview["point_count"] == 2
assert len(preview["semantic_rgb_0_to_255"]) == 6
assert preview["safety"]["navigation_or_safety_accepted"] is False
def test_dataset_preview_api_is_read_only_and_integrity_checked(
tmp_path: Path,
) -> None:
preview_path = tmp_path / "preview.json"
preview_path.write_text(
json.dumps(
{
"schema_version": "missioncore.dataset-native-scan-preview/v1",
"source_id": "goose-3d/v2025-08-22",
"frame_id": "frame-1",
"representation": "native-scan",
"sampling": "deterministic-even-index",
"source_point_count": 1,
"point_count": 1,
"points_xyz_m": [[1.0, 2.0, 3.0]],
"remission_0_to_255": [128],
"semantic_label_ids": [23],
"semantic_rgb_0_to_255": [255, 47, 128],
"ground_truth_ground": [1],
"classes": [
{
"label_id": 23,
"class_name": "asphalt",
"hex": "#ff2f80",
"challenge_category_id": 2,
"challenge_category_name": "artificial_ground",
}
],
"safety": {
"visualization_only": True,
"navigation_or_safety_accepted": False,
},
}
),
encoding="utf-8",
)
router = build_lidar_router(
dataset_admission_provider=lambda: None,
dataset_preview_provider=lambda: preview_path,
)
route = _endpoint(router, "/api/v1/lidar/dataset-gateway/preview")
response = route() # type: ignore[operator]
assert response["frame_id"] == "frame-1"
def test_goose_current_ground_is_scored_against_independent_labels(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
root = tmp_path / "datasets"
digest = "a" * 64
frame_id = "frame-1"
install = root / f"goose-3d/v2025-08-22/installs/{digest}"
frame_root = install / f"frames/{frame_id}"
frame_root.mkdir(parents=True)
np.asarray(
[
[-1.0, 0.0, 0.00, 0.1],
[0.0, 0.0, 0.02, 0.2],
[1.0, 0.0, 0.01, 0.3],
[0.0, 0.2, 1.50, 0.4],
],
dtype="<f4",
).tofile(frame_root / "points.bin")
np.asarray([23, 31, 23, 38], dtype="<u4").tofile(frame_root / "labels.label")
(install / "goose_label_mapping.csv").write_text(
"class_name,label_key,has_instance,hex\n"
"asphalt,23,0,#ff2f80\n"
"soil,31,0,#d16100\n"
"building,38,0,#013349\n",
encoding="utf-8",
)
state = {
"schema_version": "missioncore.dataset-admission/v1",
"source_id": "goose-3d/v2025-08-22",
"observed_at_utc": "2026-07-25T10:00:00Z",
"status": "frame-ready",
"storage": {
"policy": "worker-d-only",
"admitted": True,
"canonical_root": True,
"path_exposed": False,
},
"archive": {
"filename": "goose_3d_val.zip",
"source_url": "https://goose-dataset.de/storage/goose_3d_val.zip",
"bytes_transferred": 1024,
"total_bytes": 1024,
"size_bytes": 1024,
"sha256": digest,
"integrity": "zip-structure-and-content-digest",
"vendor_checksum_available": False,
},
"license": {"spdx": "CC-BY-SA-4.0", "artifact_present": True},
"frame": {
"frame_id": frame_id,
"representation": "native-scan",
"point_count": 4,
"semantic_class_count": 3,
"ground_truth_ground_points": 3,
"ground_truth_ground_fraction": 0.75,
"preview_point_count": 4,
"preview_sha256": "b" * 64,
"preview_available": True,
},
"next_action": "review-first-native-scan",
}
state_path = root / "state/goose-3d-v2025-08-22.json"
state_path.parent.mkdir(parents=True)
state_path.write_text(json.dumps(state), encoding="utf-8")
monkeypatch.setattr(goose_benchmark, "_is_worker_dataset_root", lambda _: True)
report = benchmark_goose_current_ground(root, preview_points=4)
assert report["ground_truth"]["evaluated_points"] == 4
assert 0 <= report["metrics"]["ground_iou"] <= 1
assert report["decision"]["promoted"] is False
output = (
install
/ "benchmarks"
/ f"current-ground-{report['identity_sha256']}"
/ "preview.json"
)
preview = read_dataset_ground_preview(output)
assert preview["point_count"] == 4
assert len(preview["disagreement"]) == 4
+42
View File
@@ -157,6 +157,48 @@ def test_local_percentile_ground_is_point_aligned_and_non_mutating() -> None:
np.testing.assert_array_equal(xyzi, unchanged)
def test_local_percentile_grid_index_preserves_exact_radius_result() -> None:
random = np.random.default_rng(42)
xyzi = random.normal(size=(240, 4)).astype(np.float32)
xyzi[:, :2] *= 4
profile = GroundBenchmarkProfile(
current_cell_size_m=0.6,
current_local_radius_m=1.7,
current_lower_percentile=11,
current_maximum_below_ground_m=0.2,
current_maximum_above_ground_m=0.18,
current_minimum_local_points=5,
)
actual = LocalPercentileGroundSegmenter(profile=profile).segment(xyzi)
xyz = xyzi[:, :3].astype(np.float64)
cell_keys = np.floor(xyz[:, :2] / profile.current_cell_size_m).astype(np.int64)
unique_cells, inverse = np.unique(cell_keys, axis=0, return_inverse=True)
expected = np.zeros(xyzi.shape[0], dtype=np.bool_)
global_ground_z = float(np.percentile(xyz[:, 2], profile.current_lower_percentile))
for cell_index in range(unique_cells.shape[0]):
point_indices = np.flatnonzero(inverse == cell_index)
center_xy = np.median(xyz[point_indices, :2], axis=0)
delta_xy = xyz[:, :2] - center_xy
local = xyz[
np.einsum("ij,ij->i", delta_xy, delta_xy)
<= profile.current_local_radius_m**2,
2,
]
ground_z = (
float(np.percentile(local, profile.current_lower_percentile))
if local.size >= profile.current_minimum_local_points
else global_ground_z
)
z = xyz[point_indices, 2]
expected[point_indices] = (
z >= ground_z - profile.current_maximum_below_ground_m
) & (z <= ground_z + profile.current_maximum_above_ground_m)
np.testing.assert_array_equal(actual.ground_mask, expected)
def test_ground_benchmark_is_immutable_diagnostic_evidence(tmp_path: Path) -> None:
replay = _Replay()
output = build_lidar_ground_benchmark(