NumPy-based PLY format input and output for Python with comprehensive support for reading and writing PLY polygon mesh files
npx @tessl/cli install tessl/pypi-plyfile@1.1.00
# PLYFile
1
2
NumPy-based PLY format input and output for Python. This library provides comprehensive support for reading and writing PLY (Polygon File Format) files, which are commonly used for storing 3D polygon mesh data in computer graphics and scientific computing applications.
3
4
## Package Information
5
6
- **Package Name**: plyfile
7
- **Language**: Python
8
- **Installation**: `pip install plyfile`
9
- **Dependencies**: NumPy (>=1.21)
10
11
## Core Imports
12
13
```python
14
import plyfile
15
from plyfile import PlyData, PlyElement, PlyProperty, PlyListProperty
16
```
17
18
For exception handling:
19
20
```python
21
from plyfile import PlyParseError, PlyElementParseError, PlyHeaderParseError
22
```
23
24
## Basic Usage
25
26
### Reading PLY Files
27
28
```python
29
from plyfile import PlyData
30
31
# Read PLY file from disk
32
ply_data = PlyData.read('mesh.ply')
33
34
# Access elements by name
35
vertex_element = ply_data['vertex']
36
face_element = ply_data['face']
37
38
# Access element data as NumPy arrays
39
vertices = vertex_element.data
40
faces = face_element.data
41
42
# Print file information
43
print(f"Number of vertices: {len(vertex_element)}")
44
print(f"Number of faces: {len(face_element)}")
45
print(f"Vertex properties: {[prop.name for prop in vertex_element.properties]}")
46
```
47
48
### Writing PLY Files
49
50
```python
51
import numpy as np
52
from plyfile import PlyData, PlyElement
53
54
# Create vertex data
55
vertex_data = np.array([
56
(0.0, 0.0, 0.0, 255, 0, 0),
57
(1.0, 0.0, 0.0, 0, 255, 0),
58
(0.0, 1.0, 0.0, 0, 0, 255)
59
], dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4'),
60
('red', 'u1'), ('green', 'u1'), ('blue', 'u1')])
61
62
# Create face data with connectivity lists
63
face_data = np.array([
64
([0, 1, 2],),
65
], dtype=[('vertex_indices', 'O')])
66
67
# Create PLY elements
68
vertex_element = PlyElement.describe(vertex_data, 'vertex')
69
face_element = PlyElement.describe(face_data, 'face')
70
71
# Create PLY data and write to file
72
ply_data = PlyData([vertex_element, face_element])
73
ply_data.write('output.ply')
74
```
75
76
## Capabilities
77
78
### PLY Data Container
79
80
The main container for PLY file data, including elements, comments, and format information.
81
82
```python { .api }
83
class PlyData:
84
def __init__(self, elements=[], text=False, byte_order='=', comments=[], obj_info=[]):
85
"""
86
Initialize PLY data container.
87
88
Parameters:
89
- elements (list): List of PlyElement instances
90
- text (bool): Whether file format is text/ASCII (True) or binary (False)
91
- byte_order (str): Byte order for binary files ('<', '>', '=')
92
- comments (list): Comment lines for header
93
- obj_info (list): Object info lines for header
94
"""
95
96
@staticmethod
97
def read(stream, mmap='c', known_list_len={}):
98
"""
99
Read PLY data from file or stream.
100
101
Parameters:
102
- stream (str or file): Filename or open file object
103
- mmap (str or bool): Memory mapping mode ('c', 'r', 'r+', or False)
104
- known_list_len (dict): Fixed lengths for list properties to enable memory mapping
105
106
Returns:
107
- PlyData: Parsed PLY data
108
109
Raises:
110
- PlyParseError: If file cannot be parsed
111
- ValueError: If stream mode incompatible with PLY format
112
"""
113
114
def write(self, stream):
115
"""
116
Write PLY data to file or stream.
117
118
Parameters:
119
- stream (str or file): Filename or open file object
120
121
Raises:
122
- ValueError: If stream mode incompatible with PLY format
123
"""
124
125
def __getitem__(self, name):
126
"""
127
Get element by name.
128
129
Parameters:
130
- name (str): Element name
131
132
Returns:
133
- PlyElement: The requested element
134
135
Raises:
136
- KeyError: If element not found
137
"""
138
139
def __contains__(self, name):
140
"""
141
Check if element exists.
142
143
Parameters:
144
- name (str): Element name
145
146
Returns:
147
- bool: True if element exists
148
"""
149
150
def __len__(self):
151
"""
152
Get number of elements.
153
154
Returns:
155
- int: Number of elements
156
"""
157
158
def __iter__(self):
159
"""
160
Iterate over elements.
161
162
Yields:
163
- PlyElement: Each element in order
164
"""
165
166
# Properties
167
elements: list # List of PlyElement instances
168
comments: list # Header comment lines
169
obj_info: list # Header object info lines
170
text: bool # Text format flag
171
byte_order: str # Byte order specification
172
header: str # Complete PLY header string
173
```
174
175
### PLY Element Container
176
177
Container for element data with properties and metadata.
178
179
```python { .api }
180
class PlyElement:
181
def __init__(self, name, properties, count, comments=[]):
182
"""
183
Initialize PLY element (internal use - prefer PlyElement.describe).
184
185
Parameters:
186
- name (str): Element name
187
- properties (list): List of PlyProperty instances
188
- count (int): Number of data rows
189
- comments (list): Element comments
190
"""
191
192
@staticmethod
193
def describe(data, name, len_types={}, val_types={}, comments=[]):
194
"""
195
Create PlyElement from NumPy array.
196
197
Parameters:
198
- data (numpy.ndarray): Structured NumPy array with element data
199
- name (str): Element name
200
- len_types (dict): Type specifications for list length fields
201
- val_types (dict): Type specifications for list value fields
202
- comments (list): Element comments
203
204
Returns:
205
- PlyElement: New element instance
206
207
Raises:
208
- TypeError: If data is not NumPy array
209
- ValueError: If array format unsupported
210
"""
211
212
def ply_property(self, name):
213
"""
214
Get property by name.
215
216
Parameters:
217
- name (str): Property name
218
219
Returns:
220
- PlyProperty: The requested property
221
222
Raises:
223
- KeyError: If property not found
224
"""
225
226
def dtype(self, byte_order='='):
227
"""
228
Get NumPy dtype for element data.
229
230
Parameters:
231
- byte_order (str): Byte order specification
232
233
Returns:
234
- numpy.dtype: Data type description
235
"""
236
237
def __getitem__(self, key):
238
"""
239
Access element data array.
240
241
Parameters:
242
- key: Array index or slice
243
244
Returns:
245
- Data from underlying NumPy array
246
"""
247
248
def __setitem__(self, key, value):
249
"""
250
Modify element data array.
251
252
Parameters:
253
- key: Array index or slice
254
- value: New value
255
"""
256
257
def __len__(self):
258
"""
259
Get number of data rows.
260
261
Returns:
262
- int: Number of rows
263
"""
264
265
def __contains__(self, name):
266
"""
267
Check if property exists.
268
269
Parameters:
270
- name (str): Property name
271
272
Returns:
273
- bool: True if property exists
274
"""
275
276
# Properties
277
name: str # Element name
278
count: int # Number of data rows
279
data: numpy.ndarray # Structured array with element data
280
properties: list # List of PlyProperty instances
281
comments: list # Element comments
282
header: str # PLY header block for this element
283
```
284
285
### Property Definitions
286
287
Property metadata describing scalar and list data fields.
288
289
```python { .api }
290
class PlyProperty:
291
def __init__(self, name, val_dtype):
292
"""
293
Initialize scalar property.
294
295
Parameters:
296
- name (str): Property name
297
- val_dtype (str): PLY or NumPy data type specification
298
"""
299
300
def dtype(self, byte_order='='):
301
"""
302
Get NumPy dtype string.
303
304
Parameters:
305
- byte_order (str): Byte order specification
306
307
Returns:
308
- str: NumPy dtype string
309
"""
310
311
# Properties
312
name: str # Property name
313
val_dtype: str # NumPy data type code
314
315
class PlyListProperty(PlyProperty):
316
def __init__(self, name, len_dtype, val_dtype):
317
"""
318
Initialize list property.
319
320
Parameters:
321
- name (str): Property name
322
- len_dtype (str): Data type for list length field
323
- val_dtype (str): Data type for list elements
324
"""
325
326
def list_dtype(self, byte_order='='):
327
"""
328
Get NumPy dtypes for list components.
329
330
Parameters:
331
- byte_order (str): Byte order specification
332
333
Returns:
334
- tuple: (length_dtype, value_dtype)
335
"""
336
337
def dtype(self, byte_order='='):
338
"""
339
Get NumPy dtype for property field (always object type).
340
341
Parameters:
342
- byte_order (str): Byte order specification
343
344
Returns:
345
- str: Always '|O' for object arrays
346
"""
347
348
# Properties (inherited from PlyProperty)
349
name: str # Property name
350
val_dtype: str # NumPy data type for list elements
351
len_dtype: str # NumPy data type for list length
352
```
353
354
### Exception Handling
355
356
Error handling for PLY file parsing and validation.
357
358
```python { .api }
359
class PlyParseError(Exception):
360
"""Base class for PLY parsing errors."""
361
362
class PlyElementParseError(PlyParseError):
363
def __init__(self, message, element=None, row=None, prop=None):
364
"""
365
Element parsing error.
366
367
Parameters:
368
- message (str): Error description
369
- element (PlyElement): Element being parsed
370
- row (int): Row number where error occurred
371
- prop (PlyProperty): Property being parsed
372
"""
373
374
# Properties
375
message: str # Error message
376
element: PlyElement # Element context
377
row: int # Row number
378
prop: PlyProperty # Property context
379
380
class PlyHeaderParseError(PlyParseError):
381
def __init__(self, message, line=None):
382
"""
383
Header parsing error.
384
385
Parameters:
386
- message (str): Error description
387
- line (str): Header line where error occurred
388
"""
389
390
# Properties
391
message: str # Error message
392
line: str # Header line context
393
```
394
395
## Advanced Usage Examples
396
397
### Memory Mapping for Large Files
398
399
```python
400
from plyfile import PlyData
401
402
# Enable memory mapping for better performance with large files
403
# Requires known list lengths for elements with list properties
404
known_lengths = {
405
'vertex': {}, # No list properties
406
'face': {'vertex_indices': 3} # Triangular faces
407
}
408
409
ply_data = PlyData.read('large_mesh.ply', mmap='c', known_list_len=known_lengths)
410
```
411
412
### Custom Data Types
413
414
```python
415
import numpy as np
416
from plyfile import PlyElement
417
418
# Create data with custom list length and value types
419
vertex_data = np.array([
420
(0.0, 0.0, 0.0, [255, 128, 64]),
421
(1.0, 0.0, 0.0, [0, 255, 128]),
422
], dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4'), ('rgb', 'O')])
423
424
# Specify types for list properties
425
element = PlyElement.describe(
426
vertex_data,
427
'vertex',
428
len_types={'rgb': 'u1'}, # 8-bit unsigned for list length
429
val_types={'rgb': 'u1'} # 8-bit unsigned for RGB values
430
)
431
```
432
433
### File Format Control
434
435
```python
436
from plyfile import PlyData, PlyElement
437
438
# Create data
439
element = PlyElement.describe(data, 'vertex')
440
441
# Write as ASCII text format
442
ply_ascii = PlyData([element], text=True)
443
ply_ascii.write('mesh_ascii.ply')
444
445
# Write as binary format with specific byte order
446
ply_binary = PlyData([element], text=False, byte_order='<') # Little-endian
447
ply_binary.write('mesh_binary.ply')
448
```
449
450
### Comments and Metadata
451
452
```python
453
from plyfile import PlyData, PlyElement
454
455
# Add comments and object info to PLY file
456
element = PlyElement.describe(data, 'vertex', comments=['Vertex data'])
457
458
ply_data = PlyData(
459
[element],
460
comments=['Generated by my application', 'Processed on 2024-01-01'],
461
obj_info=['Author: John Doe', 'Version: 1.0']
462
)
463
ply_data.write('annotated_mesh.ply')
464
```