plyfile
The `plyfile` Python module provides a simple, NumPy-based facility for reading and writing ASCII and binary PLY files, a common format for storing 3D surface meshes. It is currently at version 1.1.3 (released October 21, 2025) and maintains an active development status with several releases per year, as evidenced by its changelog and PyPI activity.
Warnings
- breaking As of `plyfile` version 1.1 (and later), official support for Python versions older than 3.9 and NumPy versions older than 1.21 has been removed. Users on older environments may encounter compatibility issues.
- gotcha Memory mapping (`mmap` argument in `PlyData.read`) for faster binary data parsing has limitations with list properties. Elements containing *variable-length* list properties cannot be memory-mapped. For *fixed-length* list properties, the `known_list_len` dictionary argument is mandatory during `PlyData.read` to enable memory mapping.
- gotcha When performing I/O operations, `plyfile` differentiates between text-mode and binary-mode streams. Text-mode streams (e.g., those returned by `sys.stdin` or `sys.stdout` or `open('file.ply', 'r')`) are only compatible with ASCII-format PLY files. Binary-mode streams (`open('file.ply', 'rb')` or `open('file.ply', 'wb')`) are required for all PLY file formats (ASCII and binary). Writing a binary-format PLY to a text stream will raise a `ValueError`.
- gotcha When creating PLY files using `plyfile`, there are restrictions on the data types supported by NumPy structured arrays that can be directly mapped to PLY properties. For instance, the PLY format does not directly support 64-bit integer or complex data types. While non-scalar fields are allowed and will be serialized as list properties, users should be mindful of these underlying PLY format limitations.
- gotcha When reading a PLY file, even if a non-scalar (e.g., fixed-size array) field was serialized as a list property, `plyfile` will represent it as an `object`-typed field in the NumPy structured array, where each 'object' is itself a NumPy array (e.g., `('vertex_indices', 'O')`). This means list properties are not automatically flattened into a 2D array upon read, requiring manual post-processing if that's the desired format.
Install
-
pip install plyfile
Imports
- PlyData
from plyfile import PlyData
- PlyElement
from plyfile import PlyElement
Quickstart
import numpy as np
from plyfile import PlyData, PlyElement
import os
# 1. Prepare data for a PLY file
vertices = np.array([
(0.0, 0.0, 0.0, 255, 0, 0),
(1.0, 0.0, 0.0, 0, 255, 0),
(0.0, 1.0, 0.0, 0, 0, 255),
(1.0, 1.0, 0.0, 255, 255, 0)
], dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4'), ('red', 'u1'), ('green', 'u1'), ('blue', 'u1')])
faces = np.array([
([0, 1, 2],),
([1, 3, 2],)
], dtype=[('vertex_indices', 'i4', (3,))])
# 2. Create PlyElement instances
vertex_element = PlyElement.describe(vertices, 'vertex')
face_element = PlyElement.describe(faces, 'face')
# 3. Create a PlyData instance
plydata_to_write = PlyData([vertex_element, face_element], text=True, comments=['Created by plyfile quickstart'])
output_filename = 'quickstart_output.ply'
# 4. Write the PLY data to a file
with open(output_filename, 'wb') as f:
plydata_to_write.write(f)
print(f"Successfully wrote PLY file: {output_filename}")
# 5. Read the PLY data from the file
with open(output_filename, 'rb') as f:
plydata_read = PlyData.read(f)
print("\nSuccessfully read PLY file:")
print(plydata_read)
print("Vertices:\n", plydata_read['vertex'].data)
print("Faces:\n", plydata_read['face'].data)
# Clean up the created file
os.remove(output_filename)
print(f"Cleaned up {output_filename}")