feat: Implement depth surface strip cleanup and organize tool CLI
- Added a proposal for depth surface strip cleanup, detailing the rationale and changes to the depth plane and cutout nodes. - Created specifications for the new cleanup requirements, including scenarios for strip removal and boundary edge detection. - Developed tasks for implementing shared strip detection logic and integrating new interfaces for depth nodes. - Established a new CLI structure for organizing node groups and model commands, replacing old entry points with a more intuitive command structure. - Introduced a prototype for panorama surface generation, including requirements for distance reconstruction and geometry node surfaces. - Added comprehensive tests for depth strip cleanup functionality, ensuring correct behavior for various mesh topologies and edge cases.
This commit is contained in:
93
experiments/panorama_surface/test_geometry.py
Normal file
93
experiments/panorama_surface/test_geometry.py
Normal file
@@ -0,0 +1,93 @@
|
||||
"""Behavior checks with independent spherical and room geometry references."""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from geometry import (camera_bases, merge_distances, panorama_directions,
|
||||
room_reference, sample_panorama, view_rays)
|
||||
from prototype import fixed_fov_distance
|
||||
|
||||
|
||||
def test_fixed_fov_recovers_shift_and_metric_distance():
|
||||
rays = view_rays(64)
|
||||
depth = 2 + 0.3 * rays[..., 0]
|
||||
points = rays * depth[..., None]
|
||||
points[..., 2] -= 0.7
|
||||
distance, mask = fixed_fov_distance(points, np.ones((64, 64)), 1.5)
|
||||
np.testing.assert_allclose(distance, depth * np.linalg.norm(rays, axis=-1) * 1.5, rtol=1e-5)
|
||||
assert mask.all()
|
||||
|
||||
|
||||
def test_longitude_sampling_wraps_without_a_seam():
|
||||
directions = panorama_directions(256, 128)
|
||||
image = 2 + directions[..., 0]
|
||||
rays = np.array(((1, 1e-8, 0), (1, -1e-8, 0)))
|
||||
sampled = sample_panorama(image, rays)
|
||||
np.testing.assert_allclose(sampled[0], sampled[1], atol=1e-6)
|
||||
np.testing.assert_allclose(sampled, 3, atol=0.001)
|
||||
|
||||
|
||||
def test_fusion_removes_per_view_scale_offsets():
|
||||
bases = camera_bases()
|
||||
scales = np.exp(np.linspace(-0.4, 0.4, 12))
|
||||
distances = []
|
||||
for scale, basis in zip(scales, bases):
|
||||
rays = view_rays(128) @ basis
|
||||
rays /= np.linalg.norm(rays, axis=-1, keepdims=True)
|
||||
distances.append((3 + 0.4 * rays[..., 0] + 0.2 * rays[..., 2]) * scale)
|
||||
merged, mask = merge_distances(distances, np.ones((12, 128, 128), bool), bases, 64, 32)
|
||||
rays = panorama_directions(64, 32)
|
||||
expected = 3 + 0.4 * rays[..., 0] + 0.2 * rays[..., 2]
|
||||
merged *= np.median(expected / merged)
|
||||
assert mask.all()
|
||||
assert np.mean(np.abs(merged / expected - 1)) < 0.002
|
||||
assert np.max(np.abs(merged[:, [0, -1]] / expected[:, [0, -1]] - 1)) < 0.005
|
||||
|
||||
|
||||
def test_room_distance_lands_on_floor():
|
||||
rays = np.array(((0.0, 0.0, -1.0), (0.2, -0.3, -1.0)))
|
||||
rays /= np.linalg.norm(rays, axis=-1, keepdims=True)
|
||||
distance, _ = room_reference(rays)
|
||||
np.testing.assert_allclose((rays * distance[:, None])[:, 2], -1.6)
|
||||
|
||||
|
||||
def test_sky_view_returns_invalid_samples():
|
||||
distance, mask = fixed_fov_distance(np.ones((32, 32, 3)), np.zeros((32, 32)), 1)
|
||||
assert not mask.any()
|
||||
assert np.isfinite(distance).all()
|
||||
|
||||
|
||||
def test_room_projection_fusion_preserves_geometry():
|
||||
bases = camera_bases()
|
||||
distances = [room_reference(view_rays(256) @ basis)[0] for basis in bases]
|
||||
merged, mask = merge_distances(distances, np.ones((12, 256, 256), bool), bases, 128, 64)
|
||||
expected, _ = room_reference(panorama_directions(128, 64))
|
||||
assert mask.all()
|
||||
assert np.mean(np.abs(merged / expected - 1)) < 0.005
|
||||
|
||||
|
||||
def test_input_alpha_survives_panorama_export(tmp_path, monkeypatch):
|
||||
from PIL import Image
|
||||
from prototype import main
|
||||
|
||||
rgba = np.full((16, 32, 4), 255, dtype=np.uint8)
|
||||
rgba[:, :16, 3] = 0
|
||||
source = tmp_path / "source.png"
|
||||
destination = tmp_path / "result"
|
||||
Image.fromarray(rgba).save(source)
|
||||
monkeypatch.setattr("sys.argv", ["prototype", "--image", str(source), "--output", str(destination),
|
||||
"--reference-only", "--size", "32", "--width", "32"])
|
||||
main()
|
||||
with Image.open(destination / "panorama.png") as exported:
|
||||
np.testing.assert_array_equal(np.asarray(exported)[..., 3], rgba[..., 3])
|
||||
with np.load(destination / "prediction.npz") as data:
|
||||
assert np.all(data["alpha"][:, :16] == 0)
|
||||
assert np.any(data["alpha"][:, 16:] > 0)
|
||||
|
||||
|
||||
def test_depth_alpha_combines_mask_and_source_alpha_once():
|
||||
from prototype import depth_alpha
|
||||
|
||||
image = np.ones((2, 4, 4), dtype=np.float32)
|
||||
image[..., 3] = (0, 0.5, 0.75, 1)
|
||||
mask = np.full((2, 4), 0.5, dtype=np.float32)
|
||||
np.testing.assert_allclose(depth_alpha(image, mask), np.tile((0, 0.25, 0.375, 0.5), (2, 1)))
|
||||
Reference in New Issue
Block a user