Files
2026-09-27 19:20:43 +08:00

126 lines
4.5 KiB
Python

"""The UV-space texture: loading it, resampling it, and falling back without it.
The texture is authored in UV coordinates, black outside the island, so it
resamples into exactly the space the picker already uses. These tests pin the two
things that would silently ruin that: the orientation of the resample, and the
claim that adding a texture cannot change where a click lands.
"""
from __future__ import annotations
from pathlib import Path
import numpy as np
import pytest
from orisys_autocal.sensor import (
DEFAULT_FLAT_SIZE,
load_bundle,
load_uv_texture,
rasterise,
texture_path_for,
)
BUNDLE = Path(r"D:\orisys_ws\orisys-sensor-design\build\export\finger\r01\compute")
pytestmark = pytest.mark.skipif(
not BUNDLE.is_dir(), reason=f"no sensor export at {BUNDLE}"
)
@pytest.fixture(scope="module")
def bundle():
return load_bundle(BUNDLE)
@pytest.fixture(scope="module")
def texture(bundle):
path = texture_path_for(bundle)
if path is None:
pytest.skip("this export has no texture beside it")
loaded = load_uv_texture(path)
if loaded is None:
pytest.skip(f"could not decode {path}")
return loaded
def test_the_texture_is_found_from_the_bundle(bundle):
"""Derived, not configured: a mismatched pair resamples the wrong artwork."""
path = texture_path_for(bundle)
if path is None:
pytest.skip("this export has no texture beside it")
assert path.is_file()
assert path.name == "patch_texture_compute.png"
assert path.parent.parent.name == "finger"
def test_loading_a_texture_returns_an_rgb_ready_array(texture):
assert texture.ndim == 3 and texture.shape[2] == 4
assert texture.dtype == np.uint8
assert texture.shape[0] > 1 and texture.shape[1] > 1
def test_a_missing_texture_is_none_not_an_exception():
"""The picker has to keep working without artwork."""
assert load_uv_texture(r"D:\definitely\not\here.png") is None
def test_the_texture_is_black_outside_the_island_and_not_inside(texture):
"""The island's v extent is where the artwork lives; above and below is black."""
height = texture.shape[0]
top = texture[: int(height * 0.05), :, :3]
bottom = texture[int(height * 0.95) :, :, :3]
middle = texture[int(height * 0.45) : int(height * 0.55), :, :3]
assert int(top.max()) == 0, "the top band should be unused black"
assert int(bottom.max()) == 0, "the bottom band should be unused black"
assert int(middle.max()) > 0, "the island band should carry content"
def test_a_texture_does_not_change_where_a_click_lands(bundle, texture):
"""The lookup tables are the measurement; the image is only what you aim at."""
size = (256, 256)
plain = rasterise(bundle, size)
textured = rasterise(bundle, size, texture=texture)
assert plain.textured is False
assert textured.textured is True
assert np.array_equal(plain.face_index, textured.face_index)
assert np.allclose(plain.positions_mm, textured.positions_mm)
assert np.allclose(plain.normals, textured.normals)
assert plain.uv_min == textured.uv_min and plain.uv_max == textured.uv_max
def test_the_textured_image_samples_the_texture_in_the_right_place(bundle, texture):
"""Resample at the pixel's own UV, including the flip, or the map is mirrored."""
textured = rasterise(bundle, (256, 256), texture=texture)
covered = np.nonzero(textured.face_index >= 0)
rows, columns = covered[0][::97], covered[1][::97]
assert rows.size > 20
height, width = textured.face_index.shape
u0, v0 = textured.uv_min
u1, v1 = textured.uv_max
texture_height, texture_width = texture.shape[0], texture.shape[1]
for row, column in zip(rows, columns):
u = u0 + column / (width - 1) * (u1 - u0)
v = v1 - row / (height - 1) * (v1 - v0)
tex_x = int(round(u * (texture_width - 1)))
tex_y = int(round((1.0 - v) * (texture_height - 1)))
expected = texture[tex_y, tex_x, :3]
assert np.array_equal(textured.image_rgb[row, column], expected), (
f"pixel ({column}, {row}) does not carry the texture sample at its own UV"
)
def test_gap_pixels_stay_black_when_textured(bundle, texture):
"""A fabricated colour in the gap would look like part of the patch."""
textured = rasterise(bundle, (256, 256), texture=texture)
gaps = textured.face_index < 0
if not gaps.any():
pytest.skip("this raster has no gap pixels")
assert int(textured.image_rgb[gaps].max()) == 0
def test_the_default_flat_size_is_still_the_design_size():
assert DEFAULT_FLAT_SIZE == (1024, 1024)