Skip to content

Commit d7e8cb7

Browse files
authored
Merge pull request #303 from zivid/ZIVID-9208-implement-point-cloud-export-api
Implement experimental point cloud export API
2 parents 9df4ffb + 011d396 commit d7e8cb7

File tree

10 files changed

+492
-1
lines changed

10 files changed

+492
-1
lines changed

modules/_zivid/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
Frame,
5555
FrameInfo,
5656
PointCloud,
57+
point_cloud_export,
5758
Settings,
5859
version,
5960
Settings2D,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Module for exporting point cloud data to various formats. This API may change in the future."""
2+
3+
from zivid.experimental.point_cloud_export._export_frame import export_frame
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from zivid.frame import Frame
2+
from zivid.experimental.point_cloud_export.file_format import ZDF, PLY, XYZ, PCD
3+
4+
import _zivid
5+
6+
7+
def export_frame(frame, file_format):
8+
"""Save frame to a file.
9+
10+
The file format is specified by the file_format argument. The file format can be ZDF, PLY, XYZ, or PCD.
11+
12+
If the format is PCD, this function stores the ordered point cloud with a header that indicates an unordered point
13+
cloud. Since SDK 2.5, it is possible to export PCD with correct header by setting
14+
`Configuration/APIBreakingBugFixes/FileFormats/PCD/UseOrganizedFormat` in Config.yml file. See
15+
https://support.zivid.com/en/latest/reference-articles/point-cloud-structure-and-output-formats.html#organized-pcd-format.
16+
17+
Args:
18+
frame: Frame to export.
19+
file_format: File format specification.
20+
21+
Raises:
22+
TypeError: If frame is not a Frame.
23+
TypeError: If file_format is not a file format specification.
24+
"""
25+
if not isinstance(frame, Frame):
26+
raise TypeError(
27+
"Unsupported type for argument frame. Got {}, expected {}".format(
28+
type(frame), Frame
29+
)
30+
)
31+
if not any(
32+
[
33+
isinstance(file_format, ZDF),
34+
isinstance(file_format, PLY),
35+
isinstance(file_format, XYZ),
36+
isinstance(file_format, PCD),
37+
]
38+
):
39+
raise TypeError(
40+
"Unsupported type for argument file_format. Got {}, expected {}".format(
41+
type(file_format),
42+
" or ".join([t.__name__ for t in [ZDF, PLY, XYZ, PCD]]),
43+
)
44+
)
45+
46+
format_impl_attr = f"_{type(file_format).__name__}__impl"
47+
_zivid.point_cloud_export.export_frame(
48+
frame._Frame__impl, # pylint: disable=protected-access
49+
getattr(file_format, format_impl_attr),
50+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
"""Module defining file formats that point cloud data can be exported to."""
2+
3+
import _zivid
4+
5+
6+
class ColorSpace: # pylint: disable=too-few-public-methods
7+
"""Color space for saving point cloud."""
8+
9+
linear_rgb = "linear_rgb"
10+
srgb = "srgb"
11+
12+
@staticmethod
13+
def valid_values():
14+
"""Get valid values for color space.
15+
16+
Returns:
17+
List of valid color spaces.
18+
"""
19+
return [ColorSpace.linear_rgb, ColorSpace.srgb]
20+
21+
@classmethod
22+
def _to_internal(cls, value):
23+
if value == ColorSpace.linear_rgb:
24+
return _zivid.point_cloud_export.ColorSpace.linear_rgb
25+
if value == ColorSpace.srgb:
26+
return _zivid.point_cloud_export.ColorSpace.srgb
27+
raise ValueError(
28+
"Invalid color space '{}'. Valid color spaces are: {}".format(
29+
value, cls.valid_values()
30+
)
31+
)
32+
33+
34+
class ZDF: # pylint: disable=too-few-public-methods
35+
"""Specification for saving frame in ZDF (*.zdf) format."""
36+
37+
def __init__(self, file_name):
38+
"""Create a ZDF file format specification with file name.
39+
40+
Args:
41+
file_name: File name.
42+
43+
Raises:
44+
TypeError: If file_name is not a string.
45+
"""
46+
if not isinstance(file_name, str):
47+
raise TypeError(
48+
"Unsupported type for argument file_name. Got {}, expected {}".format(
49+
type(file_name), str
50+
)
51+
)
52+
self.__impl = _zivid.point_cloud_export.file_format.ZDF(file_name)
53+
54+
def __str__(self):
55+
return str(self.__impl)
56+
57+
58+
class PLY: # pylint: disable=too-few-public-methods
59+
"""Specification for saving frame in PLY (*.ply) format.
60+
61+
PLY is a file format developed at Stanford. To learn more about the PLY file format,
62+
see https://paulbourke.net/dataformats/ply/.
63+
"""
64+
65+
class Layout:
66+
"""Layout for saving point cloud."""
67+
68+
ordered = "ordered"
69+
unordered = "unordered"
70+
71+
@staticmethod
72+
def valid_values():
73+
"""Get valid values for layout.
74+
75+
Returns:
76+
List of valid layouts.
77+
"""
78+
return [PLY.Layout.ordered, PLY.Layout.unordered]
79+
80+
@classmethod
81+
def _to_internal(cls, value):
82+
if value == PLY.Layout.ordered:
83+
return _zivid.point_cloud_export.file_format.PLY.Layout.ordered
84+
if value == PLY.Layout.unordered:
85+
return _zivid.point_cloud_export.file_format.PLY.Layout.unordered
86+
raise ValueError(
87+
"Invalid layout '{}'. Valid layouts are: {}".format(
88+
value, cls.valid_values()
89+
)
90+
)
91+
92+
def __init__(self, file_name, layout=Layout.ordered, color_space=ColorSpace.srgb):
93+
"""Create a PLY file format specification with file name.
94+
95+
Args:
96+
file_name: File name.
97+
layout: Layout of point cloud. Default is ordered.
98+
color_space: Color space of point cloud. Default is sRGB.
99+
100+
Raises:
101+
TypeError: If file_name, layout, or color_space are not strings.
102+
"""
103+
if not isinstance(file_name, str):
104+
raise TypeError(
105+
"Unsupported type for argument file_name. Got {}, expected {}".format(
106+
type(file_name), str
107+
)
108+
)
109+
if not isinstance(layout, str):
110+
raise TypeError(
111+
"Unsupported type for argument layout. Got {}, expected {}".format(
112+
type(layout), str
113+
)
114+
)
115+
if not isinstance(color_space, str):
116+
raise TypeError(
117+
"Unsupported type for argument color_space. Got {}, expected {}".format(
118+
type(color_space), str
119+
)
120+
)
121+
self.__impl = _zivid.point_cloud_export.file_format.PLY(
122+
file_name,
123+
PLY.Layout._to_internal(layout),
124+
ColorSpace._to_internal(color_space),
125+
)
126+
127+
def __str__(self):
128+
return str(self.__impl)
129+
130+
131+
class XYZ: # pylint: disable=too-few-public-methods
132+
"""Specification for saving frame in ASCII (*.xyz) format.
133+
134+
ASCII characters are used to store cartesian coordinates of XYZ points and RGB color values.
135+
"""
136+
137+
def __init__(self, file_name, color_space=ColorSpace.srgb):
138+
"""Create a XYZ file format specification with file name.
139+
140+
Sets color space to linear RGB.
141+
142+
Args:
143+
file_name: File name.
144+
color_space: Color space of point cloud. Default is sRGB.
145+
146+
Raises:
147+
TypeError: If file_name or color_space are not strings.
148+
"""
149+
if not isinstance(file_name, str):
150+
raise TypeError(
151+
"Unsupported type for argument file_name. Got {}, expected {}".format(
152+
type(file_name), str
153+
)
154+
)
155+
if not isinstance(color_space, str):
156+
raise TypeError(
157+
"Unsupported type for argument color_space. Got {}, expected {}".format(
158+
type(color_space), str
159+
)
160+
)
161+
self.__impl = _zivid.point_cloud_export.file_format.XYZ(
162+
file_name, ColorSpace._to_internal(color_space)
163+
)
164+
165+
def __str__(self):
166+
return str(self.__impl)
167+
168+
169+
class PCD: # pylint: disable=too-few-public-methods
170+
"""Specification for saving frame in PCD (*.pcd) format.
171+
172+
PCD is a file format native to the Point Cloud Library (PCL). To learn more about
173+
the PCD file format, see
174+
https://pcl.readthedocs.io/projects/tutorials/en/latest/pcd_file_format.html#pcd-file-format.
175+
"""
176+
177+
def __init__(self, file_name, color_space=ColorSpace.srgb):
178+
"""Create a PCD file format specification with file name.
179+
180+
Args:
181+
file_name: File name.
182+
color_space: Color space of point cloud. Default is sRGB.
183+
184+
Raises:
185+
TypeError: If file_name or color_space are not strings.
186+
"""
187+
if not isinstance(file_name, str):
188+
raise TypeError(
189+
"Unsupported type for argument file_name. Got {}, expected {}".format(
190+
type(file_name), str
191+
)
192+
)
193+
if not isinstance(color_space, str):
194+
raise TypeError(
195+
"Unsupported type for argument color_space. Got {}, expected {}".format(
196+
type(color_space), str
197+
)
198+
)
199+
self.__impl = _zivid.point_cloud_export.file_format.PCD(
200+
file_name,
201+
ColorSpace._to_internal(color_space),
202+
)
203+
204+
def __str__(self):
205+
return str(self.__impl)

setup.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,13 @@ def _main():
135135
author="Zivid AS",
136136
author_email="[email protected]",
137137
license="BSD 3-Clause",
138-
packages=["zivid", "zivid._calibration", "zivid.experimental", "_zivid"],
138+
packages=[
139+
"zivid",
140+
"zivid._calibration",
141+
"zivid.experimental",
142+
"zivid.experimental.point_cloud_export",
143+
"_zivid",
144+
],
139145
package_dir={"": "modules"},
140146
install_requires=["numpy"],
141147
cmake_args=[

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(SOURCES
1212
InfieldCorrection/InfieldCorrection.cpp
1313
NodeType.cpp
1414
PixelMapping.cpp
15+
PointCloudExport.cpp
1516
Projection.cpp
1617
Presets.cpp
1718
ReleasableArray2D.cpp

0 commit comments

Comments
 (0)