import importlib.util import json import struct import tempfile import unittest from argparse import Namespace from pathlib import Path from PIL import Image SCRIPT = Path(__file__).parents[1] / "run_cpu_dense_reconstruction.py" SPEC = importlib.util.spec_from_file_location("cpu_dense_reconstruction", SCRIPT) MODULE = importlib.util.module_from_spec(SPEC) assert SPEC.loader is not None SPEC.loader.exec_module(MODULE) def write_test_glb(path: Path) -> None: image = Image.new("RGB", (8, 8), (0, 0, 0)) for y in range(4, 8): for x in range(8): image.putpixel((x, y), (20, 180, 60)) image.save(path.parent / "atlas.png") positions = struct.pack("<" + "f" * 18, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0) texcoords = struct.pack("<" + "f" * 12, 0.1, 0.1, 0.8, 0.1, 0.1, 0.3, 0.1, 0.7, 0.8, 0.7, 0.1, 0.9) indices = struct.pack(" None: args = Namespace(input=Path("images"), sparse_model=Path("sparse"), output=Path("output"), openmvs_bin=Path("tools")) MODULE.resolve_external_tool_paths(args) self.assertTrue(all(getattr(args, name).is_absolute() for name in ("input", "sparse_model", "output", "openmvs_bin"))) def test_point_color_preview_reads_openmvs_records(self) -> None: with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) dense = directory / "dense.ply" mesh = directory / "mesh.ply" destination = directory / "point-colour.glb" dense.write_bytes( b"ply\nformat binary_little_endian 1.0\nelement vertex 4\nproperty float32 x\nproperty float32 y\nproperty float32 z\nproperty uint8 red\nproperty uint8 green\nproperty uint8 blue\nproperty float32 nx\nproperty float32 ny\nproperty float32 nz\nproperty list uint8 uint32 view_indices\nproperty list uint8 float32 view_weights\nend_header\n" + b"".join(struct.pack(" None: with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) source = directory / "mesh.ply" destination = directory / "geometry.glb" source.write_text( "ply\nformat ascii 1.0\nelement vertex 4\nproperty float x\nproperty float y\nproperty float z\nelement face 4\nproperty list uchar int vertex_indices\nend_header\n" "0 0 0\n1 0 0\n0 1 0\n0 0 1\n3 0 1 2\n3 0 1 3\n3 0 2 3\n3 1 2 3\n", encoding="ascii", ) self.assertEqual(MODULE.build_geometry_preview_glb(source, destination), 4) self.assertTrue(destination.is_file()) def test_placeholder_colours_are_rejected(self) -> None: self.assertFalse(MODULE.reliable_texture_pixel((0, 0, 0))) self.assertFalse(MODULE.reliable_texture_pixel((255, 127, 39))) self.assertTrue(MODULE.reliable_texture_pixel((20, 180, 60))) def test_preview_keeps_only_faces_with_reliable_samples(self) -> None: with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) source = directory / "source.glb" destination = directory / "preview.glb" report_path = directory / "report.json" write_test_glb(source) report = MODULE.build_filtered_preview_glb(source, destination, report_path) self.assertEqual(report["input_faces"], 2) self.assertEqual(report["kept_faces"], 1) self.assertEqual(report["rejected_black_faces"], 1) self.assertTrue(destination.is_file()) self.assertEqual(json.loads(report_path.read_text(encoding="utf-8"))["kept_faces"], 1)