Skip to content

Commit 977b8d2

Browse files
Add bool dtype support for PLY files (#321)
1 parent 9341356 commit 977b8d2

5 files changed

Lines changed: 49 additions & 7 deletions

File tree

pyntcloud/core_class.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def to_instance(self, library, **kwargs):
195195
library = library.upper()
196196
if library not in TO_INSTANCE:
197197
raise ValueError(
198-
"Unsupported library; supported linraries are: {}".format(list(TO_INSTANCE)))
198+
"Unsupported library; supported libraries are: {}".format(list(TO_INSTANCE)))
199199

200200
return TO_INSTANCE[library](self, **kwargs)
201201

pyntcloud/io/ply.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,28 @@
3131
'binary_little_endian': '<'}
3232

3333

34-
def read_ply(filename):
35-
""" Read a .ply (binary or ascii) file and store the elements in pandas DataFrame
34+
def read_ply(filename, allow_bool=False):
35+
""" Read a .ply (binary or ascii) file and store the elements in pandas DataFrame.
36+
3637
Parameters
3738
----------
3839
filename: str
3940
Path to the filename
41+
allow_bool: bool
42+
flag to allow bool as a valid PLY dtype. False by default to mirror original PLY specification.
43+
4044
Returns
4145
-------
4246
data: dict
4347
Elements as pandas DataFrames; comments and ob_info as list of string
4448
"""
49+
if allow_bool:
50+
ply_dtypes[b'bool'] = '?'
4551

4652
with open(filename, 'rb') as ply:
4753

4854
if b'ply' not in ply.readline():
49-
raise ValueError('The file does not start whith the word ply')
55+
raise ValueError('The file does not start with the word ply')
5056
# get binary_little/big or ascii
5157
fmt = ply.readline().split()[1].decode()
5258
# get extension for building the numpy dtypes
@@ -165,7 +171,7 @@ def read_ply(filename):
165171

166172

167173
def write_ply(filename, points=None, mesh=None, as_text=False, comments=None):
168-
"""
174+
"""Write a PLY file populated with the given fields.
169175
170176
Parameters
171177
----------
@@ -242,7 +248,7 @@ def describe_element(name, df):
242248
-------
243249
element: list[str]
244250
"""
245-
property_formats = {'f': 'float', 'u': 'uchar', 'i': 'int'}
251+
property_formats = {'f': 'float', 'u': 'uchar', 'i': 'int', 'b': 'bool'}
246252
element = ['element ' + name + ' ' + str(len(df))]
247253

248254
if name == 'face':

tests/data/diamond_with_bool.ply

420 Bytes
Binary file not shown.

tests/integration/io/test_from_file.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,15 @@ def test_obj_issue_vn(data_path):
8585

8686
assert len(cloud.xyz) == 3
8787
assert len(cloud.mesh) == 1
88+
89+
90+
def test_ply_with_bool(data_path):
91+
"""Expectation: a PLY file that contains bool types can be read into a PyntCloud object."""
92+
TEST_PLY = str(data_path / "diamond_with_bool.ply")
93+
94+
with pytest.raises(KeyError, match="bool"):
95+
cloud = PyntCloud.from_file(TEST_PLY)
96+
97+
cloud = PyntCloud.from_file(filename=TEST_PLY, allow_bool=True)
98+
assert "is_green" in cloud.points.columns, "Failed to find expected Boolean column: 'is_green'"
99+
assert cloud.points.is_green.dtype == bool, "Boolean column no loaded as bool dtype"

tests/integration/io/test_to_file.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,28 @@ def test_to_file(tmpdir, diamond, extension, color, mesh, comments):
3737

3838
def test_to_bin_raises_ValueError_if_invalid_kwargs(tmpdir, diamond):
3939
with pytest.raises(ValueError):
40-
diamond.to_file(str(tmpdir.join("written.bin")), also_save=["mesh"])
40+
diamond.to_file(str(tmpdir.join("written.bin")), also_save=["mesh"])
41+
42+
43+
def test_write_ply_with_bool(plane_pyntcloud, tmp_path):
44+
"""Expectation: a PyntCloud class holding Boolean column within `points` can be written and re-read as a PLY file.
45+
46+
After adding the new column, we have the following DataFrame under plane_pyntcloud.points:
47+
48+
x y z bool_col
49+
0 0.0 0.0 0.0 True
50+
1 1.0 1.0 0.0 True
51+
2 2.0 2.0 0.0 False
52+
3 1.0 2.0 0.0 True
53+
4 0.1 0.2 0.3 True
54+
"""
55+
# Insert the additional column of dtype: bool.
56+
plane_pyntcloud.points["bool_col"] = plane_pyntcloud.points.x < 2
57+
58+
# Write the DataFrame containing Boolean data.
59+
ply_out = (tmp_path / "test_file.ply").as_posix()
60+
plane_pyntcloud.to_file(ply_out)
61+
62+
# Reload the test file and compare it is exactly as it was before writing.
63+
new_pyntcloud = PyntCloud.from_file(ply_out, allow_bool=True)
64+
assert new_pyntcloud.points.equals(plane_pyntcloud.points), "Re-read pyntcloud is not identical to before writing"

0 commit comments

Comments
 (0)