feat(perception): add ground-aware cuboid refusion
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user