197 lines
6.0 KiB
Python
197 lines
6.0 KiB
Python
"""Verify shared Depth calibration and metadata from Depth artifacts."""
|
|
|
|
import json
|
|
from types import SimpleNamespace
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from anyimage.common.coordinate import canonical_direction_to_symmetry_legacy
|
|
from anyimage.common.depth import (
|
|
FLAT_DEPTH_DIRECTION,
|
|
REFERENCE_DEPTH_BASELINE,
|
|
depth_uniform_scale,
|
|
fit_depth_direction,
|
|
fit_symmetry_depth_direction,
|
|
load_depth_metadata,
|
|
reference_depth,
|
|
)
|
|
from tests.support.image_objects import _depth_image
|
|
|
|
|
|
def _camera_depth_image(depth, alpha=None):
|
|
depth = np.asarray(depth, dtype=np.float32)
|
|
points = np.zeros((*depth.shape, 3), dtype=np.float32)
|
|
points[..., 2] = depth
|
|
if alpha is None:
|
|
alpha = np.ones(depth.shape, dtype=np.float32)
|
|
return _depth_image(points, alpha)
|
|
|
|
|
|
def test_depth_metadata_loader_reads_only_size_and_intrinsics(tmp_path):
|
|
path = tmp_path / "depth.json"
|
|
path.write_text(
|
|
json.dumps(
|
|
{
|
|
"image_size": [3, 2],
|
|
"intrinsics": np.eye(3).tolist(),
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
metadata = load_depth_metadata(path)
|
|
|
|
assert metadata == {
|
|
"image_size": (3, 2),
|
|
"intrinsics": ((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)),
|
|
}
|
|
|
|
|
|
def test_base_plane_fit_is_stable_for_a_slender_diagonal_domain():
|
|
along = np.linspace(-1.0, 1.0, 101)
|
|
across = np.resize(np.asarray((-0.01, 0.0, 0.01)), len(along))
|
|
coordinates = np.column_stack((along - across, along + across))
|
|
values = 0.4 * along + np.sign(across) * 0.05
|
|
|
|
direction = fit_depth_direction(
|
|
coordinates,
|
|
values + 2.0,
|
|
np.ones(len(values), dtype=bool),
|
|
1.0,
|
|
)
|
|
slopes = np.asarray(direction[:2]) / direction[2]
|
|
selected_plane = coordinates @ slopes
|
|
corners = np.asarray(((-1.0, -1.0), (-1.0, 1.0), (1.0, -1.0), (1.0, 1.0)))
|
|
corner_plane = corners @ slopes
|
|
|
|
assert np.ptp(corner_plane) <= np.ptp(selected_plane) * 1.25
|
|
assert np.corrcoef(selected_plane, along)[0, 1] > 0.99
|
|
|
|
|
|
def test_reference_depth_uses_the_upper_depth_percentile():
|
|
depth = np.linspace(1.0, 1.2, 400, dtype=np.float32).reshape(20, 20)
|
|
|
|
reference = reference_depth(_camera_depth_image(depth))
|
|
|
|
assert reference == pytest.approx(float(np.percentile(depth, 95.0)))
|
|
assert reference > float(np.median(depth))
|
|
|
|
|
|
def test_reference_depth_ignores_transparent_invalid_and_non_positive_pixels():
|
|
depth = np.asarray(
|
|
((1.0, 1.05, 1.1, 9.0), (1.15, 0.0, np.nan, 1.2)),
|
|
dtype=np.float32,
|
|
)
|
|
alpha = np.asarray(
|
|
((True, True, True, False), (True, True, True, True)),
|
|
)
|
|
|
|
reference = reference_depth(_camera_depth_image(depth, alpha))
|
|
|
|
assert reference == pytest.approx(
|
|
float(np.percentile(np.asarray((1.0, 1.05, 1.1, 1.15, 1.2)), 95.0))
|
|
)
|
|
|
|
|
|
def test_reference_depth_narrows_its_domain_to_the_mask():
|
|
depth = np.asarray(((1.0, 1.05, 1.1, 1.15),), dtype=np.float32)
|
|
image = _camera_depth_image(depth)
|
|
mask = np.asarray(((False, False, True, True),))
|
|
|
|
assert reference_depth(image) == pytest.approx(
|
|
float(np.percentile(depth, 95.0))
|
|
)
|
|
assert reference_depth(image, mask) == pytest.approx(
|
|
float(np.percentile(np.asarray((1.1, 1.15)), 95.0))
|
|
)
|
|
|
|
|
|
def test_reference_depth_ignores_soft_depth_alpha():
|
|
depth = np.asarray(((1.0, 1.05, 1.1, 1.15),), dtype=np.float32)
|
|
image = _camera_depth_image(depth, ((1.0, 1.0, 0.9, 0.6),))
|
|
|
|
assert reference_depth(image) == pytest.approx(
|
|
float(np.percentile(np.asarray((1.0, 1.05)), 95.0))
|
|
)
|
|
|
|
|
|
def test_reference_depth_ignores_a_distant_tail():
|
|
depth = np.asarray(((2.0, 4.0, 6.0, 1000.0),), dtype=np.float32)
|
|
|
|
reference = reference_depth(_camera_depth_image(depth))
|
|
|
|
assert reference == pytest.approx(
|
|
float(np.percentile(np.asarray((2.0, 4.0)), 95.0))
|
|
)
|
|
|
|
|
|
def test_reference_depth_follows_the_content_scale_when_everything_is_far():
|
|
depth = np.asarray(((9.4, 10.6, 11.6, 13.0),), dtype=np.float32)
|
|
|
|
reference = reference_depth(_camera_depth_image(depth))
|
|
|
|
assert reference == pytest.approx(
|
|
float(np.percentile(np.asarray((9.4, 10.6, 11.6)), 95.0))
|
|
)
|
|
|
|
|
|
def test_reference_depth_falls_back_to_the_baseline_without_a_selection():
|
|
transparent = _camera_depth_image(
|
|
np.zeros((2, 2), dtype=np.float32),
|
|
np.zeros((2, 2), dtype=np.float32),
|
|
)
|
|
|
|
assert reference_depth(transparent) == pytest.approx(REFERENCE_DEPTH_BASELINE)
|
|
|
|
|
|
def test_symmetry_depth_direction_fits_a_sloped_base_plane():
|
|
vertices = []
|
|
uv = []
|
|
camera_depth = np.empty((3, 3), dtype=np.float32)
|
|
for row in range(3):
|
|
for column in range(3):
|
|
local_x, local_y = column - 1.0, 1.0 - row
|
|
vertices.append((local_x, 0.0, local_y))
|
|
uv.append((column * 0.5, 1.0 - row * 0.5))
|
|
camera_depth[row, column] = 2.0 + 0.2 * local_x + 0.1 * local_y
|
|
|
|
direction = fit_symmetry_depth_direction(
|
|
_camera_depth_image(camera_depth),
|
|
vertices,
|
|
uv,
|
|
0.5,
|
|
)
|
|
|
|
expected = np.asarray((0.1, 1.0, 0.05), dtype=np.float64)
|
|
expected /= np.linalg.norm(expected)
|
|
assert np.allclose(direction, expected, atol=1e-6)
|
|
|
|
|
|
def test_symmetry_depth_direction_returns_the_flat_direction_without_samples():
|
|
vertices = ((0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0))
|
|
uv = ((0.0, 0.0), (1.0, 0.0), (0.0, 1.0))
|
|
depth = np.ones((2, 2), dtype=np.float32)
|
|
image = _camera_depth_image(depth, np.zeros((2, 2), dtype=bool))
|
|
|
|
direction = fit_symmetry_depth_direction(image, vertices, uv, 1.0)
|
|
|
|
assert direction == FLAT_DEPTH_DIRECTION
|
|
assert canonical_direction_to_symmetry_legacy(direction) == (0.0, 0.0, 1.0)
|
|
|
|
|
|
def test_depth_uniform_scale_converts_model_units_at_the_reference_depth():
|
|
metadata = {
|
|
"intrinsics": ((100.0, 0.0, 50.0), (0.0, 100.0, 25.0), (0.0, 0.0, 1.0)),
|
|
"image_size": (101, 51),
|
|
}
|
|
|
|
uniform_scale = depth_uniform_scale(
|
|
SimpleNamespace(),
|
|
metadata,
|
|
2.0,
|
|
bounds=(-2.0, 2.0, -1.0, 1.0),
|
|
)
|
|
|
|
assert uniform_scale == pytest.approx(2.0)
|