Files
Presa-Mano/tests/test_depth_align.py
Adriano Dal PastroandClaude Opus 4.7 810a6102fb test(depth_align): real z-buffer collision + drop dead var
The 2× focal-length collision test wasn't actually colliding (adjacent IR
pixels rounded to different RGB columns). 4× makes them collapse, so the
np.minimum.at reduce is now exercised. Also drop unused H_ir, W_ir.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:28:15 +02:00

86 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Unit tests for align_depth_to_rgb."""
from __future__ import annotations
import numpy as np
from presa_mano.perception.depth_align import align_depth_to_rgb
from presa_mano.perception.depth_to_rgb import CameraRegistration
def _identity_reg(W: int = 64, H: int = 48) -> CameraRegistration:
"""Same K for RGB and IR, no extrinsic offset."""
K = np.array([[60.0, 0.0, W / 2.0],
[0.0, 60.0, H / 2.0],
[0.0, 0.0, 1.0]], dtype=np.float64)
return CameraRegistration(
K_rgb=K.copy(), K_ir=K.copy(),
R_ir_to_rgb=np.eye(3, dtype=np.float64),
t_ir_to_rgb=np.zeros(3, dtype=np.float64),
rgb_width=W, rgb_height=H,
ir_width=W, ir_height=H,
)
def test_identity_alignment_preserves_depth():
"""K_ir == K_rgb and no extrinsic → every IR pixel maps to itself."""
reg = _identity_reg()
depth_ir = np.full((reg.ir_height, reg.ir_width), 1000, dtype=np.uint16)
depth_rgb = align_depth_to_rgb(depth_ir, reg)
assert depth_rgb.shape == (reg.rgb_height, reg.rgb_width)
assert depth_rgb.dtype == np.uint16
nonzero = depth_rgb[depth_rgb > 0]
# Coverage close to full (rounding can drop a few border pixels).
assert nonzero.size >= 0.95 * depth_rgb.size, f"coverage too low: {nonzero.size}/{depth_rgb.size}"
# All filled pixels equal the input depth (±1 mm rounding).
assert np.all(np.abs(nonzero.astype(int) - 1000) <= 1)
def test_zero_depth_pixels_stay_zero():
"""IR pixels with depth=0 must not produce any RGB output."""
reg = _identity_reg()
depth_ir = np.zeros((reg.ir_height, reg.ir_width), dtype=np.uint16)
depth_rgb = align_depth_to_rgb(depth_ir, reg)
assert depth_rgb.shape == (reg.rgb_height, reg.rgb_width)
assert int(depth_rgb.sum()) == 0
def test_sparse_pixels_preserved():
"""Two isolated IR depths produce exactly two filled RGB pixels (identity reg)."""
reg = _identity_reg(W=64, H=48)
depth_ir = np.zeros((reg.ir_height, reg.ir_width), dtype=np.uint16)
depth_ir[0, 0] = 1000
depth_ir[reg.ir_height - 1, reg.ir_width - 1] = 1000
depth_rgb = align_depth_to_rgb(depth_ir, reg)
assert (depth_rgb > 0).sum() == 2
def test_z_buffer_keeps_closer_point():
"""When two IR pixels collide on the same RGB pixel, the smaller depth wins.
Force a collision with K_ir = 4 × K_rgb (same resolution), so adjacent IR
pixels round to the same RGB pixel."""
W = H = 16
K_rgb = np.array([[60.0, 0.0, W / 2.0],
[0.0, 60.0, H / 2.0],
[0.0, 0.0, 1.0]], dtype=np.float64)
K_ir = K_rgb.copy()
K_ir[0, 0] *= 4.0 # 4× focal length → adjacent IR pixels collapse to one RGB pixel
K_ir[1, 1] *= 4.0
reg = CameraRegistration(
K_rgb=K_rgb, K_ir=K_ir,
R_ir_to_rgb=np.eye(3, dtype=np.float64),
t_ir_to_rgb=np.zeros(3, dtype=np.float64),
rgb_width=W, rgb_height=H, ir_width=W, ir_height=H,
)
depth_ir = np.zeros((H, W), dtype=np.uint16)
# IR pixels (8, 8) and (8, 9) both project to RGB pixel (8, 8):
# u_rgb = 0.25*(u_ir - 8) + 8 → 8.0 and 8.25 (both round to 8).
depth_ir[8, 8] = 1500
depth_ir[8, 9] = 800
depth_rgb = align_depth_to_rgb(depth_ir, reg)
nonzero = depth_rgb[depth_rgb > 0]
# Exactly one filled pixel; z-buffer kept the closer (800) value.
assert nonzero.size == 1
assert int(nonzero[0]) == 800
assert int(depth_rgb[8, 8]) == 800