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))
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")
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))
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)
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)
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))
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))
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)))
def load_mesh(): ply_file = StringIO(ply) verts, faces = load_ply(ply_file)
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))
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)))