Example #1
0
 def _bm_load_ply(verts: torch.Tensor, faces: torch.Tensor,
                  decimal_places: int):
     f = StringIO()
     save_ply(f, verts, faces, decimal_places)
     s = f.getvalue()
     # Recreate stream so it's unaffected by how it was created.
     return lambda: load_ply(StringIO(s))
Example #2
0
    def test_simple_save(self):
        verts = torch.tensor(
            [[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0], [1, 2, 0]],
            dtype=torch.float32)
        faces = torch.tensor([[0, 1, 2], [0, 3, 4]])
        for filetype in BytesIO, TemporaryFile:
            lengths = {}
            for ascii in [True, False]:
                file = filetype()
                save_ply(file, verts=verts, faces=faces, ascii=ascii)
                lengths[ascii] = file.tell()

                file.seek(0)
                verts2, faces2 = load_ply(file)
                self.assertClose(verts, verts2)
                self.assertClose(faces, faces2)

                file.seek(0)
                if ascii:
                    file.read().decode("ascii")
                else:
                    with self.assertRaises(UnicodeDecodeError):
                        file.read().decode("ascii")

                if filetype is TemporaryFile:
                    file.close()
            self.assertLess(lengths[False], lengths[True],
                            "ascii should be longer")
Example #3
0
 def bm_load_simple_ply_with_init(V: int, F: int):
     verts = torch.tensor([[0.1, 0.2, 0.3]]).expand(V, 3)
     faces = torch.tensor([[0, 1, 2]], dtype=torch.int64).expand(F, 3)
     ply_file = StringIO()
     save_ply(ply_file, verts=verts, faces=faces)
     ply = ply_file.getvalue()
     # Recreate stream so it's unaffected by how it was created.
     return lambda: load_ply(StringIO(ply))
Example #4
0
 def test_simple_save(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, 3, 4]])
     file = StringIO()
     save_ply(file, verts=verts, faces=faces)
     file.seek(0)
     verts2, faces2 = load_ply(file)
     self.assertClose(verts, verts2)
     self.assertClose(faces, faces2)
Example #5
0
 def _test_save_load(self, verts, faces):
     f = StringIO()
     save_ply(f, verts, faces)
     f.seek(0)
     # raise Exception(f.getvalue())
     expected_verts, expected_faces = verts, faces
     if not len(expected_verts):  # Always compare with a (V, 3) tensor
         expected_verts = torch.zeros(size=(0, 3), dtype=torch.float32)
     if not len(expected_faces):  # Always compare with an (F, 3) tensor
         expected_faces = torch.zeros(size=(0, 3), dtype=torch.int64)
     actual_verts, actual_faces = load_ply(f)
     self.assertClose(expected_verts, actual_verts)
     self.assertClose(expected_faces, actual_faces)
Example #6
0
 def test_load_simple_ascii(self):
     ply_file = "\n".join(CUBE_PLY_LINES)
     for line_ending in [None, "\n", "\r\n"]:
         if line_ending is None:
             stream = StringIO(ply_file)
         else:
             byte_file = ply_file.encode("ascii")
             if line_ending == "\r\n":
                 byte_file = byte_file.replace(b"\n", b"\r\n")
             stream = BytesIO(byte_file)
         verts, faces = load_ply(stream)
         self.assertEqual(verts.shape, (8, 3))
         self.assertEqual(faces.shape, (12, 3))
         self.assertClose(verts, torch.FloatTensor(CUBE_VERTS))
         self.assertClose(faces, torch.LongTensor(CUBE_FACES))
Example #7
0
 def test_load_simple_ascii(self):
     ply_file = "\n".join([
         "ply",
         "format ascii 1.0",
         "comment made by Greg Turk",
         "comment this file is a cube",
         "element vertex 8",
         "property float x",
         "property float y",
         "property float z",
         "element face 6",
         "property list uchar int vertex_index",
         "end_header",
         "0 0 0",
         "0 0 1",
         "0 1 1",
         "0 1 0",
         "1 0 0",
         "1 0 1",
         "1 1 1",
         "1 1 0",
         "4 0 1 2 3",
         "4 7 6 5 4",
         "4 0 4 5 1",
         "4 1 5 6 2",
         "4 2 6 7 3",
         "4 3 7 4 0",
     ])
     for line_ending in [None, "\n", "\r\n"]:
         if line_ending is None:
             stream = StringIO(ply_file)
         else:
             byte_file = ply_file.encode("ascii")
             if line_ending == "\r\n":
                 byte_file = byte_file.replace(b"\n", b"\r\n")
             stream = BytesIO(byte_file)
         verts, faces = load_ply(stream)
         self.assertEqual(verts.shape, (8, 3))
         self.assertEqual(faces.shape, (12, 3))
         verts_expected = [
             [0, 0, 0],
             [0, 0, 1],
             [0, 1, 1],
             [0, 1, 0],
             [1, 0, 0],
             [1, 0, 1],
             [1, 1, 1],
             [1, 1, 0],
         ]
         self.assertClose(verts, torch.FloatTensor(verts_expected))
         faces_expected = [
             [0, 1, 2],
             [7, 6, 5],
             [0, 4, 5],
             [1, 5, 6],
             [2, 6, 7],
             [3, 7, 4],
             [0, 2, 3],
             [7, 5, 4],
             [0, 5, 1],
             [1, 6, 2],
             [2, 7, 3],
             [3, 4, 0],
         ]
         self.assertClose(faces, torch.LongTensor(faces_expected))
Example #8
0
    def test_bad_ply_syntax(self):
        """Some syntactically bad ply files."""
        lines = [
            "ply",
            "format ascii 1.0",
            "comment dashfadskfj;k",
            "element vertex 1",
            "property float x",
            "element listy 1",
            "property list uint int x",
            "end_header",
            "0",
            "0",
        ]
        lines2 = lines.copy()
        # this is ok
        _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[0] = "PLY"
        with self.assertRaisesRegex(ValueError, "Invalid file header."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[2] = "#this is a comment"
        with self.assertRaisesRegex(ValueError, "Invalid line.*"):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[3] = lines[4]
        lines2[4] = lines[3]
        with self.assertRaisesRegex(
                ValueError, "Encountered property before any element."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[8] = "1 2"
        with self.assertRaisesRegex(ValueError,
                                    "Inconsistent data for vertex."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines[:-1]
        with self.assertRaisesRegex(ValueError, "Not enough data for listy."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[5] = "element listy 2"
        with self.assertRaisesRegex(ValueError, "Not enough data for listy."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2.insert(4, "property short x")
        with self.assertRaisesRegex(
                ValueError, "Cannot have two properties called x in vertex."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2.insert(4, "property zz short")
        with self.assertRaisesRegex(ValueError, "Invalid datatype: zz"):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2.append("3")
        with self.assertRaisesRegex(ValueError, "Extra data at end of file."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2.append("comment foo")
        with self.assertRaisesRegex(ValueError, "Extra data at end of file."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2.insert(4, "element bad 1")
        with self.assertRaisesRegex(ValueError,
                                    "Found an element with no properties."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[-1] = "3 2 3 3"
        _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[-1] = "3 1 2 3 4"
        msg = "A line of listy data did not have the specified length."
        with self.assertRaisesRegex(ValueError, msg):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2 = lines.copy()
        lines2[3] = "element vertex one"
        msg = "Number of items for vertex was not a number."
        with self.assertRaisesRegex(ValueError, msg):
            _load_ply_raw(StringIO("\n".join(lines2)))

        # Heterogenous cases
        lines2 = lines.copy()
        lines2.insert(4, "property double y")

        with self.assertRaisesRegex(ValueError,
                                    "Too little data for an element."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        lines2[-2] = "3.3 4.2"
        _load_ply_raw(StringIO("\n".join(lines2)))

        lines2[-2] = "3.3 4.3 2"
        with self.assertRaisesRegex(ValueError,
                                    "Too much data for an element."):
            _load_ply_raw(StringIO("\n".join(lines2)))

        # Now make the ply file actually be readable as a Mesh

        with self.assertRaisesRegex(ValueError,
                                    "The ply file has no face element."):
            load_ply(StringIO("\n".join(lines)))

        lines2 = lines.copy()
        lines2[5] = "element face 1"
        with self.assertRaisesRegex(ValueError, "Invalid vertices in file."):
            load_ply(StringIO("\n".join(lines2)))

        lines2.insert(5, "property float z")
        lines2.insert(5, "property float y")
        lines2[-2] = "0 0 0"
        lines2[-1] = ""
        with self.assertRaisesRegex(ValueError, "Not enough data for face."):
            load_ply(StringIO("\n".join(lines2)))

        lines2[-1] = "2 0 0"
        with self.assertRaisesRegex(ValueError,
                                    "Faces must have at least 3 vertices."):
            load_ply(StringIO("\n".join(lines2)))

        # Good one
        lines2[-1] = "3 0 0 0"
        load_ply(StringIO("\n".join(lines2)))
Example #9
0
 def load_mesh():
     ply_file = StringIO(ply)
     verts, faces = load_ply(ply_file)
Example #10
0
 def test_load_simple_ascii(self):
     ply_file = '\n'.join(
         [
             'ply',
             'format ascii 1.0',
             'comment made by Greg Turk',
             'comment this file is a cube',
             'element vertex 8',
             'property float x',
             'property float y',
             'property float z',
             'element face 6',
             'property list uchar int vertex_index',
             'end_header',
             '0 0 0',
             '0 0 1',
             '0 1 1',
             '0 1 0',
             '1 0 0',
             '1 0 1',
             '1 1 1',
             '1 1 0',
             '4 0 1 2 3',
             '4 7 6 5 4',
             '4 0 4 5 1',
             '4 1 5 6 2',
             '4 2 6 7 3',
             '4 3 7 4 0',
         ]
     )
     for line_ending in [None, '\n', '\r\n']:
         if line_ending is None:
             stream = StringIO(ply_file)
         else:
             byte_file = ply_file.encode('ascii')
             if line_ending == '\r\n':
                 byte_file = byte_file.replace(b'\n', b'\r\n')
             stream = BytesIO(byte_file)
         verts, faces = load_ply(stream)
         self.assertEqual(verts.shape, (8, 3))
         self.assertEqual(faces.shape, (12, 3))
         verts_expected = [
             [0, 0, 0],
             [0, 0, 1],
             [0, 1, 1],
             [0, 1, 0],
             [1, 0, 0],
             [1, 0, 1],
             [1, 1, 1],
             [1, 1, 0],
         ]
         self.assertClose(verts, torch.FloatTensor(verts_expected))
         faces_expected = [
             [0, 1, 2],
             [7, 6, 5],
             [0, 4, 5],
             [1, 5, 6],
             [2, 6, 7],
             [3, 7, 4],
             [0, 2, 3],
             [7, 5, 4],
             [0, 5, 1],
             [1, 6, 2],
             [2, 7, 3],
             [3, 4, 0],
         ]
         self.assertClose(faces, torch.LongTensor(faces_expected))
Example #11
0
    def test_bad_ply_syntax(self):
        """Some syntactically bad ply files."""
        lines = [
            'ply',
            'format ascii 1.0',
            'comment dashfadskfj;k',
            'element vertex 1',
            'property float x',
            'element listy 1',
            'property list uint int x',
            'end_header',
            '0',
            '0',
        ]
        lines2 = lines.copy()
        # this is ok
        _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[0] = 'PLY'
        with self.assertRaisesRegex(ValueError, 'Invalid file header.'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[2] = '#this is a comment'
        with self.assertRaisesRegex(ValueError, 'Invalid line.*'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[3] = lines[4]
        lines2[4] = lines[3]
        with self.assertRaisesRegex(
            ValueError, 'Encountered property before any element.'
        ):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[8] = '1 2'
        with self.assertRaisesRegex(
            ValueError, 'Inconsistent data for vertex.'
        ):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines[:-1]
        with self.assertRaisesRegex(ValueError, 'Not enough data for listy.'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[5] = 'element listy 2'
        with self.assertRaisesRegex(ValueError, 'Not enough data for listy.'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2.insert(4, 'property short x')
        with self.assertRaisesRegex(
            ValueError, 'Cannot have two properties called x in vertex.'
        ):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2.insert(4, 'property zz short')
        with self.assertRaisesRegex(ValueError, 'Invalid datatype: zz'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2.append('3')
        with self.assertRaisesRegex(ValueError, 'Extra data at end of file.'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2.append('comment foo')
        with self.assertRaisesRegex(ValueError, 'Extra data at end of file.'):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2.insert(4, 'element bad 1')
        with self.assertRaisesRegex(
            ValueError, 'Found an element with no properties.'
        ):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[-1] = '3 2 3 3'
        _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[-1] = '3 1 2 3 4'
        msg = 'A line of listy data did not have the specified length.'
        with self.assertRaisesRegex(ValueError, msg):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2 = lines.copy()
        lines2[3] = 'element vertex one'
        msg = 'Number of items for vertex was not a number.'
        with self.assertRaisesRegex(ValueError, msg):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        # Heterogenous cases
        lines2 = lines.copy()
        lines2.insert(4, 'property double y')

        with self.assertRaisesRegex(
            ValueError, 'Too little data for an element.'
        ):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2[-2] = '3.3 4.2'
        _load_ply_raw(StringIO('\n'.join(lines2)))

        lines2[-2] = '3.3 4.3 2'
        with self.assertRaisesRegex(
            ValueError, 'Too much data for an element.'
        ):
            _load_ply_raw(StringIO('\n'.join(lines2)))

        # Now make the ply file actually be readable as a Mesh

        with self.assertRaisesRegex(
            ValueError, 'The ply file has no face element.'
        ):
            load_ply(StringIO('\n'.join(lines)))

        lines2 = lines.copy()
        lines2[5] = 'element face 1'
        with self.assertRaisesRegex(ValueError, 'Invalid vertices in file.'):
            load_ply(StringIO('\n'.join(lines2)))

        lines2.insert(5, 'property float z')
        lines2.insert(5, 'property float y')
        lines2[-2] = '0 0 0'
        lines2[-1] = ''
        with self.assertRaisesRegex(ValueError, 'Not enough data for face.'):
            load_ply(StringIO('\n'.join(lines2)))

        lines2[-1] = '2 0 0'
        with self.assertRaisesRegex(
            ValueError, 'Faces must have at least 3 vertices.'
        ):
            load_ply(StringIO('\n'.join(lines2)))

        # Good one
        lines2[-1] = '3 0 0 0'
        load_ply(StringIO('\n'.join(lines2)))