shuishen
5 days ago fbb068ec702338d609c1ca6eddbdb9f182d8f211
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
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_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)