feat(perception): add PointPillars transfer gate

This commit is contained in:
DCCONSTRUCTIONS
2026-07-31 10:48:27 +03:00
parent 29fee5c0ac
commit a9a5ca5a26
23 changed files with 5407 additions and 0 deletions
@@ -0,0 +1,566 @@
"""KITTI-to-LiDAR truth conversion and bounded PointPillars metrics."""
from __future__ import annotations
import math
import zipfile
from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Final
import numpy as np
from k1link.compute.pointpillars_postprocess import (
POINTPILLARS_MODEL_POINT_CLOUD_RANGE,
PointPillarsBox,
oriented_3d_iou,
oriented_bev_iou,
)
KITTI_BENCHMARK_CLASSES: Final = ("Car", "Pedestrian", "Cyclist")
MODEL_TO_KITTI_CLASS: Final = {
"Vehicle": "Car",
"Pedestrian": "Pedestrian",
"Cyclist": "Cyclist",
}
KITTI_IOU_THRESHOLDS: Final = {
"Car": 0.7,
"Pedestrian": 0.5,
"Cyclist": 0.5,
}
KITTI_REFERENCE_POINT_CLOUD_RANGE: Final = (
0.0,
-39.68,
-3.0,
69.12,
39.68,
1.0,
)
CROSS_DOMAIN_EVALUATION_RANGE: Final = tuple(
max(
KITTI_REFERENCE_POINT_CLOUD_RANGE[index],
POINTPILLARS_MODEL_POINT_CLOUD_RANGE[index],
)
if index < 3
else min(
KITTI_REFERENCE_POINT_CLOUD_RANGE[index],
POINTPILLARS_MODEL_POINT_CLOUD_RANGE[index],
)
for index in range(6)
)
DISTANCE_BUCKETS_M: Final = ((0.0, 20.0), (20.0, 40.0), (40.0, 70.0))
class KittiPointPillarsBenchmarkError(RuntimeError):
"""KITTI truth or PointPillars predictions violate the frozen contract."""
@dataclass(frozen=True, slots=True)
class KittiLidarTruth:
frame_id: str
benchmark_class: str
x_m: float
y_m: float
z_m: float
length_m: float
width_m: float
height_m: float
yaw_rad: float
@dataclass(frozen=True, slots=True)
class PointPillarsFramePrediction:
frame_id: str
boxes: tuple[PointPillarsBox, ...]
inference_ms: float
@dataclass(frozen=True, slots=True)
class _ScoredPrediction:
frame_id: str
index: int
benchmark_class: str
box: PointPillarsBox
@dataclass(frozen=True, slots=True)
class _MetricEvaluation:
average_precision_40: float
precision: float
recall: float
true_positives: int
false_positives: int
ground_truth_count: int
matched_truth: frozenset[tuple[str, int]]
matched_pairs: tuple[tuple[PointPillarsBox, KittiLidarTruth], ...]
def read_kitti_validation_truth(
*,
labels_archive: Path,
calibrations_archive: Path,
validation_frame_ids: tuple[str, ...],
) -> dict[str, tuple[KittiLidarTruth, ...]]:
"""Read target cuboids and convert camera-bottom centers into LiDAR centers."""
if (
not validation_frame_ids
or len(set(validation_frame_ids)) != len(validation_frame_ids)
or any(len(frame_id) != 6 or not frame_id.isdigit() for frame_id in validation_frame_ids)
):
raise KittiPointPillarsBenchmarkError("KITTI validation frame index is invalid")
try:
with (
zipfile.ZipFile(labels_archive.resolve(strict=True)) as labels_zip,
zipfile.ZipFile(calibrations_archive.resolve(strict=True)) as calib_zip,
):
truth = {
frame_id: _read_frame_truth(labels_zip, calib_zip, frame_id)
for frame_id in validation_frame_ids
}
except (OSError, KeyError, UnicodeDecodeError, zipfile.BadZipFile) as exc:
raise KittiPointPillarsBenchmarkError(
"KITTI validation truth could not be read"
) from exc
if any(
not any(
box.benchmark_class == class_name
for boxes in truth.values()
for box in boxes
)
for class_name in KITTI_BENCHMARK_CLASSES
):
raise KittiPointPillarsBenchmarkError("KITTI validation lacks a target class")
return truth
def evaluate_pointpillars_predictions(
*,
truth_by_frame: dict[str, tuple[KittiLidarTruth, ...]],
predictions: tuple[PointPillarsFramePrediction, ...],
) -> dict[str, Any]:
"""Evaluate the frozen model-to-KITTI mapping without retuning."""
if not truth_by_frame or set(truth_by_frame) != {
prediction.frame_id for prediction in predictions
}:
raise KittiPointPillarsBenchmarkError(
"prediction frames do not equal the admitted validation split"
)
if len(predictions) != len(truth_by_frame):
raise KittiPointPillarsBenchmarkError("prediction frames contain duplicates")
if any(
not math.isfinite(prediction.inference_ms) or prediction.inference_ms <= 0.0
for prediction in predictions
):
raise KittiPointPillarsBenchmarkError("inference latency is invalid")
all_boxes = tuple(
box
for prediction in predictions
for box in prediction.boxes
)
evaluated_boxes = tuple(
box for box in all_boxes if _center_in_evaluation_range(box.x_m, box.y_m, box.z_m)
)
scored = tuple(
_ScoredPrediction(
frame_id=prediction.frame_id,
index=index,
benchmark_class=_benchmark_class(box),
box=box,
)
for prediction in predictions
for index, box in enumerate(prediction.boxes)
if _center_in_evaluation_range(box.x_m, box.y_m, box.z_m)
)
per_class: dict[str, dict[str, Any]] = {}
three_d_evaluations: dict[str, _MetricEvaluation] = {}
for class_name in KITTI_BENCHMARK_CLASSES:
bev = _evaluate_metric(
class_name=class_name,
truth_by_frame=truth_by_frame,
predictions=scored,
iou=oriented_bev_iou,
)
three_d = _evaluate_metric(
class_name=class_name,
truth_by_frame=truth_by_frame,
predictions=scored,
iou=oriented_3d_iou,
)
three_d_evaluations[class_name] = three_d
per_class[class_name] = {
"iou_threshold": KITTI_IOU_THRESHOLDS[class_name],
"bev_ap40": bev.average_precision_40,
"3d_ap40": three_d.average_precision_40,
"precision": three_d.precision,
"recall": three_d.recall,
"true_positives": three_d.true_positives,
"false_positives": three_d.false_positives,
"ground_truth_count": three_d.ground_truth_count,
}
all_three_d_pairs = tuple(
pair
for class_name in KITTI_BENCHMARK_CLASSES
for pair in three_d_evaluations[class_name].matched_pairs
)
errors = _matched_errors(all_three_d_pairs)
total_predictions = sum(
evaluation.true_positives + evaluation.false_positives
for evaluation in three_d_evaluations.values()
)
total_false_positives = sum(
evaluation.false_positives for evaluation in three_d_evaluations.values()
)
latencies = np.asarray(
[prediction.inference_ms for prediction in predictions],
dtype=np.float64,
)
return {
"metric_contract": {
"official_kitti_server_metric": False,
"evaluation_kind": "public-cross-domain-transfer-probe",
"ap_interpolation": "40-point",
"difficulty_filtering": False,
"retuning_on_validation": False,
"model_to_benchmark_class_mapping": MODEL_TO_KITTI_CLASS,
"iou_thresholds": KITTI_IOU_THRESHOLDS,
"model_training_domain": "proprietary-solid-state-lidar",
"model_point_cloud_range": list(
POINTPILLARS_MODEL_POINT_CLOUD_RANGE
),
"dataset_reference_point_cloud_range": list(
KITTI_REFERENCE_POINT_CLOUD_RANGE
),
"shared_evaluation_range": list(CROSS_DOMAIN_EVALUATION_RANGE),
"predictions_outside_shared_range_ignored": True,
},
"frame_count": len(predictions),
"per_class": per_class,
"aggregates": {
"bev_map40": _mean(
[per_class[class_name]["bev_ap40"] for class_name in KITTI_BENCHMARK_CLASSES]
),
"3d_map40": _mean(
[per_class[class_name]["3d_ap40"] for class_name in KITTI_BENCHMARK_CLASSES]
),
"false_occupied_rate": (
total_false_positives / total_predictions
if total_predictions
else 0.0
),
"prediction_volume": {
"model_output_box_count": len(all_boxes),
"evaluated_box_count": len(evaluated_boxes),
"outside_shared_range_count": len(all_boxes)
- len(evaluated_boxes),
},
**errors,
"inference_latency_ms": {
"mean": float(np.mean(latencies)),
"p50": float(np.percentile(latencies, 50)),
"p95": float(np.percentile(latencies, 95)),
"maximum": float(np.max(latencies)),
},
"distance_bucket_recall": _distance_bucket_recall(
truth_by_frame,
three_d_evaluations,
),
},
"claim_boundary": {
"public_cross_domain_transfer_probe": True,
"native_model_accuracy_evaluated": False,
"k1_transfer_evaluated": False,
"navigation_or_safety_accepted": False,
"camera_first_candidate_replaced": False,
},
}
def _read_frame_truth(
labels_zip: zipfile.ZipFile,
calib_zip: zipfile.ZipFile,
frame_id: str,
) -> tuple[KittiLidarTruth, ...]:
calibration = _calibration_transform(
calib_zip.read(f"training/calib/{frame_id}.txt").decode("ascii")
)
labels = labels_zip.read(f"training/label_2/{frame_id}.txt").decode("ascii")
result: list[KittiLidarTruth] = []
for line in labels.splitlines():
fields = line.split()
if not fields or fields[0] not in KITTI_BENCHMARK_CLASSES:
continue
if len(fields) != 15:
raise KittiPointPillarsBenchmarkError("KITTI label row is invalid")
try:
values = np.asarray([float(value) for value in fields[1:]], dtype=np.float64)
except ValueError as exc:
raise KittiPointPillarsBenchmarkError(
"KITTI label row contains invalid numbers"
) from exc
if not np.isfinite(values).all():
raise KittiPointPillarsBenchmarkError(
"KITTI label row contains non-finite numbers"
)
height_m, width_m, length_m = (float(value) for value in values[7:10])
if height_m <= 0.0 or width_m <= 0.0 or length_m <= 0.0:
raise KittiPointPillarsBenchmarkError("KITTI cuboid dimensions are invalid")
camera_bottom_center = np.asarray(
[values[10], values[11], values[12], 1.0],
dtype=np.float64,
)
lidar_bottom_center = calibration @ camera_bottom_center
x_m, y_m, z_m = (float(value) for value in lidar_bottom_center[:3])
z_m += height_m / 2.0
if not _center_in_evaluation_range(x_m, y_m, z_m):
continue
result.append(
KittiLidarTruth(
frame_id=frame_id,
benchmark_class=fields[0],
x_m=x_m,
y_m=y_m,
z_m=z_m,
length_m=length_m,
width_m=width_m,
height_m=height_m,
yaw_rad=_wrap_angle(-(float(values[13]) + math.pi / 2.0)),
)
)
return tuple(result)
def _calibration_transform(payload: str) -> np.ndarray:
values: dict[str, np.ndarray] = {}
for line in payload.splitlines():
if not line.strip():
continue
key, separator, raw = line.partition(":")
if not separator:
raise KittiPointPillarsBenchmarkError("KITTI calibration row is invalid")
try:
row = np.asarray([float(value) for value in raw.split()], dtype=np.float64)
except ValueError as exc:
raise KittiPointPillarsBenchmarkError(
"KITTI calibration contains invalid numbers"
) from exc
if not np.isfinite(row).all():
raise KittiPointPillarsBenchmarkError(
"KITTI calibration contains non-finite numbers"
)
values[key] = row
if "R0_rect" not in values or values["R0_rect"].size != 9:
raise KittiPointPillarsBenchmarkError("KITTI R0_rect is invalid")
if "Tr_velo_to_cam" not in values or values["Tr_velo_to_cam"].size != 12:
raise KittiPointPillarsBenchmarkError("KITTI Tr_velo_to_cam is invalid")
rectification = np.eye(4, dtype=np.float64)
rectification[:3, :3] = values["R0_rect"].reshape(3, 3)
lidar_to_camera = np.eye(4, dtype=np.float64)
lidar_to_camera[:3, :4] = values["Tr_velo_to_cam"].reshape(3, 4)
try:
return np.linalg.inv(rectification @ lidar_to_camera)
except np.linalg.LinAlgError as exc:
raise KittiPointPillarsBenchmarkError(
"KITTI calibration transform is singular"
) from exc
def _center_in_evaluation_range(x_m: float, y_m: float, z_m: float) -> bool:
return (
CROSS_DOMAIN_EVALUATION_RANGE[0]
<= x_m
<= CROSS_DOMAIN_EVALUATION_RANGE[3]
and CROSS_DOMAIN_EVALUATION_RANGE[1]
<= y_m
<= CROSS_DOMAIN_EVALUATION_RANGE[4]
and CROSS_DOMAIN_EVALUATION_RANGE[2]
<= z_m
<= CROSS_DOMAIN_EVALUATION_RANGE[5]
)
def _benchmark_class(box: PointPillarsBox) -> str:
try:
return MODEL_TO_KITTI_CLASS[box.model_class]
except KeyError as exc:
raise KittiPointPillarsBenchmarkError(
"PointPillars model class is not admitted"
) from exc
def _truth_as_box(truth: KittiLidarTruth) -> PointPillarsBox:
return PointPillarsBox(
x_m=truth.x_m,
y_m=truth.y_m,
z_m=truth.z_m,
length_m=truth.length_m,
width_m=truth.width_m,
height_m=truth.height_m,
yaw_rad=truth.yaw_rad,
class_id=-1,
model_class=truth.benchmark_class,
score=1.0,
)
def _evaluate_metric(
*,
class_name: str,
truth_by_frame: dict[str, tuple[KittiLidarTruth, ...]],
predictions: tuple[_ScoredPrediction, ...],
iou: Callable[[PointPillarsBox, PointPillarsBox], float],
) -> _MetricEvaluation:
truths = {
frame_id: tuple(
box for box in boxes if box.benchmark_class == class_name
)
for frame_id, boxes in truth_by_frame.items()
}
ground_truth_count = sum(len(boxes) for boxes in truths.values())
ordered = sorted(
(
prediction
for prediction in predictions
if prediction.benchmark_class == class_name
),
key=lambda prediction: (
-prediction.box.score,
prediction.frame_id,
prediction.index,
),
)
matched: set[tuple[str, int]] = set()
matched_pairs: list[tuple[PointPillarsBox, KittiLidarTruth]] = []
true_positive_flags: list[int] = []
false_positive_flags: list[int] = []
threshold = KITTI_IOU_THRESHOLDS[class_name]
for prediction in ordered:
candidates = truths[prediction.frame_id]
best_index = -1
best_iou = -1.0
for truth_index, truth in enumerate(candidates):
if (prediction.frame_id, truth_index) in matched:
continue
overlap = iou(prediction.box, _truth_as_box(truth))
if overlap > best_iou:
best_iou = overlap
best_index = truth_index
if best_index >= 0 and best_iou >= threshold:
matched.add((prediction.frame_id, best_index))
matched_pairs.append((prediction.box, candidates[best_index]))
true_positive_flags.append(1)
false_positive_flags.append(0)
else:
true_positive_flags.append(0)
false_positive_flags.append(1)
cumulative_true = np.cumsum(true_positive_flags, dtype=np.float64)
cumulative_false = np.cumsum(false_positive_flags, dtype=np.float64)
precision = np.divide(
cumulative_true,
np.maximum(cumulative_true + cumulative_false, 1.0),
)
recall = cumulative_true / max(float(ground_truth_count), 1.0)
average_precision = _ap40(precision, recall)
return _MetricEvaluation(
average_precision_40=average_precision,
precision=float(precision[-1]) if precision.size else 0.0,
recall=float(recall[-1]) if recall.size else 0.0,
true_positives=int(cumulative_true[-1]) if cumulative_true.size else 0,
false_positives=int(cumulative_false[-1]) if cumulative_false.size else 0,
ground_truth_count=ground_truth_count,
matched_truth=frozenset(matched),
matched_pairs=tuple(matched_pairs),
)
def _ap40(precision: np.ndarray, recall: np.ndarray) -> float:
if precision.size == 0:
return 0.0
interpolated = [
float(np.max(precision[recall >= threshold]))
if np.any(recall >= threshold)
else 0.0
for threshold in np.arange(40, dtype=np.float64) / 40.0
]
return _mean(interpolated)
def _matched_errors(
pairs: tuple[tuple[PointPillarsBox, KittiLidarTruth], ...],
) -> dict[str, Any]:
center_errors = [
math.dist(
(prediction.x_m, prediction.y_m, prediction.z_m),
(truth.x_m, truth.y_m, truth.z_m),
)
for prediction, truth in pairs
]
range_errors = [
abs(
math.hypot(prediction.x_m, prediction.y_m)
- math.hypot(truth.x_m, truth.y_m)
)
for prediction, truth in pairs
]
yaw_errors = [
abs(_wrap_angle(prediction.yaw_rad - truth.yaw_rad))
for prediction, truth in pairs
]
return {
"center_error_m": _error_summary(center_errors),
"range_error_m": _error_summary(range_errors),
"yaw_error_rad": _error_summary(yaw_errors),
}
def _error_summary(values: list[float]) -> dict[str, float | int | None]:
if not values:
return {"count": 0, "mean": None, "p95": None, "maximum": None}
array = np.asarray(values, dtype=np.float64)
return {
"count": len(values),
"mean": float(np.mean(array)),
"p95": float(np.percentile(array, 95)),
"maximum": float(np.max(array)),
}
def _distance_bucket_recall(
truth_by_frame: dict[str, tuple[KittiLidarTruth, ...]],
evaluations: dict[str, _MetricEvaluation],
) -> dict[str, dict[str, float | int]]:
result: dict[str, dict[str, float | int]] = {}
for minimum, maximum in DISTANCE_BUCKETS_M:
total = 0
matched = 0
for frame_id, truths in truth_by_frame.items():
by_class_index = {class_name: 0 for class_name in KITTI_BENCHMARK_CLASSES}
for truth in truths:
index = by_class_index[truth.benchmark_class]
by_class_index[truth.benchmark_class] += 1
distance = math.hypot(truth.x_m, truth.y_m)
if not minimum <= distance < maximum:
continue
total += 1
if (frame_id, index) in evaluations[
truth.benchmark_class
].matched_truth:
matched += 1
result[f"{int(minimum)}-{int(maximum)}m"] = {
"ground_truth_count": total,
"matched_count": matched,
"recall": matched / total if total else 0.0,
}
return result
def _wrap_angle(value: float) -> float:
return (value + math.pi) % (2.0 * math.pi) - math.pi
def _mean(values: list[float]) -> float:
return sum(values) / len(values) if values else 0.0
@@ -0,0 +1,828 @@
"""Fail-closed admission for the L3 NVIDIA PointPillars benchmark.
The admission deliberately separates three different claims:
* a detector-quality baseline needs independent oriented 3D box truth;
* K1 transfer can measure runtime and representation stability only after that
baseline has been accepted;
* point-wise semantic or instance labels are valuable evidence, but are not a
substitute for 3D cuboid truth.
The result is immutable and content addressed. It grants no command,
navigation, or safety authority and cannot start a worker or install a model.
"""
from __future__ import annotations
import hashlib
import json
import os
import re
import shutil
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Final
from k1link.artifacts import utc_now_iso
from k1link.compute.pointpillars_postprocess import (
POINTPILLARS_EMBEDDED_SCORE_THRESHOLD,
POINTPILLARS_MODEL_POINT_CLOUD_RANGE,
)
L3_PROFILE_SCHEMA: Final = "missioncore.l3-pointpillars-benchmark-profile/v1"
L3_DATASET_INVENTORY_SCHEMA: Final = "missioncore.l3-lidar-dataset-inventory/v1"
L3_WORKER_INVENTORY_SCHEMA: Final = "missioncore.l3-worker-inventory/v1"
L3_ADMISSION_SCHEMA: Final = "missioncore.l3-pointpillars-admission/v1"
L3_REPORT_SCHEMA: Final = "missioncore.l3-pointpillars-admission-report/v1"
L3_REPORT_NAME: Final = "admission-report.json"
L3_MANIFEST_NAME: Final = "manifest.json"
_RESULT_ID = re.compile(r"^l3-pointpillars-admission-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
_IDENTIFIER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/-]{0,191}$")
_BOX_ANNOTATION = "oriented-3d-boxes"
_ONNX_CONTRACT_SHA256 = (
"2fd29cd054ab058c2cfec3dfba305c71e123ef3f04b457d0c64de0c8dac2e1be"
)
_POINT_FIELDS = ("x", "y", "z", "intensity")
_MODEL_CLASSES = ("Vehicle", "Pedestrian", "Cyclist")
_BENCHMARK_CLASSES = ("Car", "Pedestrian", "Cyclist")
_BENCHMARK_CLASS_MAPPING = {
"Vehicle": "Car",
"Pedestrian": "Pedestrian",
"Cyclist": "Cyclist",
}
_POSTPROCESSING_CONTRACT = {
"reference_repository": "https://github.com/NVIDIA-AI-IOT/tao_toolkit_recipes",
"reference_commit": "a540badc47812a17a94e924b537d49ad3969b5a8",
"output_row_fields": [
"x",
"y",
"z",
"length",
"width",
"height",
"yaw",
"class_id",
"score",
],
"class_agnostic_nms": True,
"nms_iou_threshold": 0.01,
"pre_nms_top_n": 4096,
"embedded_score_threshold": POINTPILLARS_EMBEDDED_SCORE_THRESHOLD,
"embedded_contract_source": "onnx-node-attributes",
}
_BENCHMARK_METRIC_CONTRACT = {
"official_kitti_server_metric": False,
"evaluation_kind": "public-cross-domain-transfer-probe",
"ap_interpolation": "40-point",
"difficulty_filtering": False,
"predictions_outside_shared_range_ignored": True,
"iou_thresholds": {
"Car": 0.7,
"Pedestrian": 0.5,
"Cyclist": 0.5,
},
"distance_buckets_m": [[0, 20], [20, 40], [40, 70]],
}
_POINT_CLOUD_RANGE = POINTPILLARS_MODEL_POINT_CLOUD_RANGE
_PUBLIC_TRANSFER_METRICS = (
"bev-map",
"3d-map",
"center-error-m",
"range-error-m",
"yaw-error-rad",
"distance-bucket-recall",
"false-occupied-rate",
"end-to-end-latency-ms",
)
_TRANSFER_METRICS = (
"input-admission-rate",
"output-schema-valid-rate",
"deterministic-replay-rate",
"end-to-end-latency-ms",
"queue-wait-ms",
"drop-rate",
)
class L3PointPillarsAdmissionError(RuntimeError):
"""An L3 profile, inventory, or immutable result is invalid."""
@dataclass(frozen=True, slots=True)
class L3PointPillarsAdmission:
result_root: Path
result_id: str
manifest: dict[str, Any]
report: dict[str, Any]
@property
def public_transfer_probe_authorized(self) -> bool:
decision = _object(self.report.get("decision"), "L3 decision")
return decision.get("public_cross_domain_probe_authorized") is True
def build_l3_pointpillars_admission(
*,
profile_path: Path,
dataset_inventory_path: Path,
worker_inventory_path: Path,
output_root: Path,
) -> L3PointPillarsAdmission:
"""Build or reopen one immutable L3 benchmark admission."""
profile_path = profile_path.resolve(strict=True)
dataset_inventory_path = dataset_inventory_path.resolve(strict=True)
worker_inventory_path = worker_inventory_path.resolve(strict=True)
profile = _read_json(profile_path)
dataset_inventory = _read_json(dataset_inventory_path)
worker_inventory = _read_json(worker_inventory_path)
_validate_profile(profile)
_validate_dataset_inventory(dataset_inventory)
_validate_worker_inventory(worker_inventory)
identity = {
"schema_version": L3_ADMISSION_SCHEMA,
"profile": profile,
"profile_sha256": _sha256(profile_path),
"dataset_inventory_sha256": _sha256(dataset_inventory_path),
"worker_inventory_sha256": _sha256(worker_inventory_path),
"producer_sha256": _sha256(Path(__file__)),
"authority": _authority(),
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
result_id = f"l3-pointpillars-admission-{identity_sha256}"
destination = output_root.expanduser().absolute()
destination.mkdir(mode=0o700, parents=True, exist_ok=True)
result_root = destination / result_id
if result_root.exists():
return read_l3_pointpillars_admission(result_root)
report = _build_report(
result_id=result_id,
profile=profile,
dataset_inventory=dataset_inventory,
worker_inventory=worker_inventory,
)
staging = destination / f".{result_id}.{os.getpid()}.incomplete"
staging.mkdir(mode=0o700, exist_ok=False)
try:
_write_json(staging / L3_REPORT_NAME, report)
report_artifact = _artifact(
staging / L3_REPORT_NAME,
"l3-pointpillars-admission-report",
)
manifest = {
"schema_version": L3_ADMISSION_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"identity": identity,
"status": report["status"],
"artifacts": [report_artifact],
"created_at_utc": utc_now_iso(),
"authority": _authority(),
}
_write_json(staging / L3_MANIFEST_NAME, manifest)
os.replace(staging, result_root)
except BaseException:
shutil.rmtree(staging, ignore_errors=True)
raise
return read_l3_pointpillars_admission(result_root)
def read_l3_pointpillars_admission(root: Path) -> L3PointPillarsAdmission:
"""Read and fully validate an immutable L3 admission result."""
resolved = root.expanduser().resolve(strict=True)
if not resolved.is_dir() or _RESULT_ID.fullmatch(resolved.name) is None:
raise L3PointPillarsAdmissionError("L3 result root is invalid")
manifest = _read_json(resolved / L3_MANIFEST_NAME)
report = _read_json(resolved / L3_REPORT_NAME)
if (
manifest.get("schema_version") != L3_ADMISSION_SCHEMA
or manifest.get("result_id") != resolved.name
or report.get("schema_version") != L3_REPORT_SCHEMA
or report.get("result_id") != resolved.name
or manifest.get("status") != report.get("status")
or manifest.get("authority") != _authority()
or report.get("authority") != _authority()
):
raise L3PointPillarsAdmissionError("L3 manifest and report are inconsistent")
identity = _object(manifest.get("identity"), "L3 identity")
expected_identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
if (
manifest.get("identity_sha256") != expected_identity_sha256
or resolved.name != f"l3-pointpillars-admission-{expected_identity_sha256}"
):
raise L3PointPillarsAdmissionError("L3 result identity is invalid")
artifacts = _array(manifest.get("artifacts"), "L3 artifacts")
if len(artifacts) != 1:
raise L3PointPillarsAdmissionError("L3 result has an invalid artifact set")
artifact = _object(artifacts[0], "L3 report artifact")
report_path = resolved / L3_REPORT_NAME
if (
artifact.get("role") != "l3-pointpillars-admission-report"
or artifact.get("path") != L3_REPORT_NAME
or artifact.get("media_type") != "application/json"
or artifact.get("sha256") != _sha256(report_path)
or artifact.get("byte_length") != report_path.stat().st_size
):
raise L3PointPillarsAdmissionError("L3 report artifact is invalid")
_validate_report(report)
return L3PointPillarsAdmission(
result_root=resolved,
result_id=resolved.name,
manifest=manifest,
report=report,
)
def _build_report(
*,
result_id: str,
profile: dict[str, Any],
dataset_inventory: dict[str, Any],
worker_inventory: dict[str, Any],
) -> dict[str, Any]:
detector = _object(profile.get("detector"), "L3 detector")
runtime_policy = _object(profile.get("runtime_policy"), "L3 runtime policy")
public_probe = _object(
profile.get("public_cross_domain_probe"),
"L3 public cross-domain probe",
)
required_model = _required_string(detector, "triton_model_name")
candidate_frozen = detector.get("candidate_frozen") is True
required_version = detector.get("candidate_model_version")
required_source_sha256 = detector.get("candidate_source_sha256")
required_label_sha256 = detector.get("candidate_label_sha256")
triton = _object(worker_inventory.get("triton"), "L3 Triton inventory")
installed_models = [
_object(item, "L3 worker model")
for item in _array(triton.get("models"), "L3 worker models")
]
matching_models = [
model
for model in installed_models
if candidate_frozen
and model.get("name") == required_model
and model.get("upstream_version") == required_version
and model.get("source_model_sha256") == required_source_sha256
and model.get("source_label_sha256") == required_label_sha256
]
model_ready = any(_model_ready(model) for model in matching_models)
staged_models = [
_object(item, "L3 staged worker model")
for item in _array(
worker_inventory.get("staged_models"),
"L3 staged worker models",
)
]
matching_staged_models = [
model
for model in staged_models
if candidate_frozen
and model.get("name") == required_model
and model.get("upstream_version") == required_version
and model.get("source_model_sha256") == required_source_sha256
and model.get("source_label_sha256") == required_label_sha256
]
staged_model_ready = any(_model_ready(model) for model in matching_staged_models)
datasets = [
_object(item, "L3 dataset")
for item in _array(dataset_inventory.get("datasets"), "L3 datasets")
]
dataset_findings = [_dataset_finding(dataset) for dataset in datasets]
eligible_datasets = [
dataset
for dataset in datasets
if _dataset_supports_public_probe(dataset, public_probe)
]
runtime_checks = {
"canonical_triton_healthy": triton.get("healthy") is True,
"canonical_triton_image_pinned": (
triton.get("image") == runtime_policy.get("triton_image")
and triton.get("image_digest") == runtime_policy.get("triton_image_digest")
),
"explicit_model_control": triton.get("model_control_mode") == "explicit",
"strict_readiness": triton.get("strict_readiness") is True,
"model_repository_read_only": triton.get("model_repository_read_only") is True,
"second_serving_stack_absent": (
worker_inventory.get("serving_stack_count") == 1
),
}
blockers: list[str] = []
if not all(runtime_checks.values()):
blockers.append("canonical-triton-runtime-policy-not-satisfied")
if not eligible_datasets:
blockers.append("public-oriented-3d-box-truth-not-admitted")
if not candidate_frozen:
blockers.append("pointpillars-compatible-candidate-not-frozen")
elif not matching_models:
blockers.append(
"pointpillars-model-not-installed-live"
if staged_model_ready
else "pointpillars-model-artifact-not-installed"
)
elif not model_ready:
blockers.append("pointpillars-target-engine-or-provenance-not-verified")
public_probe_authorized = not blockers
status = (
"ready-for-public-cross-domain-probe"
if public_probe_authorized
else "blocked-foundation-assets"
)
next_gate = (
"run-public-cross-domain-pointpillars-probe"
if public_probe_authorized
else _next_gate(blockers)
)
return {
"schema_version": L3_REPORT_SCHEMA,
"result_id": result_id,
"status": status,
"profile_id": profile["profile_id"],
"observations": {
"dataset_inventory_observed_at_utc": dataset_inventory["observed_at_utc"],
"worker_inventory_observed_at_utc": worker_inventory["observed_at_utc"],
"worker_host_id": worker_inventory["host_id"],
},
"detector": {
"family": detector["family"],
"upstream_model_id": detector["upstream_model_id"],
"candidate_frozen": candidate_frozen,
"candidate_model_version": required_version,
"candidate_source_sha256": required_source_sha256,
"candidate_label_sha256": required_label_sha256,
"triton_model_name": required_model,
"required_input_fields": list(_POINT_FIELDS),
"maximum_points": detector["maximum_points"],
"point_cloud_range": list(_POINT_CLOUD_RANGE),
"training_domain": detector["training_domain"],
"training_ground_truth_publicly_reproducible": False,
"model_classes": list(_MODEL_CLASSES),
"benchmark_classes": list(_BENCHMARK_CLASSES),
"benchmark_class_mapping": _BENCHMARK_CLASS_MAPPING,
"benchmark_metric_contract": _BENCHMARK_METRIC_CONTRACT,
"postprocessing": _POSTPROCESSING_CONTRACT,
"installed_matching_model_count": len(matching_models),
"model_ready": model_ready,
"staged_matching_model_count": len(matching_staged_models),
"staged_target_engine_ready": staged_model_ready,
},
"runtime_checks": runtime_checks,
"dataset_findings": dataset_findings,
"eligible_public_probe_dataset_ids": [
_required_string(dataset, "dataset_id") for dataset in eligible_datasets
],
"blocker_codes": blockers,
"decision": {
"public_cross_domain_probe_authorized": public_probe_authorized,
"native_model_accuracy_claim_authorized": False,
"k1_transfer_stability_authorized": False,
"k1_transfer_quality_claim_authorized": False,
"semantic_point_labels_substitute_for_3d_boxes": False,
"fine_tuning_allowed": False,
"second_serving_stack_allowed": False,
"lab_publication_allowed": False,
"centerpoint_comparison_allowed": False,
},
"claim_boundaries": {
"public_cross_domain_probe_metrics": list(_PUBLIC_TRANSFER_METRICS),
"k1_transfer_stability_metrics": list(_TRANSFER_METRICS),
"public_probe_is_native_model_accuracy": False,
"k1_accuracy_requires_independent_truth": True,
"absence_of_detection_means_free_space": False,
"point_instance_clusters_are_3d_box_truth": False,
},
"next_gate": next_gate,
"authority": _authority(),
}
def _dataset_finding(dataset: dict[str, Any]) -> dict[str, Any]:
annotations = set(_string_array(dataset.get("annotations"), "dataset annotations"))
installed = dataset.get("installed") is True
has_box_truth = _BOX_ANNOTATION in annotations
return {
"dataset_id": _required_string(dataset, "dataset_id"),
"installed": installed,
"point_fields": _string_array(dataset.get("point_fields"), "dataset point fields"),
"annotations": sorted(annotations),
"independent_ground_truth": dataset.get("independent_ground_truth") is True,
"oriented_3d_box_accuracy_eligible": (
installed
and has_box_truth
and dataset.get("independent_ground_truth") is True
and set(_POINT_FIELDS).issubset(
set(_string_array(dataset.get("point_fields"), "dataset point fields"))
)
),
"semantic_or_instance_labels_are_not_boxes": (
not has_box_truth
and bool(
annotations.intersection(
{"point-semantic-labels", "point-instance-labels"}
)
)
),
}
def _dataset_supports_public_probe(
dataset: dict[str, Any],
public_probe: dict[str, Any],
) -> bool:
splits = set(_string_array(dataset.get("splits"), "dataset splits"))
point_fields = set(
_string_array(dataset.get("point_fields"), "dataset point fields")
)
annotations = set(
_string_array(dataset.get("annotations"), "dataset annotations")
)
required_split = _required_string(public_probe, "required_split")
return (
dataset.get("installed") is True
and dataset.get("independent_ground_truth") is True
and required_split in splits
and set(_POINT_FIELDS).issubset(point_fields)
and _BOX_ANNOTATION in annotations
)
def _model_ready(model: dict[str, Any]) -> bool:
smoke = model.get("representation_smoke")
return (
_valid_sha256(model.get("artifact_sha256"))
and model.get("backend") == "tensorrt"
and model.get("precision") == "strongly-typed"
and model.get("engine_built_on_target") is True
and model.get("provenance_verified") is True
and model.get("source_format") == "onnx"
and model.get("input_fields") == list(_POINT_FIELDS)
and model.get("maximum_points") == 204_800
and model.get("point_cloud_range") == list(_POINT_CLOUD_RANGE)
and model.get("model_classes") == list(_MODEL_CLASSES)
and _valid_sha256(model.get("source_label_sha256"))
and model.get("outputs")
== [
{
"name": "output_boxes",
"dtype": "FP32",
"shape": [1, 393_216, 9],
},
{"name": "num_boxes", "dtype": "INT32", "shape": [1]},
]
and isinstance(smoke, dict)
and smoke.get("status") == "engine-executed"
and _valid_sha256(smoke.get("input_artifact_sha256"))
and smoke.get("input_point_count") == 169_883
and isinstance(smoke.get("single_query_gpu_compute_ms"), float)
and 0.0 < smoke["single_query_gpu_compute_ms"] < 1000.0
and smoke.get("accuracy_evaluated") is False
and smoke.get("navigation_or_safety_accepted") is False
)
def _next_gate(blockers: list[str]) -> str:
missing_dataset = "public-oriented-3d-box-truth-not-admitted" in blockers
missing_candidate = "pointpillars-compatible-candidate-not-frozen" in blockers
missing_model = "pointpillars-model-artifact-not-installed" in blockers
staged_model = "pointpillars-model-not-installed-live" in blockers
if missing_dataset and missing_candidate:
return "admit-public-3d-box-split-and-freeze-compatible-pointpillars-candidate"
if missing_dataset and missing_model:
return "admit-public-3d-box-split-and-build-target-pointpillars-engine"
if missing_dataset and staged_model:
return "admit-public-3d-box-split-then-install-staged-pointpillars-model"
if missing_dataset:
return "admit-public-oriented-3d-box-validation-split"
if missing_model:
return "build-and-admit-target-pointpillars-engine"
if staged_model:
return "install-staged-pointpillars-model-in-canonical-triton"
if missing_candidate:
return "freeze-compatible-pointpillars-onnx-candidate"
if "pointpillars-target-engine-or-provenance-not-verified" in blockers:
return "verify-target-engine-and-model-provenance"
return "repair-canonical-triton-runtime-policy"
def _validate_profile(profile: dict[str, Any]) -> None:
if profile.get("schema_version") != L3_PROFILE_SCHEMA:
raise L3PointPillarsAdmissionError("L3 profile schema is invalid")
_safe_identifier(_required_string(profile, "profile_id"), "L3 profile id")
detector = _object(profile.get("detector"), "L3 detector")
if (
detector.get("family") != "nvidia-tao-pointpillars"
or detector.get("upstream_model_id") != "nvidia/tao/pointpillarnet"
or detector.get("triton_model_name") != "pointpillars"
or detector.get("required_source_format") != "onnx"
or detector.get("input_representation") != "native-sensor-scan"
or detector.get("input_coordinate_frame") != "sensor/lidar"
or detector.get("input_fields") != list(_POINT_FIELDS)
or detector.get("batch_size") != 1
or detector.get("maximum_points") != 204_800
or detector.get("point_cloud_range") != list(_POINT_CLOUD_RANGE)
or detector.get("training_domain") != "proprietary-solid-state-lidar"
or detector.get("training_ground_truth_publicly_reproducible") is not False
or detector.get("onnx_contract_sha256") != _ONNX_CONTRACT_SHA256
or detector.get("model_classes") != list(_MODEL_CLASSES)
):
raise L3PointPillarsAdmissionError("L3 detector contract is invalid")
candidate_frozen = detector.get("candidate_frozen")
candidate_version = detector.get("candidate_model_version")
candidate_sha256 = detector.get("candidate_source_sha256")
candidate_label_sha256 = detector.get("candidate_label_sha256")
if (
not isinstance(candidate_frozen, bool)
or (
candidate_frozen
and (
not isinstance(candidate_version, str)
or not candidate_version
or not _valid_sha256(candidate_sha256)
or not _valid_sha256(candidate_label_sha256)
)
)
or (
not candidate_frozen
and (
candidate_version is not None
or candidate_sha256 is not None
or candidate_label_sha256 is not None
)
)
):
raise L3PointPillarsAdmissionError("L3 detector candidate freeze is invalid")
if detector.get("postprocessing") != _POSTPROCESSING_CONTRACT:
raise L3PointPillarsAdmissionError("L3 detector postprocessing is invalid")
runtime = _object(profile.get("runtime_policy"), "L3 runtime policy")
if (
runtime.get("existing_triton_only") is not True
or runtime.get("second_serving_stack_allowed") is not False
or runtime.get("engine_built_on_target_required") is not True
or runtime.get("precision") != "strongly-typed"
or runtime.get("triton_image") != "nvcr.io/nvidia/tritonserver:26.06-py3"
or not _valid_sha256(runtime.get("triton_image_digest"))
):
raise L3PointPillarsAdmissionError("L3 runtime policy is invalid")
public_probe = _object(
profile.get("public_cross_domain_probe"),
"L3 public cross-domain probe",
)
if (
public_probe.get("required_split") != "validation"
or public_probe.get("required_ground_truth") != _BOX_ANNOTATION
or public_probe.get("independent_ground_truth_required") is not True
or public_probe.get("benchmark_classes") != list(_BENCHMARK_CLASSES)
or public_probe.get("model_to_benchmark_class_mapping")
!= _BENCHMARK_CLASS_MAPPING
or public_probe.get("metric_contract") != _BENCHMARK_METRIC_CONTRACT
or public_probe.get("metrics") != list(_PUBLIC_TRANSFER_METRICS)
or public_probe.get("retuning_allowed") is not False
or public_probe.get("native_accuracy_claim_allowed") is not False
):
raise L3PointPillarsAdmissionError(
"L3 public cross-domain probe is invalid"
)
transfer = _object(profile.get("k1_transfer_stability"), "L3 transfer gate")
if (
transfer.get("requires_completed_public_cross_domain_probe") is not True
or transfer.get("metrics") != list(_TRANSFER_METRICS)
or transfer.get("accuracy_claim_allowed") is not False
or transfer.get("retuning_allowed") is not False
):
raise L3PointPillarsAdmissionError("L3 K1 transfer gate is invalid")
if profile.get("authority") != _authority():
raise L3PointPillarsAdmissionError("L3 profile authority is invalid")
def _validate_dataset_inventory(inventory: dict[str, Any]) -> None:
if inventory.get("schema_version") != L3_DATASET_INVENTORY_SCHEMA:
raise L3PointPillarsAdmissionError("L3 dataset inventory schema is invalid")
_utc(_required_string(inventory, "observed_at_utc"), "dataset observation time")
datasets = _array(inventory.get("datasets"), "L3 datasets")
if not datasets:
raise L3PointPillarsAdmissionError("L3 dataset inventory is empty")
seen: set[str] = set()
for raw_dataset in datasets:
dataset = _object(raw_dataset, "L3 dataset")
dataset_id = _safe_identifier(
_required_string(dataset, "dataset_id"),
"L3 dataset id",
)
if dataset_id in seen:
raise L3PointPillarsAdmissionError("L3 dataset inventory has duplicates")
seen.add(dataset_id)
installed = dataset.get("installed")
if not isinstance(installed, bool):
raise L3PointPillarsAdmissionError("L3 dataset installed flag is invalid")
release_identity = dataset.get("release_identity_sha256")
if installed and not _valid_sha256(release_identity):
raise L3PointPillarsAdmissionError(
"installed L3 dataset lacks a release identity"
)
if not installed and release_identity is not None:
raise L3PointPillarsAdmissionError(
"absent L3 dataset cannot claim a release identity"
)
point_fields = _string_array(
dataset.get("point_fields"),
"dataset point fields",
)
if any(field not in _POINT_FIELDS for field in point_fields):
raise L3PointPillarsAdmissionError("L3 dataset point field is unknown")
_string_array(dataset.get("annotations"), "dataset annotations")
_string_array(dataset.get("splits"), "dataset splits")
if not isinstance(dataset.get("independent_ground_truth"), bool):
raise L3PointPillarsAdmissionError(
"L3 dataset truth independence flag is invalid"
)
_required_string(dataset, "license")
def _validate_worker_inventory(inventory: dict[str, Any]) -> None:
if inventory.get("schema_version") != L3_WORKER_INVENTORY_SCHEMA:
raise L3PointPillarsAdmissionError("L3 worker inventory schema is invalid")
_safe_identifier(_required_string(inventory, "host_id"), "L3 worker host id")
_utc(_required_string(inventory, "observed_at_utc"), "worker observation time")
serving_stack_count = inventory.get("serving_stack_count")
if (
isinstance(serving_stack_count, bool)
or not isinstance(serving_stack_count, int)
or serving_stack_count < 0
):
raise L3PointPillarsAdmissionError("L3 serving stack count is invalid")
triton = _object(inventory.get("triton"), "L3 Triton inventory")
_required_string(triton, "container_name")
_required_string(triton, "image")
if not _valid_sha256(triton.get("image_digest")):
raise L3PointPillarsAdmissionError("L3 Triton image digest is invalid")
for key in ("healthy", "strict_readiness", "model_repository_read_only"):
if not isinstance(triton.get(key), bool):
raise L3PointPillarsAdmissionError(f"L3 Triton {key} flag is invalid")
_required_string(triton, "model_control_mode")
models = _array(triton.get("models"), "L3 worker models")
seen: set[str] = set()
for raw_model in models:
model = _object(raw_model, "L3 worker model")
name = _safe_identifier(_required_string(model, "name"), "L3 worker model name")
if name in seen:
raise L3PointPillarsAdmissionError("L3 worker inventory has duplicate models")
seen.add(name)
if not _valid_sha256(model.get("artifact_sha256")):
raise L3PointPillarsAdmissionError("L3 worker model identity is invalid")
_required_string(model, "backend")
staged_models = _array(inventory.get("staged_models"), "L3 staged worker models")
staged_seen: set[str] = set()
for raw_model in staged_models:
model = _object(raw_model, "L3 staged worker model")
name = _safe_identifier(
_required_string(model, "name"),
"L3 staged worker model name",
)
if name in staged_seen:
raise L3PointPillarsAdmissionError(
"L3 worker inventory has duplicate staged models"
)
staged_seen.add(name)
if not _valid_sha256(model.get("artifact_sha256")):
raise L3PointPillarsAdmissionError(
"L3 staged worker model identity is invalid"
)
_required_string(model, "backend")
gpu = _object(inventory.get("gpu"), "L3 GPU inventory")
_required_string(gpu, "name")
_required_string(gpu, "driver_version")
memory_mib = gpu.get("memory_total_mib")
if (
isinstance(memory_mib, bool)
or not isinstance(memory_mib, int)
or memory_mib <= 0
):
raise L3PointPillarsAdmissionError("L3 GPU memory declaration is invalid")
def _validate_report(report: dict[str, Any]) -> None:
if report.get("schema_version") != L3_REPORT_SCHEMA:
raise L3PointPillarsAdmissionError("L3 report schema is invalid")
decision = _object(report.get("decision"), "L3 decision")
public_probe = decision.get("public_cross_domain_probe_authorized")
transfer = decision.get("k1_transfer_stability_authorized")
blockers = _string_array(report.get("blocker_codes"), "L3 blockers")
if (
not isinstance(public_probe, bool)
or not isinstance(transfer, bool)
or transfer is not False
or decision.get("native_model_accuracy_claim_authorized") is not False
or decision.get("k1_transfer_quality_claim_authorized") is not False
or decision.get("semantic_point_labels_substitute_for_3d_boxes") is not False
or decision.get("fine_tuning_allowed") is not False
or decision.get("second_serving_stack_allowed") is not False
or decision.get("lab_publication_allowed") is not False
or decision.get("centerpoint_comparison_allowed") is not False
or (public_probe and blockers)
or (not public_probe and not blockers)
or report.get("authority") != _authority()
):
raise L3PointPillarsAdmissionError("L3 report decision is invalid")
def _authority() -> dict[str, bool]:
return {
"shadow_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
}
def _artifact(path: Path, role: str) -> dict[str, object]:
return {
"role": role,
"path": path.name,
"media_type": "application/json",
"byte_length": path.stat().st_size,
"sha256": _sha256(path),
}
def _read_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise L3PointPillarsAdmissionError(
f"cannot read L3 JSON: {path.name}"
) from exc
return _object(value, f"L3 JSON {path.name}")
def _write_json(path: Path, value: object) -> None:
path.write_text(
json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _valid_sha256(value: object) -> bool:
return isinstance(value, str) and _SHA256.fullmatch(value) is not None
def _safe_identifier(value: str, label: str) -> str:
if _IDENTIFIER.fullmatch(value) is None:
raise L3PointPillarsAdmissionError(f"{label} is invalid")
return value
def _utc(value: str, label: str) -> None:
if not value.endswith("Z") or "T" not in value:
raise L3PointPillarsAdmissionError(f"{label} is invalid")
def _object(value: object, label: str) -> dict[str, Any]:
if not isinstance(value, dict) or any(not isinstance(key, str) for key in value):
raise L3PointPillarsAdmissionError(f"{label} must be an object")
return value
def _array(value: object, label: str) -> list[Any]:
if not isinstance(value, list):
raise L3PointPillarsAdmissionError(f"{label} must be an array")
return value
def _string_array(value: object, label: str) -> list[str]:
values = _array(value, label)
if any(not isinstance(item, str) or not item for item in values):
raise L3PointPillarsAdmissionError(f"{label} must contain strings")
return values
def _required_string(value: dict[str, Any], key: str) -> str:
item = value.get(key)
if not isinstance(item, str) or not item.strip():
raise L3PointPillarsAdmissionError(f"L3 {key} is invalid")
return item
@@ -0,0 +1,321 @@
"""Validated NVIDIA PointPillars candidate decoding and sample-compatible NMS."""
from __future__ import annotations
import math
from dataclasses import dataclass
from typing import Final
import numpy as np
POINTPILLARS_MODEL_CLASSES: Final = ("Vehicle", "Pedestrian", "Cyclist")
POINTPILLARS_OUTPUT_FIELDS: Final = (
"x",
"y",
"z",
"length",
"width",
"height",
"yaw",
"class_id",
"score",
)
POINTPILLARS_NMS_IOU_THRESHOLD: Final = 0.01
POINTPILLARS_PRE_NMS_TOP_N: Final = 4_096
POINTPILLARS_EMBEDDED_SCORE_THRESHOLD: Final = 0.1
POINTPILLARS_MODEL_POINT_CLOUD_RANGE: Final = (
-51.20000076293945,
-51.20000076293945,
-1.399999976158142,
51.20000076293945,
51.20000076293945,
4.400000095367432,
)
NVIDIA_REFERENCE_COMMIT: Final = "a540badc47812a17a94e924b537d49ad3969b5a8"
_EPSILON: Final = 1e-8
class PointPillarsPostprocessError(RuntimeError):
"""PointPillars output does not satisfy the frozen NVIDIA contract."""
@dataclass(frozen=True, slots=True)
class PointPillarsBox:
x_m: float
y_m: float
z_m: float
length_m: float
width_m: float
height_m: float
yaw_rad: float
class_id: int
model_class: str
score: float
@dataclass(frozen=True, slots=True)
class _Geometry:
corners: tuple[tuple[float, float], ...]
area: float
minimum_x: float
maximum_x: float
minimum_y: float
maximum_y: float
def decode_pointpillars_output(
output_boxes: np.ndarray,
num_boxes: np.ndarray,
*,
pre_nms_top_n: int = POINTPILLARS_PRE_NMS_TOP_N,
nms_iou_threshold: float = POINTPILLARS_NMS_IOU_THRESHOLD,
) -> tuple[PointPillarsBox, ...]:
"""Decode validated rows and reproduce the NVIDIA sample's class-agnostic NMS."""
boxes = np.asarray(output_boxes)
counts = np.asarray(num_boxes)
if boxes.shape != (1, 393_216, 9) or boxes.dtype != np.float32:
raise PointPillarsPostprocessError("PointPillars output_boxes contract changed")
if counts.shape != (1,) or counts.dtype != np.int32:
raise PointPillarsPostprocessError("PointPillars num_boxes contract changed")
count = int(counts[0])
if count < 0 or count > boxes.shape[1]:
raise PointPillarsPostprocessError("PointPillars candidate count is invalid")
if (
isinstance(pre_nms_top_n, bool)
or not isinstance(pre_nms_top_n, int)
or pre_nms_top_n < 1
or not math.isfinite(nms_iou_threshold)
or not 0.0 <= nms_iou_threshold <= 1.0
):
raise PointPillarsPostprocessError("PointPillars NMS parameters are invalid")
if count == 0:
return ()
candidates = boxes[0, :count]
if not np.isfinite(candidates).all():
raise PointPillarsPostprocessError("PointPillars candidate is non-finite")
decoded = tuple(_decode_row(row) for row in candidates)
ordered = tuple(
sorted(
decoded,
key=lambda box: box.score,
reverse=True,
)[:pre_nms_top_n]
)
return _class_agnostic_nms(ordered, nms_iou_threshold)
def oriented_bev_iou(one: PointPillarsBox, another: PointPillarsBox) -> float:
"""Return BEV IoU for two validated oriented boxes."""
one_geometry = _geometry(one)
another_geometry = _geometry(another)
overlap = _intersection_area(one_geometry, another_geometry)
denominator = one_geometry.area + another_geometry.area - overlap
return overlap / max(denominator, _EPSILON)
def oriented_3d_iou(one: PointPillarsBox, another: PointPillarsBox) -> float:
"""Return oriented 3D IoU for two center-based LiDAR-frame boxes."""
one_geometry = _geometry(one)
another_geometry = _geometry(another)
bev_overlap = _intersection_area(one_geometry, another_geometry)
one_minimum_z = one.z_m - one.height_m / 2.0
one_maximum_z = one.z_m + one.height_m / 2.0
another_minimum_z = another.z_m - another.height_m / 2.0
another_maximum_z = another.z_m + another.height_m / 2.0
height_overlap = max(
0.0,
min(one_maximum_z, another_maximum_z)
- max(one_minimum_z, another_minimum_z),
)
intersection = bev_overlap * height_overlap
one_volume = one_geometry.area * one.height_m
another_volume = another_geometry.area * another.height_m
return intersection / max(one_volume + another_volume - intersection, _EPSILON)
def _decode_row(row: np.ndarray) -> PointPillarsBox:
class_value = float(row[7])
class_id = int(round(class_value))
if (
abs(class_value - class_id) > 1e-5
or class_id < 0
or class_id >= len(POINTPILLARS_MODEL_CLASSES)
):
raise PointPillarsPostprocessError("PointPillars class id is invalid")
length_m, width_m, height_m = (float(row[index]) for index in (3, 4, 5))
score = float(row[8])
if (
length_m <= 0.0
or width_m <= 0.0
or height_m <= 0.0
or score < POINTPILLARS_EMBEDDED_SCORE_THRESHOLD
or score > 1.0
):
raise PointPillarsPostprocessError(
"PointPillars dimensions or score are invalid"
)
x_m, y_m, z_m = (float(row[index]) for index in (0, 1, 2))
return PointPillarsBox(
x_m=x_m,
y_m=y_m,
z_m=z_m,
length_m=length_m,
width_m=width_m,
height_m=height_m,
yaw_rad=float(row[6]),
class_id=class_id,
model_class=POINTPILLARS_MODEL_CLASSES[class_id],
score=score,
)
def _class_agnostic_nms(
boxes: tuple[PointPillarsBox, ...],
threshold: float,
) -> tuple[PointPillarsBox, ...]:
geometries = tuple(_geometry(box) for box in boxes)
suppressed = [False] * len(boxes)
accepted: list[PointPillarsBox] = []
for index, box in enumerate(boxes):
if suppressed[index]:
continue
accepted.append(box)
geometry = geometries[index]
for candidate_index in range(index + 1, len(boxes)):
if suppressed[candidate_index]:
continue
another = geometries[candidate_index]
if not _aabbs_overlap(geometry, another):
continue
overlap = _intersection_area(geometry, another)
iou = overlap / max(geometry.area + another.area - overlap, _EPSILON)
if iou >= threshold:
suppressed[candidate_index] = True
return tuple(accepted)
def _geometry(box: PointPillarsBox) -> _Geometry:
half_length = box.length_m / 2.0
half_width = box.width_m / 2.0
cosine = math.cos(box.yaw_rad)
sine = math.sin(box.yaw_rad)
corners = tuple(
(
box.x_m + local_x * cosine - local_y * sine,
box.y_m + local_x * sine + local_y * cosine,
)
for local_x, local_y in (
(-half_length, -half_width),
(half_length, -half_width),
(half_length, half_width),
(-half_length, half_width),
)
)
x_values = [point[0] for point in corners]
y_values = [point[1] for point in corners]
return _Geometry(
corners=corners,
area=box.length_m * box.width_m,
minimum_x=min(x_values),
maximum_x=max(x_values),
minimum_y=min(y_values),
maximum_y=max(y_values),
)
def _aabbs_overlap(one: _Geometry, another: _Geometry) -> bool:
return not (
one.maximum_x < another.minimum_x
or another.maximum_x < one.minimum_x
or one.maximum_y < another.minimum_y
or another.maximum_y < one.minimum_y
)
def _intersection_area(one: _Geometry, another: _Geometry) -> float:
if not _aabbs_overlap(one, another):
return 0.0
polygon = list(one.corners)
clip = another.corners
for index, edge_start in enumerate(clip):
edge_end = clip[(index + 1) % len(clip)]
polygon = _clip_polygon(polygon, edge_start, edge_end)
if not polygon:
return 0.0
return abs(
sum(
one_point[0] * another_point[1]
- another_point[0] * one_point[1]
for one_point, another_point in zip(
polygon,
(*polygon[1:], polygon[0]),
strict=True,
)
)
) / 2.0
def _clip_polygon(
polygon: list[tuple[float, float]],
edge_start: tuple[float, float],
edge_end: tuple[float, float],
) -> list[tuple[float, float]]:
if not polygon:
return []
result: list[tuple[float, float]] = []
previous = polygon[-1]
previous_inside = _inside(previous, edge_start, edge_end)
for current in polygon:
current_inside = _inside(current, edge_start, edge_end)
if current_inside:
if not previous_inside:
result.append(_line_intersection(previous, current, edge_start, edge_end))
result.append(current)
elif previous_inside:
result.append(_line_intersection(previous, current, edge_start, edge_end))
previous = current
previous_inside = current_inside
return result
def _inside(
point: tuple[float, float],
edge_start: tuple[float, float],
edge_end: tuple[float, float],
) -> bool:
return _cross(
edge_end[0] - edge_start[0],
edge_end[1] - edge_start[1],
point[0] - edge_start[0],
point[1] - edge_start[1],
) >= -_EPSILON
def _line_intersection(
segment_start: tuple[float, float],
segment_end: tuple[float, float],
edge_start: tuple[float, float],
edge_end: tuple[float, float],
) -> tuple[float, float]:
segment_x = segment_end[0] - segment_start[0]
segment_y = segment_end[1] - segment_start[1]
edge_x = edge_end[0] - edge_start[0]
edge_y = edge_end[1] - edge_start[1]
denominator = _cross(segment_x, segment_y, edge_x, edge_y)
if abs(denominator) <= _EPSILON:
return segment_end
offset_x = edge_start[0] - segment_start[0]
offset_y = edge_start[1] - segment_start[1]
ratio = _cross(offset_x, offset_y, edge_x, edge_y) / denominator
return (
segment_start[0] + ratio * segment_x,
segment_start[1] + ratio * segment_y,
)
def _cross(one_x: float, one_y: float, another_x: float, another_y: float) -> float:
return one_x * another_y - one_y * another_x