Refactor depth reference handling and cleanup

- Unified reference depth calculation to use a single implementation in common/depth.py.
- Removed server-side reference depth computation and adjusted depth.json to only include image_size and intrinsics.
- Updated depth texture handling to ignore transparent, invalid, and non-positive pixels when calculating reference depth.
- Changed reference depth calculation to use the 95th percentile of valid depth values.
- Adjusted depth direction fitting to return a flat direction when no samples are available.
- Removed cleanup threshold parameters from Cutout and related nodes, simplifying the interface.
- Updated tests to verify new reference depth logic and ensure compatibility with existing functionality.
This commit is contained in:
2026-09-15 22:50:16 +08:00
parent 94130ff38f
commit 2dfa9b2add
37 changed files with 665 additions and 639 deletions

View File

@@ -0,0 +1,190 @@
"""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_LIMIT,
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, 2.0, 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, 2.0, 3.0, 4.0), (5.0, 0.0, np.nan, 6.0)),
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, 2.0, 3.0, 5.0, 6.0)), 95.0))
)
def test_reference_depth_narrows_its_domain_to_the_mask():
depth = np.asarray(((4.0, 5.0, 1.0, 2.0),), 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.0, 2.0)), 95.0))
)
def test_reference_depth_ignores_soft_depth_alpha():
depth = np.asarray(((1.0, 2.0, 3.0, 4.0),), 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, 2.0)), 95.0))
)
def test_reference_depth_ignores_samples_beyond_the_metric_limit():
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, 6.0)), 95.0))
)
def test_reference_depth_falls_back_to_the_limit_without_a_selection():
transparent = _camera_depth_image(
np.zeros((2, 2), dtype=np.float32),
np.zeros((2, 2), dtype=np.float32),
)
far = _camera_depth_image(
np.asarray(((1000.0, 2000.0), (4000.0, 8000.0)), dtype=np.float32),
)
assert reference_depth(transparent) == pytest.approx(REFERENCE_DEPTH_LIMIT)
assert reference_depth(far) == pytest.approx(REFERENCE_DEPTH_LIMIT)
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)

View File

@@ -1,25 +0,0 @@
import json
import numpy as np
from anyimage.common.depth import load_depth_metadata
def test_depth_metadata_loader_uses_one_schema_for_plane_and_cutout(tmp_path):
path = tmp_path / "depth.json"
path.write_text(
json.dumps(
{
"image_size": [3, 2],
"intrinsics": np.eye(3).tolist(),
"reference_depth": 1.0,
}
),
encoding="utf-8",
)
metadata = load_depth_metadata(path)
assert metadata["image_size"] == (3, 2)
assert metadata["intrinsics"] == ((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
assert metadata["reference_depth"] == 1.0