twanghcmut's picture
download
raw
5.1 kB
"""Synthetic, data-free tests for fpgm.depth.scene_flow."""
from __future__ import annotations
import numpy as np
import pytest
from fpgm.config import DepthConfig
from fpgm.depth.scene_flow import SceneFlowDepthSource
from fpgm.geometry.camera import Camera
from fpgm.types import CameraIntrinsics, DepthQuery, NoValidDepthAnnotationsError
def _make_camera() -> Camera:
intrinsics = CameraIntrinsics(fx=100.0, fy=100.0, cx=50.0, cy=50.0, width=100, height=100)
return Camera(intrinsics, np.eye(4)) # identity extrinsic: world frame == camera frame
class TestPlanarDepth:
def test_interpolated_depth_matches_analytic_plane(self):
camera = _make_camera()
grid = np.linspace(20.0, 80.0, 15)
u, v = np.meshgrid(grid, grid)
ref_uv = np.stack([u.ravel(), v.ravel()], axis=1)
ref_depth = np.full(ref_uv.shape[0], 2.0)
ref_pts = camera.unproject_to_cam(ref_uv, ref_depth) # analytic: constant depth plane
scene_flows = ref_pts[None, :, :]
n = ref_pts.shape[0]
scene_visibility = np.ones((1, n), dtype=bool)
scene_depth_valid = np.ones((1, n), dtype=bool)
source = SceneFlowDepthSource(
camera, scene_flows, scene_visibility, scene_depth_valid, True, DepthConfig()
)
rng = np.random.default_rng(3)
query_uv = rng.uniform(low=25.0, high=75.0, size=(10, 2))
result = source.query(
DepthQuery(frame_idx=0, uv=query_uv, query_resolution=(100, 100))
)
assert np.all(result.valid)
assert np.allclose(result.depth, 2.0, atol=1e-3)
assert np.all(result.confidence > 0.9) # a perfectly flat plane should be near-certain
class TestObjectMaskRestriction:
def test_mask_prevents_far_surface_contaminating_near_query(self):
camera = _make_camera()
# Foreground "object": a sparse 3x3 grid inside the [45, 55] pixel box, at
# depth 1.0. Its nearest point to the query below is ~2.24px away.
object_uv = np.array(
[[u, v] for u in (45.0, 49.0, 53.0) for v in (45.0, 49.0, 53.0)]
)
object_pts = camera.unproject_to_cam(object_uv, np.full(len(object_uv), 1.0))
# Background: a tight cluster at depth 5.0, positioned just *outside* the
# object's mask box (u < 45) but closer (~1.1-1.7px) to the query than any
# object point -- i.e. the far surface annotation intrudes into the k-NN
# neighbourhood right at the object's silhouette boundary, as it would at a
# real occlusion edge.
bg_u = np.linspace(44.3, 44.9, 12)
bg_v = np.full(12, 47.0)
bg_uv = np.stack([bg_u, bg_v], axis=1)
assert np.all(bg_uv[:, 0] < 45.0) # confirm construction: truly outside the mask box
bg_pts = camera.unproject_to_cam(bg_uv, np.full(len(bg_uv), 5.0))
all_pts = np.concatenate([object_pts, bg_pts], axis=0)
n = all_pts.shape[0]
scene_flows = all_pts[None, :, :]
scene_visibility = np.ones((1, n), dtype=bool)
scene_depth_valid = np.ones((1, n), dtype=bool)
source = SceneFlowDepthSource(
camera, scene_flows, scene_visibility, scene_depth_valid, True, DepthConfig()
)
# Just inside the object's silhouette, near its boundary -- close enough to
# the dense background cluster that unmasked k-NN is dominated by it.
query_uv = np.array([[46.0, 47.0]])
object_mask = np.zeros((100, 100), dtype=bool)
object_mask[45:56, 45:56] = True
unmasked = source.query(
DepthQuery(frame_idx=0, uv=query_uv, query_resolution=(100, 100))
)
masked = source.query(
DepthQuery(
frame_idx=0, uv=query_uv, query_resolution=(100, 100), object_mask=object_mask
)
)
assert unmasked.valid[0]
# All 8 nearest neighbours are the background cluster (closer than any
# object point), so the unmasked estimate should be ~exactly the far depth.
assert np.isclose(unmasked.depth[0], 5.0, atol=0.05), (
"unmasked query should be contaminated by the far surface"
)
assert masked.valid[0]
assert np.isclose(masked.depth[0], 1.0, atol=0.05), "masked query should recover the object"
class TestEmptySupport:
def test_empty_frame_support_raises(self):
camera = _make_camera()
rng = np.random.default_rng(9)
pts = rng.uniform(low=[-0.3, -0.3, 1.0], high=[0.3, 0.3, 2.0], size=(5, 3))
scene_flows = pts[None, :, :]
scene_visibility = np.zeros((1, 5), dtype=bool) # nothing visible this frame
scene_depth_valid = np.ones((1, 5), dtype=bool)
source = SceneFlowDepthSource(
camera, scene_flows, scene_visibility, scene_depth_valid, True, DepthConfig()
)
with pytest.raises(NoValidDepthAnnotationsError):
source.query(
DepthQuery(
frame_idx=0, uv=np.array([[50.0, 50.0]]), query_resolution=(100, 100)
)
)

Xet Storage Details

Size:
5.1 kB
·
Xet hash:
12b734f1729dd9f81bb28d3f3497b58328b93e158e296f34dc747c6d77b61104

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.