feat(polygon): qualify GOOSE ground providers
This commit is contained in:
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user