-
Notifications
You must be signed in to change notification settings - Fork 5
/
load_ply.py
137 lines (111 loc) · 4.52 KB
/
load_ply.py
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
132
133
134
135
136
137
#!/usr/bin/env python3
# Simple .ply file loader to avoid adding an unnecessary dependency.
import pathlib
from pathlib import Path
import numpy
def load_ply_using_library(filename):
""" Load a plyfile using the plyfile library.
Doing it this way softens the dependency on the external library. """
from plyfile import PlyData, PlyElement
plydata = PlyData.read(filename)
data = plydata['vertex'].data
properties = plydata.elements[0].properties
columnnames = [p.name for p in properties]
# The library represents data in a list of tuples.
# I use a flat numpy array. plyfile uses structured arrays
columns = len(data.dtype)
rows = len(data)
outdata = numpy.zeros((rows,columns),dtype=numpy.float32)
i = 0
for columnname in columnnames:
outdata[:,i] = data[columnname]
i += 1
columntypes = [str(data.dtype[i]) for i in range(len(data.dtype))] # numpy typestrings
return outdata, columnnames, columntypes
def save_ply(xyz,filename):
""" Dump a point cloud to an ascii ply file."""
assert len(xyz.shape) == 2
assert xyz.shape[1] == 3
header="""ply
format ascii 1.0
element vertex """+str(xyz.shape[0])+"""
property float x
property float y
property float z
end_header"""
numpy.savetxt(str(filename),xyz,header=header,fmt='%.8f',comments='')
def load_ply(filename, enableCaching=True):
""" Load an ascii based .ply file.
Inputs:
filename -- string or path
enableCaching -- bool, defaults to True
Outputs:
data -- row-major AOS numpy array.
columnnames -- list of string
columntypes -- list of string
"""
# Parse the header
filename = str(filename)
file = open(filename,'r')
header_lines = 0
try:
assert file.readline().strip() == 'ply'
except:
print("Trouble running readline on the file! Will try loading the file using the plyfile library!")
return load_ply_using_library(filename)
header_lines += 1
nextline = file.readline().strip()
if 'ascii' not in nextline:
assert 'binary' in nextline, 'Cannot even detect if ply file is ascii or binary!'
return load_ply_using_library(filename)
assert nextline == 'format ascii 1.0'
header_lines += 1
nextline = file.readline().strip()
while(nextline.split(' ')[0] == 'comment'):
header_lines += 1
nextline = file.readline().strip()
assert nextline.split(' ')[0] == 'element'
assert nextline.split(' ')[1] == 'vertex'
expected_vertices = int(nextline.split(' ')[2])
header_lines += 1
columntypes = []
columnnames = []
while(1):
nextline = file.readline().strip().split(' ')
if nextline[0] == 'property':
columntypes.append(nextline[1])
columnnames.append(nextline[2])
header_lines += 1
continue
else:
break
# meshlab annoyingly exports files with zero faces, instead of just ommitting the element.
if nextline[0] == 'element' and nextline[1] == 'face':
assert nextline[2] == '0', "Nonzero number of faces in the ply file is not handled yet!"
header_lines += 1
nextline = file.readline().strip().split(' ')
assert nextline[0] == 'property'
header_lines += 1
nextline = file.readline().strip().split(' ')
assert nextline[0] == 'end_header'
header_lines += 1
file.close()
plyPath = Path(filename)
plyTimestamp = plyPath.stat().st_mtime
plyCachedPath = plyPath.with_suffix('.npy')
if enableCaching and plyCachedPath.is_file() and plyCachedPath.stat().st_mtime > plyTimestamp:
#print("Pickled point cloud is newer than ascii .ply file; loading it!")
data = numpy.load(file=str(plyCachedPath))
else:
data = numpy.loadtxt(fname=filename,skiprows=header_lines)
#print("Pickle non-existent or older than .ply file; regenerating it!")
numpy.save(arr=data,file=str(plyCachedPath))
assert data.shape[0]==expected_vertices, 'Ply file corrupted! Inconsistent number of vertices!'
assert data.shape[0] > 0, 'Ply file did not contain any points!'
return data, columnnames, columntypes
def load_ply_just_xyz(plyFilePath, enableCaching=None):
""" A specialization that just returns the points """
data, columnnames, columntypes = load_ply(plyFilePath,enableCaching=enableCaching)
assert columnnames[0:3] == ['x', 'y', 'z']
xyz = data[:, 0:3].copy() # Just in case some other code needs a dense array.
return xyz