Files
AnyImage/tests/anyimage/common/test_depth.py

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)