コード例 #1
0
    def test_pluggable_load_cube(self):
        """
        This won't work on Windows due to NamedTemporaryFile being reopened.
        Use the testpath package instead?
        """
        ply_file = "\n".join(CUBE_PLY_LINES)
        io = IO()
        with NamedTemporaryFile(mode="w", suffix=".ply") as f:
            f.write(ply_file)
            f.flush()
            mesh = io.load_mesh(f.name)
        self.assertClose(mesh.verts_padded(),
                         torch.FloatTensor(CUBE_VERTS)[None])
        self.assertClose(mesh.faces_padded(),
                         torch.LongTensor(CUBE_FACES)[None])

        device = torch.device("cuda:0")

        with NamedTemporaryFile(mode="w", suffix=".ply") as f2:
            io.save_mesh(mesh, f2.name)
            f2.flush()
            mesh2 = io.load_mesh(f2.name, device=device)
        self.assertEqual(mesh2.verts_padded().device, device)
        self.assertClose(mesh2.verts_padded().cpu(), mesh.verts_padded())
        self.assertClose(mesh2.faces_padded().cpu(), mesh.faces_padded())

        with NamedTemporaryFile(mode="w") as f3:
            with self.assertRaisesRegex(
                    ValueError, "No mesh interpreter found to write to"):
                io.save_mesh(mesh, f3.name)
            with self.assertRaisesRegex(ValueError,
                                        "No mesh interpreter found to read "):
                io.load_mesh(f3.name)
コード例 #2
0
    def test_load_vertex_colors(self):
        # Example with no faces and with integer vertex colors
        off_file_lines = [
            "8 1 12",
            " 1.0  0.0 1.4142 0 1 0",
            " 0.0  1.0 1.4142 0 1 0",
            "-1.0  0.0 1.4142 0 1 0",
            " 0.0 -1.0 1.4142 0 1 0",
            " 1.0  0.0 0.0 0 1 0",
            " 0.0  1.0 0.0 0 1 0",
            "-1.0  0.0 0.0 0 1 0",
            " 0.0 -1.0 0.0 0 1 0",
            "3 0 1 2",
        ]
        off_file = "\n".join(off_file_lines)
        io = IO()
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            f.write(off_file)
            f.flush()
            mesh = io.load_mesh(f.name)

        self.assertEqual(mesh.verts_padded().shape, (1, 8, 3))
        verts_lines = (line.split()[:3] for line in off_file_lines[1:9])
        verts_data = [[[float(x) for x in line] for line in verts_lines]]
        self.assertClose(mesh.verts_padded(), torch.tensor(verts_data))
        self.assertClose(mesh.faces_padded(), torch.tensor([[[0, 1, 2]]]))

        self.assertIsInstance(mesh.textures, TexturesVertex)
        colors = mesh.textures.verts_features_padded()

        self.assertEqual(colors.shape, (1, 8, 3))
        self.assertClose(colors[0, :, [0, 2]], torch.zeros(8, 2))
        self.assertClose(colors[0, :, 1], torch.full((8, ), 1.0 / 255))
コード例 #3
0
    def test_load_lumpy(self):
        # Example off file whose faces have different numbers of vertices.
        off_file_lines = [
            "8 3 12",
            " 1.0  0.0 1.4142",
            " 0.0  1.0 1.4142",
            "-1.0  0.0 1.4142",
            " 0.0 -1.0 1.4142",
            " 1.0  0.0 0.0",
            " 0.0  1.0 0.0",
            "-1.0  0.0 0.0",
            " 0.0 -1.0 0.0",
            "3  0 1 2    255 0 0 #red",
            "4  7 4 0 3  0 255 0 #green",
            "4  4 5 1 0  0 0 255 #blue",
        ]
        off_file = "\n".join(off_file_lines)
        io = IO()
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            f.write(off_file)
            f.flush()
            mesh = io.load_mesh(f.name)

        self.assertEqual(mesh.verts_padded().shape, (1, 8, 3))
        verts_str = " ".join(off_file_lines[1:9])
        verts_data = torch.tensor([float(i) for i in verts_str.split()])
        self.assertClose(mesh.verts_padded().flatten(), verts_data)

        self.assertEqual(mesh.faces_padded().shape, (1, 5, 3))
        faces_expected = [[0, 1, 2], [7, 4, 0], [7, 0, 3], [4, 5, 1],
                          [4, 1, 0]]
        self.assertClose(mesh.faces_padded()[0], torch.tensor(faces_expected))
コード例 #4
0
    def test_load_face_colors(self):
        # Example from wikipedia
        off_file_lines = [
            "OFF",
            "# cube.off",
            "# A cube",
            " ",
            "8 6 12",
            " 1.0  0.0 1.4142",
            " 0.0  1.0 1.4142",
            "-1.0  0.0 1.4142",
            " 0.0 -1.0 1.4142",
            " 1.0  0.0 0.0",
            " 0.0  1.0 0.0",
            "-1.0  0.0 0.0",
            " 0.0 -1.0 0.0",
            "4  0 1 2 3  255 0 0 #red",
            "4  7 4 0 3  0 255 0 #green",
            "4  4 5 1 0  0 0 255 #blue",
            "4  5 6 2 1  0 255 0 ",
            "4  3 2 6 7  0 0 255",
            "4  6 5 4 7  255 0 0",
        ]
        off_file = "\n".join(off_file_lines)
        io = IO()
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            f.write(off_file)
            f.flush()
            mesh = io.load_mesh(f.name)

        self.assertEqual(mesh.verts_padded().shape, (1, 8, 3))
        verts_str = " ".join(off_file_lines[5:13])
        verts_data = torch.tensor([float(i) for i in verts_str.split()])
        self.assertClose(mesh.verts_padded().flatten(), verts_data)
        self.assertClose(mesh.faces_padded(), torch.tensor(CUBE_FACES)[None])

        faces_colors_full = mesh.textures.atlas_padded()
        self.assertEqual(faces_colors_full.shape, (1, 12, 1, 1, 3))
        faces_colors = faces_colors_full[0, :, 0, 0]
        max_color = faces_colors.max()
        self.assertEqual(max_color, 1)

        # Every face has one color 1, the rest 0.
        total_color = faces_colors.sum(dim=1)
        self.assertEqual(total_color.max(), max_color)
        self.assertEqual(total_color.min(), max_color)
コード例 #5
0
    def test_save_load_meshes(self):
        verts = torch.tensor([[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0]],
                             dtype=torch.float32)
        faces = torch.tensor([[0, 1, 2], [0, 2, 3]])
        normals = torch.tensor([[0, 1, 0], [1, 0, 0], [1, 4, 1], [1, 0, 0]],
                               dtype=torch.float32)
        vert_colors = torch.rand_like(verts)
        texture = TexturesVertex(verts_features=[vert_colors])

        for do_textures, do_normals in itertools.product([True, False],
                                                         [True, False]):
            mesh = Meshes(
                verts=[verts],
                faces=[faces],
                textures=texture if do_textures else None,
                verts_normals=[normals] if do_normals else None,
            )
            device = torch.device("cuda:0")

            io = IO()
            with NamedTemporaryFile(mode="w", suffix=".ply") as f:
                io.save_mesh(mesh.cuda(), f.name)
                f.flush()
                mesh2 = io.load_mesh(f.name, device=device)
            self.assertEqual(mesh2.device, device)
            mesh2 = mesh2.cpu()
            self.assertClose(mesh2.verts_padded(), mesh.verts_padded())
            self.assertClose(mesh2.faces_padded(), mesh.faces_padded())
            if do_normals:
                self.assertTrue(mesh.has_verts_normals())
                self.assertTrue(mesh2.has_verts_normals())
                self.assertClose(mesh2.verts_normals_padded(),
                                 mesh.verts_normals_padded())
            else:
                self.assertFalse(mesh.has_verts_normals())
                self.assertFalse(mesh2.has_verts_normals())
                self.assertFalse(
                    torch.allclose(mesh2.verts_normals_padded(), normals))
            if do_textures:
                self.assertIsInstance(mesh2.textures, TexturesVertex)
                self.assertClose(mesh2.textures.verts_features_list()[0],
                                 vert_colors)
            else:
                self.assertIsNone(mesh2.textures)
コード例 #6
0
    def test_load_open3d_mesh(self):
        # Header based on issue #1104
        header = "\n".join([
            "ply",
            "format binary_little_endian 1.0",
            "comment Created by Open3D",
            "element vertex 3",
            "property double x",
            "property double y",
            "property double z",
            "property double nx",
            "property double ny",
            "property double nz",
            "property uchar red",
            "property uchar green",
            "property uchar blue",
            "element face 1",
            "property list uchar uint vertex_indices",
            "end_header",
            "",
        ]).encode("ascii")
        vert_data = struct.pack("<" + "ddddddBBB" * 3, *range(9 * 3))
        face_data = struct.pack("<" + "BIII", 3, 0, 1, 2)
        io = IO()
        with NamedTemporaryFile(mode="wb", suffix=".ply") as f:
            f.write(header)
            f.write(vert_data)
            f.write(face_data)
            f.flush()
            mesh = io.load_mesh(f.name)

        self.assertClose(mesh.faces_padded(), torch.arange(3)[None, None])
        self.assertClose(
            mesh.verts_padded(),
            (torch.arange(3) + 9.0 * torch.arange(3)[:, None])[None],
        )
コード例 #7
0
    def test_save_load_icosphere(self):
        # Test that saving a mesh as an off file and loading it results in the
        # same data on the correct device, for all permitted types of textures.
        # Standard test is for random colors, but also check totally white,
        # because there's a different in OFF semantics between "1.0" color (=full)
        # and "1" (= 1/255 color)
        sphere = ico_sphere(0)
        io = IO()
        device = torch.device("cuda:0")

        atlas_padded = torch.rand(1, sphere.faces_list()[0].shape[0], 1, 1, 3)
        atlas = TexturesAtlas(atlas_padded)

        atlas_padded_white = torch.ones(1,
                                        sphere.faces_list()[0].shape[0], 1, 1,
                                        3)
        atlas_white = TexturesAtlas(atlas_padded_white)

        verts_colors_padded = torch.rand(1, sphere.verts_list()[0].shape[0], 3)
        vertex_texture = TexturesVertex(verts_colors_padded)

        verts_colors_padded_white = torch.ones(1,
                                               sphere.verts_list()[0].shape[0],
                                               3)
        vertex_texture_white = TexturesVertex(verts_colors_padded_white)

        # No colors case
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            io.save_mesh(sphere, f.name)
            f.flush()
            mesh1 = io.load_mesh(f.name, device=device)
        self.assertEqual(mesh1.device, device)
        mesh1 = mesh1.cpu()
        self.assertClose(mesh1.verts_padded(), sphere.verts_padded())
        self.assertClose(mesh1.faces_padded(), sphere.faces_padded())
        self.assertIsNone(mesh1.textures)

        # Atlas case
        sphere.textures = atlas
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            io.save_mesh(sphere, f.name)
            f.flush()
            mesh2 = io.load_mesh(f.name, device=device)

        self.assertEqual(mesh2.device, device)
        mesh2 = mesh2.cpu()
        self.assertClose(mesh2.verts_padded(), sphere.verts_padded())
        self.assertClose(mesh2.faces_padded(), sphere.faces_padded())
        self.assertClose(mesh2.textures.atlas_padded(),
                         atlas_padded,
                         atol=1e-4)

        # White atlas case
        sphere.textures = atlas_white
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            io.save_mesh(sphere, f.name)
            f.flush()
            mesh3 = io.load_mesh(f.name)

        self.assertClose(mesh3.textures.atlas_padded(),
                         atlas_padded_white,
                         atol=1e-4)

        # TexturesVertex case
        sphere.textures = vertex_texture
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            io.save_mesh(sphere, f.name)
            f.flush()
            mesh4 = io.load_mesh(f.name, device=device)

        self.assertEqual(mesh4.device, device)
        mesh4 = mesh4.cpu()
        self.assertClose(mesh4.verts_padded(), sphere.verts_padded())
        self.assertClose(mesh4.faces_padded(), sphere.faces_padded())
        self.assertClose(mesh4.textures.verts_features_padded(),
                         verts_colors_padded,
                         atol=1e-4)

        # white TexturesVertex case
        sphere.textures = vertex_texture_white
        with NamedTemporaryFile(mode="w", suffix=".off") as f:
            io.save_mesh(sphere, f.name)
            f.flush()
            mesh5 = io.load_mesh(f.name)

        self.assertClose(mesh5.textures.verts_features_padded(),
                         verts_colors_padded_white,
                         atol=1e-4)
コード例 #8
0
ファイル: test_io_obj.py プロジェクト: zvict/pytorch3d
    def test_load_obj_complex_pluggable(self):
        """
        This won't work on Windows due to the behavior of NamedTemporaryFile
        """
        obj_file = "\n".join([
            "# this is a comment",  # Comments should be ignored.
            "v 0.1 0.2 0.3",
            "v 0.2 0.3 0.4",
            "v 0.3 0.4 0.5",
            "v 0.4 0.5 0.6",
            "vn 0.000000 0.000000 -1.000000",
            "vn -1.000000 -0.000000 -0.000000",
            "vn -0.000000 -0.000000 1.000000",  # Normals should not be ignored.
            "v 0.5 0.6 0.7",
            "vt 0.749279 0.501284 0.0",  # Some files add 0.0 - ignore this.
            "vt 0.999110 0.501077",
            "vt 0.999455 0.750380",
            "f 1 2 3",
            "f 1 2 4 3 5",  # Polygons should be split into triangles
            "f 2/1/2 3/1/2 4/2/2",  # Texture/normals are loaded correctly.
            "f -1 -2 1",  # Negative indexing counts from the end.
        ])
        io = IO()
        with NamedTemporaryFile(mode="w", suffix=".obj") as f:
            f.write(obj_file)
            f.flush()
            mesh = io.load_mesh(f.name)
            mesh_from_path = io.load_mesh(Path(f.name))

        with NamedTemporaryFile(mode="w", suffix=".ply") as f:
            f.write(obj_file)
            f.flush()
            with self.assertRaisesRegex(ValueError, "Invalid file header."):
                io.load_mesh(f.name)

        expected_verts = torch.tensor(
            [
                [0.1, 0.2, 0.3],
                [0.2, 0.3, 0.4],
                [0.3, 0.4, 0.5],
                [0.4, 0.5, 0.6],
                [0.5, 0.6, 0.7],
            ],
            dtype=torch.float32,
        )
        expected_faces = torch.tensor(
            [
                [0, 1, 2],  # First face
                [0, 1, 3],  # Second face (polygon)
                [0, 3, 2],  # Second face (polygon)
                [0, 2, 4],  # Second face (polygon)
                [1, 2, 3],  # Third face (normals / texture)
                [4, 3, 0],  # Fourth face (negative indices)
            ],
            dtype=torch.int64,
        )
        self.assertClose(mesh.verts_padded(), expected_verts[None])
        self.assertClose(mesh.faces_padded(), expected_faces[None])
        self.assertClose(mesh_from_path.verts_padded(), expected_verts[None])
        self.assertClose(mesh_from_path.faces_padded(), expected_faces[None])
        self.assertIsNone(mesh.textures)
コード例 #9
0
def _load(path, **kwargs) -> Meshes:
    io = IO()
    io.register_meshes_format(MeshGlbFormat())
    return io.load_mesh(path, **kwargs)