108 lines
3.5 KiB
Python
108 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import math
|
|
import sys
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
WORKER_ROOT = (
|
|
Path(__file__).parents[1] / "experiments" / "perception" / "worker"
|
|
)
|
|
PROFILE_PATH = WORKER_ROOT / "e3_k1_camera1_profile.json"
|
|
|
|
|
|
def _worker_module() -> ModuleType:
|
|
path = WORKER_ROOT / "run_e3_rectified_segmentation.py"
|
|
sys.path.insert(0, str(WORKER_ROOT))
|
|
spec = importlib.util.spec_from_file_location("e3_rectified_segmentation", path)
|
|
assert spec is not None and spec.loader is not None
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def test_e3_profile_is_pinned_to_camera1_kb4_and_exact_model() -> None:
|
|
worker = _worker_module()
|
|
|
|
profile, digest = worker._profile(PROFILE_PATH)
|
|
|
|
assert len(digest) == 64
|
|
assert profile["source"]["calibration_slot"] == "camera_1"
|
|
assert profile["source"]["distortion_kb4"] == pytest.approx(
|
|
[
|
|
-0.023164451386679667,
|
|
-0.0014974198594105452,
|
|
-0.001039213149441563,
|
|
-0.000035237331915978814,
|
|
]
|
|
)
|
|
assert profile["model"]["revision"] == "8d6b6d1a3f7b50d441afd7d247c2ed10db186e8f"
|
|
assert profile["model"]["files"]["model.safetensors"]["sha256"] == (
|
|
"c265da9a74f58f5c3f4826d23ca4ca78beac0b106cca5842beca61580de5b782"
|
|
)
|
|
|
|
|
|
def test_kb4_inverse_round_trips_valid_field_angles() -> None:
|
|
worker = _worker_module()
|
|
profile, _digest = worker._profile(PROFILE_PATH)
|
|
coefficients = np.asarray(profile["source"]["distortion_kb4"])
|
|
theta = np.linspace(0.0, math.radians(96.4), 512)
|
|
squared = theta * theta
|
|
distorted = theta * (
|
|
1.0
|
|
+ coefficients[0] * squared
|
|
+ coefficients[1] * squared**2
|
|
+ coefficients[2] * squared**3
|
|
+ coefficients[3] * squared**4
|
|
)
|
|
|
|
recovered = worker._invert_kb4(distorted, coefficients)
|
|
|
|
assert recovered == pytest.approx(theta, abs=1e-10)
|
|
|
|
|
|
def test_five_view_rectification_covers_the_k1_valid_circle() -> None:
|
|
worker = _worker_module()
|
|
profile, _digest = worker._profile(PROFILE_PATH)
|
|
width, height = profile["source"]["resolution"]
|
|
_fx, _fy, cx, cy = profile["source"]["intrinsic_fx_fy_cx_cy"]
|
|
y, x = np.mgrid[:height, :width]
|
|
valid_mask = np.hypot(x - cx, y - cy) <= 293.0
|
|
|
|
maps = worker._rectification_maps(profile, valid_mask)
|
|
|
|
assert maps["coverage"]["coverage_fraction"] == 1.0
|
|
assert maps["coverage"]["covered_pixel_count"] == int(valid_mask.sum())
|
|
assert maps["coverage"]["overlap_count"]["min"] >= 1
|
|
assert maps["coverage"]["overlap_count"]["max"] <= 3
|
|
|
|
|
|
def test_tile_fusion_uses_the_selected_view_and_masks_outside_fov() -> None:
|
|
worker = _worker_module()
|
|
valid_mask = np.asarray([[True, True], [False, True]])
|
|
maps = {
|
|
"selected_tile": np.asarray([[0, 1], [-1, 1]], dtype=np.int8),
|
|
"tiles": [
|
|
{
|
|
"tile_map_x": np.zeros((2, 2), dtype=np.float32),
|
|
"tile_map_y": np.zeros((2, 2), dtype=np.float32),
|
|
},
|
|
{
|
|
"tile_map_x": np.ones((2, 2), dtype=np.float32),
|
|
"tile_map_y": np.ones((2, 2), dtype=np.float32),
|
|
},
|
|
],
|
|
}
|
|
semantics = [
|
|
np.asarray([[4, 4], [4, 4]], dtype=np.uint8),
|
|
np.asarray([[7, 7], [7, 7]], dtype=np.uint8),
|
|
]
|
|
|
|
result = worker._fuse_tiles(semantics, maps, valid_mask)
|
|
|
|
assert result.tolist() == [[4, 7], [0, 7]]
|