feat(perception): add ground-aware cuboid refusion

This commit is contained in:
DCCONSTRUCTIONS
2026-07-23 08:24:37 +03:00
parent b53d6d5a45
commit a0706fd5d8
8 changed files with 1116 additions and 114 deletions
+94
View File
@@ -120,6 +120,22 @@ def test_e13_profile_pins_provenance_marked_amodal_completion() -> None:
assert profile["cuboid_completion"]["temporal"]["confirmation_hits"] == 3
def test_e19_profile_adds_ground_aware_support_without_mutating_e14() -> None:
_fusion, runner = _worker_modules()
root = Path(__file__).resolve().parents[1] / "experiments" / "perception" / "worker"
e14, _e14_digest = runner.read_profile(root / "e14_full_session_amodal_profile.json")
e19, _e19_digest = runner.read_profile(root / "e19_ground_aware_cuboid_profile.json")
assert "object_support_ground_filter" not in e14["association"]
assert "support_duplicate_overlap_threshold" not in e14["association"]
assert e19["profile_id"] == "lab-e19-ground-aware-cuboids-v1"
assert (
e19["association"]["object_support_ground_filter"]["mode"]
== "local-ground-relative-object-support-v1"
)
assert e19["association"]["support_duplicate_overlap_threshold"] == 0.6
def test_e14_profile_combines_full_session_and_amodal_gates() -> None:
_fusion, runner = _worker_modules()
profile_path = (
@@ -278,6 +294,84 @@ def test_e13_temporal_filter_reduces_cuboid_center_jitter() -> None:
assert outputs[-1].temporal_status == "confirmed"
def test_e19_ground_filter_preserves_cloud_and_excludes_ground_from_box_support() -> None:
fusion, runner = _worker_modules()
profile, _digest = runner.read_profile(
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e19_ground_aware_cuboid_profile.json"
)
ground = np.asarray(
[[x, y, 0.0] for x in (9.5, 10.0, 10.5) for y in (-0.6, 0.0, 0.6)],
dtype=np.float64,
)
vehicle = np.asarray(
[[x, y, z] for x in (9.8, 10.2) for y in (-0.4, 0.4) for z in (0.3, 0.9, 1.4)],
dtype=np.float64,
)
cloud = np.concatenate((ground, vehicle))
unchanged = cloud.copy()
indices = np.arange(cloud.shape[0], dtype=np.int64)
filtered, ground_z, rejected = fusion._filter_object_support_by_ground(
indices,
cloud,
group="vehicle",
profile=profile["association"]["object_support_ground_filter"],
)
assert ground_z == 0.0
assert rejected == ground.shape[0]
assert filtered.tolist() == list(range(ground.shape[0], cloud.shape[0]))
np.testing.assert_array_equal(cloud, unchanged)
def test_e19_duplicate_tracks_cannot_publish_the_same_lidar_support_twice() -> None:
fusion, _runner = _worker_modules()
cuboid = fusion.Cuboid(
center_map=(10.0, 0.0, 0.8),
half_size=(2.25, 0.925, 0.775),
quaternion_xyzw=(0.0, 0.0, 0.0, 1.0),
)
def item(track_id: int, score: float, indices: list[int]) -> object:
source = np.asarray(indices, dtype=np.int64)
return fusion.TrackFusion(
track_id=track_id,
label="car",
association_group="vehicle",
score=score,
bbox_xyxy=(100.0, 100.0, 200.0, 200.0),
candidate_points=source.size,
semantic_points=source.size,
clustered_points=source.size,
distance_p10_m=9.5,
distance_median_m=10.0,
distance_smoothed_m=10.0,
status="accepted-class-prior-amodal-v1",
cuboid=cuboid,
source_indices=source,
)
result = fusion._suppress_duplicate_support_fusions(
[
item(10, 0.91, [1, 2, 3, 4, 5]),
item(11, 0.72, [1, 2, 3, 4]),
item(12, 0.80, [20, 21, 22, 23]),
],
overlap_threshold=0.6,
)
by_track = {value.track_id: value for value in result}
assert by_track[10].cuboid is not None
assert by_track[11].cuboid is None
assert by_track[11].status == "rejected-duplicate-lidar-support"
assert by_track[11].source_indices.size == 0
assert by_track[12].cuboid is not None
def test_e10_semantic_binding_is_explicit_about_freshness() -> None:
_fusion, runner = _worker_modules()
assert runner.semantic_binding(None, 2.0, 750.0) == ("unavailable", None)
+45 -29
View File
@@ -237,7 +237,15 @@ def test_recorded_blueprint_accumulates_point_frames_on_session_timeline() -> No
visible_ranges = spatial_view.properties["VisibleTimeRanges"]
line_grid = spatial_view.properties["LineGrid3D"]
assert visible_ranges.ranges.as_arrow_array().to_pylist() == [
assert visible_ranges.ranges.as_arrow_array().to_pylist() == []
assert line_grid.visible.as_arrow_array().to_pylist() == [False]
point_behavior, point_visualizer, point_time_ranges = spatial_view.visualizer_overrides[
"/world/points"
]
trajectory_behavior, trajectory_time_ranges = spatial_view.visualizer_overrides[
"/world/trajectory"
]
expected_accumulation = [
{
"timeline": SESSION_TIMELINE,
"range": {
@@ -246,9 +254,8 @@ def test_recorded_blueprint_accumulates_point_frames_on_session_timeline() -> No
},
}
]
assert line_grid.visible.as_arrow_array().to_pylist() == [False]
point_behavior, point_visualizer = spatial_view.visualizer_overrides["/world/points"]
trajectory_behavior = spatial_view.visualizer_overrides["/world/trajectory"]
assert point_time_ranges.ranges.as_arrow_array().to_pylist() == expected_accumulation
assert trajectory_time_ranges.ranges.as_arrow_array().to_pylist() == expected_accumulation
perception_behavior = spatial_view.visualizer_overrides["/world/perception"]
assert point_behavior.visible.as_arrow_array().to_pylist() == [False]
assert trajectory_behavior.visible.as_arrow_array().to_pylist() == [True]
@@ -277,9 +284,9 @@ def test_zero_accumulation_uses_latest_frame_instead_of_empty_time_range() -> No
blueprint = _recorded_blueprint(RerunSceneSettings(accumulation_seconds=0.0, show_grid=True))
spatial_view = blueprint.root_container.contents[0]
assert "VisibleTimeRanges" not in spatial_view.properties
assert spatial_view.properties["VisibleTimeRanges"].ranges.as_arrow_array().to_pylist() == []
point_behavior, point_visualizer = spatial_view.visualizer_overrides["/world/points"]
trajectory_behavior = spatial_view.visualizer_overrides["/world/trajectory"]
(trajectory_behavior,) = spatial_view.visualizer_overrides["/world/trajectory"]
assert point_behavior.visible.as_arrow_array().to_pylist() == [True]
assert trajectory_behavior.visible.as_arrow_array().to_pylist() == [True]
point_components = {str(batch.component_descriptor()) for batch in point_visualizer.overrides}
@@ -309,18 +316,32 @@ def test_dynamic_blueprint_reuses_scene_ids_without_playback_mutation() -> None:
assert first.root_container.contents[3].id == RECORDED_METRICS_VIEW_ID
assert second.root_container.contents[3].id == RECORDED_METRICS_VIEW_ID
first_point_behavior, first_point_visualizer = first_view.visualizer_overrides["/world/points"]
second_point_behavior, second_point_visualizer = second_view.visualizer_overrides[
"/world/points"
first_point_behavior, first_point_visualizer, first_point_time_ranges = (
first_view.visualizer_overrides["/world/points"]
)
second_point_behavior, second_point_visualizer, second_point_time_ranges = (
second_view.visualizer_overrides["/world/points"]
)
first_trajectory, first_trajectory_time_ranges = first_view.visualizer_overrides[
"/world/trajectory"
]
second_trajectory, second_trajectory_time_ranges = second_view.visualizer_overrides[
"/world/trajectory"
]
first_trajectory = first_view.visualizer_overrides["/world/trajectory"]
second_trajectory = second_view.visualizer_overrides["/world/trajectory"]
assert first_point_visualizer.id == RECORDED_POINTS_VISUALIZER_ID
assert second_point_visualizer.id == RECORDED_POINTS_VISUALIZER_ID
assert first_point_behavior.visible.as_arrow_array().to_pylist() == [False]
assert second_point_behavior.visible.as_arrow_array().to_pylist() == [True]
assert first_trajectory.visible.as_arrow_array().to_pylist() == [True]
assert second_trajectory.visible.as_arrow_array().to_pylist() == [False]
assert (
first_point_time_ranges.ranges.as_arrow_array().to_pylist()
== first_trajectory_time_ranges.ranges.as_arrow_array().to_pylist()
)
assert (
second_point_time_ranges.ranges.as_arrow_array().to_pylist()
== second_trajectory_time_ranges.ranges.as_arrow_array().to_pylist()
)
payload = recorded_blueprint_rrd(
RerunSceneSettings(show_points=False, show_trajectory=False),
@@ -373,17 +394,14 @@ def test_viewer_blueprint_reset_is_bounded_and_recreates_render_views() -> None:
cuboid_view = cuboids.root_container.contents[2]
assert cuboid_view.origin == "/world"
assert "VisibleTimeRanges" not in cuboid_view.properties
cuboid_points, cuboid_point_visualizer = cuboid_view.visualizer_overrides[
"/world/points"
]
cuboid_points, cuboid_point_visualizer = cuboid_view.visualizer_overrides["/world/points"]
cuboid_perception = cuboid_view.visualizer_overrides["/world/perception"]
cuboid_diagnostic_lidar = cuboid_view.visualizer_overrides[
"/world/perception/lidar"
]
cuboid_diagnostic_lidar = cuboid_view.visualizer_overrides["/world/perception/lidar"]
assert cuboid_points.visible.as_arrow_array().to_pylist() == [True]
assert cuboid_point_visualizer.id != initial.root_container.contents[0].visualizer_overrides[
"/world/points"
][1].id
assert (
cuboid_point_visualizer.id
!= initial.root_container.contents[0].visualizer_overrides["/world/points"][1].id
)
assert cuboid_perception.visible.as_arrow_array().to_pylist() == [True]
assert cuboid_diagnostic_lidar.visible.as_arrow_array().to_pylist() == [False]
perception_behavior = initial.root_container.contents[0].visualizer_overrides[
@@ -415,18 +433,16 @@ def test_viewer_blueprint_unifies_original_video_and_independent_ai_layers() ->
assert camera_view.visualizer_overrides[
"/perception/camera/segmentation"
].visible.as_arrow_array().to_pylist() == [True]
semantic_behavior, semantic_time_ranges = spatial_view.visualizer_overrides[
"/world/perception/semantic_points"
]
cuboid_behavior, cuboid_time_ranges = spatial_view.visualizer_overrides[
"/world/perception/boxes3d"
]
(semantic_behavior,) = spatial_view.visualizer_overrides["/world/perception/semantic_points"]
(cuboid_behavior,) = spatial_view.visualizer_overrides["/world/perception/boxes3d"]
assert semantic_behavior.visible.as_arrow_array().to_pylist() == [True]
assert cuboid_behavior.visible.as_arrow_array().to_pylist() == [False]
assert semantic_time_ranges.ranges.as_arrow_array().to_pylist() == []
assert cuboid_time_ranges.ranges.as_arrow_array().to_pylist() == []
visible_ranges = spatial_view.properties["VisibleTimeRanges"]
assert visible_ranges.ranges.as_arrow_array().to_pylist() == [
assert visible_ranges.ranges.as_arrow_array().to_pylist() == []
_point_behavior, _point_visualizer, point_time_ranges = spatial_view.visualizer_overrides[
"/world/points"
]
assert point_time_ranges.ranges.as_arrow_array().to_pylist() == [
{
"timeline": SESSION_TIMELINE,
"range": {"start": -12_000_000_000, "end": 0},