shuishen
14 hours ago 385be2eca72eb3833efa4be0a0088b34e764788a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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("<IIIIII", 0, 1, 2, 3, 4, 5)
    binary = positions + texcoords + indices
    document = {
        "asset": {"version": "2.0"},
        "buffers": [{"byteLength": len(binary)}],
        "bufferViews": [
            {"buffer": 0, "byteOffset": 0, "byteLength": len(positions)},
            {"buffer": 0, "byteOffset": len(positions), "byteLength": len(texcoords)},
            {"buffer": 0, "byteOffset": len(positions) + len(texcoords), "byteLength": len(indices)},
        ],
        "accessors": [
            {"bufferView": 0, "componentType": 5126, "count": 6, "type": "VEC3"},
            {"bufferView": 1, "componentType": 5126, "count": 6, "type": "VEC2"},
            {"bufferView": 2, "componentType": 5125, "count": 6, "type": "SCALAR"},
        ],
        "images": [{"uri": "atlas.png"}],
        "textures": [{"source": 0}],
        "materials": [{"pbrMetallicRoughness": {"baseColorTexture": {"index": 0}}}],
        "meshes": [{"primitives": [{"attributes": {"POSITION": 0, "TEXCOORD_0": 1}, "indices": 2, "material": 0}]}],
    }
    json_chunk = json.dumps(document, separators=(",", ":")).encode("utf-8")
    json_chunk += b" " * ((-len(json_chunk)) % 4)
    output = bytearray(struct.pack("<4sII", b"glTF", 2, 0))
    for kind, payload in ((b"JSON", json_chunk), (b"BIN\x00", binary)):
        output.extend(struct.pack("<I4s", len(payload), kind))
        output.extend(payload)
    struct.pack_into("<I", output, 8, len(output))
    path.write_bytes(output)
 
 
class ReliableTexturePreviewTests(unittest.TestCase):
    def test_cuda_dense_log_requires_explicit_cuda_or_gpu_evidence(self) -> None:
        MODULE.verify_cuda_dense_log("OpenMVS CUDA device 0 initialized")
        MODULE.verify_cuda_dense_log("GPU depth maps are enabled")
        with self.assertRaisesRegex(RuntimeError, "did not report"):
            MODULE.verify_cuda_dense_log("OpenMVS x64 v2.4.0")
 
    def test_openmvs_tool_log_uses_the_latest_timestamped_log(self) -> None:
        with tempfile.TemporaryDirectory() as temporary:
            directory = Path(temporary)
            (directory / "DensifyPointCloud-older.log").write_text("CPU", encoding="utf-8")
            (directory / "DensifyPointCloud-newer.log").write_text("CUDA device 0", encoding="utf-8")
            self.assertEqual(MODULE.openmvs_tool_log(directory, "DensifyPointCloud"), "CUDA device 0")
 
    def test_openmvs_failure_includes_native_log(self) -> None:
        with tempfile.TemporaryDirectory() as temporary:
            directory = Path(temporary)
            (directory / "DensifyPointCloud-native.log").write_text("CUDA error (code 801)", encoding="utf-8")
            error = MODULE.with_openmvs_log(RuntimeError("DensifyPointCloud.exe failed"), directory, "DensifyPointCloud")
            self.assertIn("CUDA error (code 801)", str(error))
 
    def test_external_tool_paths_are_absolute(self) -> 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("<fffBBBfffBB", *point, 0, 0) for point in ((0, 0, 0, 255, 0, 0, 0, 0, 1), (1, 0, 0, 0, 255, 0, 0, 0, 1), (0, 1, 0, 0, 0, 255, 0, 0, 1), (0, 0, 1, 255, 255, 255, 0, 0, 1)))
            )
            mesh.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_point_color_preview_glb(dense, mesh, destination), 4)
            self.assertTrue(destination.is_file())
 
    def test_geometry_preview_keeps_all_mesh_faces(self) -> 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)