feat(polygon): qualify GOOSE ground providers

This commit is contained in:
DCCONSTRUCTIONS
2026-07-25 16:02:20 +03:00
parent 60ba64004b
commit d6decbe05c
14 changed files with 2718 additions and 22 deletions
+20
View File
@@ -9,6 +9,7 @@ from k1link.datasets.gateway import (
configured_dataset_ground_preview,
configured_dataset_preview,
dataset_gateway_catalog,
decode_semantic_kitti_frame,
read_dataset_admission_manifest,
read_dataset_ground_preview,
read_dataset_native_scan_preview,
@@ -27,6 +28,16 @@ from k1link.datasets.goose_profile import (
GOOSE_PATCHWORK_PROFILE_SCHEMA,
GoosePatchworkProfile,
)
from k1link.datasets.goose_qualification import (
DEFAULT_DEGRADATIONS,
GOOSE_QUALIFICATION_FRAME_SCHEMA,
GOOSE_QUALIFICATION_PREVIEW_SCHEMA,
GOOSE_QUALIFICATION_PROFILE_SCHEMA,
GOOSE_QUALIFICATION_REPORT_SCHEMA,
DegradationProfile,
GroundAcceptancePolicy,
qualify_goose_ground,
)
__all__ = [
"DATASET_GATEWAY_CATALOG_SCHEMA",
@@ -39,15 +50,24 @@ __all__ = [
"GOOSE_GROUND_BENCHMARK_SCHEMA",
"GOOSE_GROUND_PREVIEW_SCHEMA",
"GOOSE_PATCHWORK_PROFILE_SCHEMA",
"GOOSE_QUALIFICATION_FRAME_SCHEMA",
"GOOSE_QUALIFICATION_PREVIEW_SCHEMA",
"GOOSE_QUALIFICATION_PROFILE_SCHEMA",
"GOOSE_QUALIFICATION_REPORT_SCHEMA",
"GoosePatchworkProfile",
"GroundAcceptancePolicy",
"DegradationProfile",
"DEFAULT_DEGRADATIONS",
"benchmark_goose_current_ground",
"benchmark_goose_patchwork_ground",
"configured_dataset_admission_manifest",
"configured_dataset_ground_preview",
"configured_dataset_preview",
"dataset_gateway_catalog",
"decode_semantic_kitti_frame",
"read_dataset_admission_manifest",
"read_dataset_ground_preview",
"read_dataset_native_scan_preview",
"read_semantic_kitti_frame",
"qualify_goose_ground",
]
+35
View File
@@ -13,6 +13,7 @@ from k1link.datasets.goose_benchmark import (
benchmark_goose_current_ground,
benchmark_goose_patchwork_ground,
)
from k1link.datasets.goose_qualification import qualify_goose_ground
app = typer.Typer(
add_completion=False,
@@ -83,5 +84,39 @@ def benchmark_goose_patchwork_ground_command(
typer.echo(json.dumps(report, ensure_ascii=False, sort_keys=True))
@app.command("qualify-goose-ground")
def qualify_goose_ground_command(
dataset_root: Annotated[
Path,
typer.Option("--dataset-root", exists=True, file_okay=False, resolve_path=True),
],
runs_root: Annotated[
Path,
typer.Option("--runs-root", file_okay=False, resolve_path=True),
],
mission_core_commit: Annotated[
str,
typer.Option("--mission-core-commit", min=7, max=64),
],
workers: Annotated[
int,
typer.Option("--workers", min=1, max=32),
] = 8,
) -> None:
"""Run the immutable GOOSE validation-split ground qualification."""
try:
report = qualify_goose_ground(
dataset_root,
runs_root,
mission_core_commit=mission_core_commit,
parallel_workers=workers,
)
except (GooseAdmissionError, ValueError) as exc:
typer.echo(str(exc), err=True)
raise typer.Exit(code=2) from exc
typer.echo(json.dumps(report, ensure_ascii=False, sort_keys=True))
if __name__ == "__main__":
app()
+54 -14
View File
@@ -67,27 +67,45 @@ def read_semantic_kitti_frame(
) -> DatasetPointFrame:
"""Read one GOOSE/SemanticKITTI XYZI + packed-label frame losslessly."""
if maximum_points <= 0:
raise DatasetFrameError("maximum_points must be positive")
try:
point_size = point_path.stat().st_size
label_size = label_path.stat().st_size
_validate_semantic_kitti_sizes(
point_size,
label_size,
maximum_points=maximum_points,
)
point_bytes = point_path.read_bytes()
label_bytes = label_path.read_bytes()
except OSError as exc:
raise DatasetFrameError("dataset point or label file is unavailable") from exc
if point_size == 0 or point_size % 16:
raise DatasetFrameError("point frame must contain little-endian float32 XYZI tuples")
if label_size % 4:
raise DatasetFrameError("label frame must contain packed little-endian uint32 values")
point_count = point_size // 16
if point_count > maximum_points:
raise DatasetFrameError("dataset point frame exceeds the configured safety limit")
if label_size // 4 != point_count:
raise DatasetFrameError("point and label counts do not match")
return decode_semantic_kitti_frame(
point_bytes,
label_bytes,
maximum_points=maximum_points,
)
def decode_semantic_kitti_frame(
point_bytes: bytes,
label_bytes: bytes,
*,
maximum_points: int = 2_000_000,
) -> DatasetPointFrame:
"""Decode one in-memory SemanticKITTI frame without extracting an archive."""
point_size = len(point_bytes)
label_size = len(label_bytes)
point_count = _validate_semantic_kitti_sizes(
point_size,
label_size,
maximum_points=maximum_points,
)
try:
xyzi = np.fromfile(point_path, dtype="<f4").reshape((-1, 4))
packed_labels = np.fromfile(label_path, dtype="<u4")
except (OSError, ValueError) as exc:
xyzi = np.frombuffer(point_bytes, dtype="<f4").reshape((-1, 4))
packed_labels = np.frombuffer(label_bytes, dtype="<u4")
except ValueError as exc:
raise DatasetFrameError("dataset frame cannot be decoded") from exc
if not np.isfinite(xyzi).all():
raise DatasetFrameError("dataset point frame contains non-finite values")
@@ -98,9 +116,31 @@ def read_semantic_kitti_frame(
instance = np.ascontiguousarray(packed_labels >> np.uint32(16), dtype=np.uint16)
for values in (points, remission, semantic, instance):
values.setflags(write=False)
if points.shape[0] != point_count:
raise DatasetFrameError("decoded point count differs from the admitted size")
return DatasetPointFrame(points, remission, semantic, instance)
def _validate_semantic_kitti_sizes(
point_size: int,
label_size: int,
*,
maximum_points: int,
) -> int:
if maximum_points <= 0:
raise DatasetFrameError("maximum_points must be positive")
if point_size == 0 or point_size % 16:
raise DatasetFrameError("point frame must contain little-endian float32 XYZI tuples")
if label_size % 4:
raise DatasetFrameError("label frame must contain packed little-endian uint32 values")
point_count = point_size // 16
if point_count > maximum_points:
raise DatasetFrameError("dataset point frame exceeds the configured safety limit")
if label_size // 4 != point_count:
raise DatasetFrameError("point and label counts do not match")
return point_count
def _configured_dataset_root() -> Path | None:
raw = os.environ.get(DATASET_ROOT_ENV, "").strip()
return Path(raw).expanduser().absolute() if raw else None
File diff suppressed because it is too large Load Diff