Spaces:
Paused
Paused
Download sensorflow/evaluation/validation.py from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 8.08 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/evaluation/validation.py
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/sensorflow/evaluation/validation.py
-
curl -L -o validation.py https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/evaluation/validation.py
8.08 kB
| """Strict Quality Validation Engine: deterministic geometric plausibility checks. | |
| Compares LiDAR point cloud vs bounding-box proposal (spec §8) and computes | |
| per-3D-box metrics with configurable tolerances (spec §9): 3D IoU, position | |
| error, orientation error, dimension error, point density, point-in-box ratio, | |
| ground contact error, plus sensor-consistency checks. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| from typing import Dict, List, Optional | |
| import numpy as np | |
| from sensorflow.evaluation.records import ( | |
| Annotation, | |
| CheckLine, | |
| EvalStore, | |
| Frame, | |
| ValidationResult, | |
| ) | |
| from sensorflow.evaluation.synthetic import ( | |
| CLASS_DIMS, | |
| frame_points, | |
| points_in_box, | |
| project_bbox_2d, | |
| ) | |
| from sensorflow.metrics.perception_3d import bev_iou | |
| DEFAULT_TOLERANCES: Dict[str, float] = { | |
| "min_points_in_box": 12, | |
| "min_point_density": 0.4, # points per m^3 | |
| "min_box_occupancy": 0.05, # fraction of nearby points captured | |
| "max_dim_ratio": 1.6, # vs class prior | |
| "min_dim_ratio": 0.55, | |
| "max_centroid_offset_m": 1.2, # points centroid vs box center (BEV) | |
| "max_ground_contact_error_m": 0.5, | |
| "max_orientation_track_dev_deg": 45.0, | |
| "min_iou_3d": 0.5, # plausibility floor (triage gate is stricter) | |
| "max_position_error_m": 1.0, | |
| "max_orientation_error_deg": 30.0, | |
| "max_dimension_error": 0.5, | |
| } | |
| def iou_3d(box_a: List[float], box_b: List[float]) -> float: | |
| """BEV IoU extended with vertical overlap.""" | |
| biou = bev_iou(box_a, box_b) | |
| za0, za1 = box_a[2] - box_a[5] / 2, box_a[2] + box_a[5] / 2 | |
| zb0, zb1 = box_b[2] - box_b[5] / 2, box_b[2] + box_b[5] / 2 | |
| inter_h = max(0.0, min(za1, zb1) - max(za0, zb0)) | |
| union_h = max(za1, zb1) - min(za0, zb0) | |
| if union_h <= 0: | |
| return 0.0 | |
| return biou * (inter_h / union_h) * (union_h / max(box_a[5], box_b[5])) # approx volumetric | |
| def validate_annotation( | |
| store: EvalStore, | |
| ann: Annotation, | |
| frame: Frame, | |
| points: Optional[np.ndarray] = None, | |
| tolerances: Optional[Dict[str, float]] = None, | |
| ) -> ValidationResult: | |
| tol = dict(DEFAULT_TOLERANCES) | |
| if tolerances: | |
| tol.update(tolerances) | |
| checks: List[CheckLine] = [] | |
| result = ValidationResult(annotation_id=ann.annotation_id) | |
| if not ann.bbox_3d: | |
| result.passed = False | |
| result.checks = [CheckLine(gate="has_bbox_3d", actual=False, threshold=True, passed=False)] | |
| store.put("validations", result) | |
| return result | |
| if points is None: | |
| points = frame_points(store, frame) | |
| x, y, z, l, w, h, yaw = ann.bbox_3d | |
| volume = max(l * w * h, 1e-3) | |
| # --- point support | |
| mask = points_in_box(points, ann.bbox_3d) | |
| n_in = int(mask.sum()) | |
| density = n_in / volume | |
| result.point_density = round(density, 3) | |
| checks.append(CheckLine(gate="points_in_box", actual=n_in, | |
| threshold=tol["min_points_in_box"], | |
| passed=n_in >= tol["min_points_in_box"])) | |
| checks.append(CheckLine(gate="point_density_per_m3", actual=round(density, 2), | |
| threshold=tol["min_point_density"], | |
| passed=density >= tol["min_point_density"])) | |
| # --- box occupancy: fraction of points near the box that fall inside it | |
| near = points[(np.abs(points[:, 0] - x) < max(l, 4)) & (np.abs(points[:, 1] - y) < max(w, 4))] | |
| above_ground = near[near[:, 2] > 0.15] if len(near) else near | |
| occupancy = n_in / max(len(above_ground), 1) | |
| result.point_in_box_ratio = round(min(occupancy, 1.0), 4) | |
| checks.append(CheckLine(gate="box_occupancy", actual=round(occupancy, 3), | |
| threshold=tol["min_box_occupancy"], | |
| passed=occupancy >= tol["min_box_occupancy"])) | |
| # --- dimension plausibility vs class prior | |
| exp = CLASS_DIMS.get(ann.class_name) | |
| if exp: | |
| ratio = float(np.mean([l / exp[0], w / exp[1], h / exp[2]])) | |
| ok = tol["min_dim_ratio"] <= ratio <= tol["max_dim_ratio"] | |
| checks.append(CheckLine(gate="box_dimensions_vs_class_prior", actual=round(ratio, 3), | |
| threshold=f"[{tol['min_dim_ratio']}, {tol['max_dim_ratio']}]", passed=ok)) | |
| else: | |
| checks.append(CheckLine(gate="box_dimensions_vs_class_prior", actual="unknown_class", | |
| threshold="known class", passed=True, applicable=False)) | |
| # --- centroid consistency: LiDAR centroid vs box center | |
| if n_in >= 3: | |
| centroid = points[mask].mean(axis=0) | |
| offset = math.hypot(centroid[0] - x, centroid[1] - y) | |
| checks.append(CheckLine(gate="centroid_consistency_m", actual=round(offset, 3), | |
| threshold=tol["max_centroid_offset_m"], | |
| passed=offset <= tol["max_centroid_offset_m"])) | |
| else: | |
| checks.append(CheckLine(gate="centroid_consistency_m", actual="insufficient points", | |
| threshold=tol["max_centroid_offset_m"], passed=False, applicable=n_in > 0)) | |
| # --- object-to-ground relationship | |
| ground_err = abs((z - h / 2) - 0.0) | |
| result.ground_contact_error = round(ground_err, 3) | |
| checks.append(CheckLine(gate="ground_contact_error_m", actual=round(ground_err, 3), | |
| threshold=tol["max_ground_contact_error_m"], | |
| passed=ground_err <= tol["max_ground_contact_error_m"])) | |
| # --- sensor consistency: LiDAR box should have a camera projection when | |
| # visible; a projectable box missing a 2D detection is a disagreement. | |
| expected_2d = project_bbox_2d(ann.bbox_3d) | |
| sensor_ok = not (expected_2d is not None and ann.bbox_2d is None) | |
| result.sensor_consistent = sensor_ok | |
| checks.append(CheckLine(gate="sensor_consistency_cam_lidar", | |
| actual="2d_present" if ann.bbox_2d else "2d_missing", | |
| threshold="camera+lidar agree", passed=sensor_ok, | |
| applicable=expected_2d is not None)) | |
| # --- reference comparison (only when a reference ground truth exists) | |
| gt = next((g for g in frame.gt_boxes if g.gt_id == ann.matched_gt_id), None) | |
| if gt is not None: | |
| iou = iou_3d(ann.bbox_3d, gt.bbox_3d) | |
| pos_err = math.hypot(x - gt.bbox_3d[0], y - gt.bbox_3d[1]) | |
| yaw_err = abs(yaw - gt.bbox_3d[6]) % (2 * math.pi) | |
| yaw_err = min(yaw_err, 2 * math.pi - yaw_err) | |
| yaw_err_deg = math.degrees(yaw_err) | |
| dim_err = float(np.mean([abs(ann.bbox_3d[i] - gt.bbox_3d[i]) / max(gt.bbox_3d[i], 0.1) for i in (3, 4, 5)])) | |
| result.iou_3d = round(iou, 4) | |
| result.position_error = round(pos_err, 3) | |
| result.orientation_error_deg = round(yaw_err_deg, 2) | |
| result.dimension_error = round(dim_err, 4) | |
| checks.append(CheckLine(gate="iou_3d_vs_reference", actual=round(iou, 3), | |
| threshold=tol["min_iou_3d"], passed=iou >= tol["min_iou_3d"])) | |
| checks.append(CheckLine(gate="position_error_m", actual=round(pos_err, 3), | |
| threshold=tol["max_position_error_m"], | |
| passed=pos_err <= tol["max_position_error_m"])) | |
| checks.append(CheckLine(gate="orientation_error_deg", actual=round(yaw_err_deg, 1), | |
| threshold=tol["max_orientation_error_deg"], | |
| passed=yaw_err_deg <= tol["max_orientation_error_deg"])) | |
| checks.append(CheckLine(gate="dimension_error", actual=round(dim_err, 3), | |
| threshold=tol["max_dimension_error"], | |
| passed=dim_err <= tol["max_dimension_error"])) | |
| else: | |
| checks.append(CheckLine(gate="iou_3d_vs_reference", actual="no reference GT", | |
| threshold=tol["min_iou_3d"], passed=True, applicable=False)) | |
| result.checks = checks | |
| result.passed = all(c.passed for c in checks if c.applicable) | |
| store.put("validations", result) | |
| return result | |