Skip to content

Commit

Permalink
Implement experimental point cloud export API
Browse files Browse the repository at this point in the history
  • Loading branch information
johningve committed Dec 6, 2024
1 parent f11e9b6 commit d54ebeb
Show file tree
Hide file tree
Showing 10 changed files with 492 additions and 1 deletion.
1 change: 1 addition & 0 deletions modules/_zivid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
Frame,
FrameInfo,
PointCloud,
point_cloud_export,
Settings,
version,
Settings2D,
Expand Down
3 changes: 3 additions & 0 deletions modules/zivid/experimental/point_cloud_export/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Module for exporting point cloud data to various formats. This API may change in the future."""

from zivid.experimental.point_cloud_export._export_frame import export_frame
50 changes: 50 additions & 0 deletions modules/zivid/experimental/point_cloud_export/_export_frame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from zivid.frame import Frame
from zivid.experimental.point_cloud_export.file_format import ZDF, PLY, XYZ, PCD

import _zivid


def export_frame(frame, file_format):
"""Save frame to a file.
The file format is specified by the file_format argument. The file format can be ZDF, PLY, XYZ, or PCD.
If the format is PCD, this function stores the ordered point cloud with a header that indicates an unordered point
cloud. Since SDK 2.5, it is possible to export PCD with correct header by setting
`Configuration/APIBreakingBugFixes/FileFormats/PCD/UseOrganizedFormat` in Config.yml file. See
https://support.zivid.com/en/latest/reference-articles/point-cloud-structure-and-output-formats.html#organized-pcd-format.
Args:
frame: Frame to export.
file_format: File format specification.
Raises:
TypeError: If frame is not a Frame.
TypeError: If file_format is not a file format specification.
"""
if not isinstance(frame, Frame):
raise TypeError(
"Unsupported type for argument frame. Got {}, expected {}".format(
type(frame), Frame
)
)
if not any(
[
isinstance(file_format, ZDF),
isinstance(file_format, PLY),
isinstance(file_format, XYZ),
isinstance(file_format, PCD),
]
):
raise TypeError(
"Unsupported type for argument file_format. Got {}, expected {}".format(
type(file_format),
" or ".join([t.__name__ for t in [ZDF, PLY, XYZ, PCD]]),
)
)

format_impl_attr = f"_{type(file_format).__name__}__impl"
_zivid.point_cloud_export.export_frame(
frame._Frame__impl, # pylint: disable=protected-access
getattr(file_format, format_impl_attr),
)
205 changes: 205 additions & 0 deletions modules/zivid/experimental/point_cloud_export/file_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
"""Module defining file formats that point cloud data can be exported to."""

import _zivid


class ColorSpace: # pylint: disable=too-few-public-methods
"""Color space for saving point cloud."""

linear_rgb = "linear_rgb"
srgb = "srgb"

@staticmethod
def valid_values():
"""Get valid values for color space.
Returns:
List of valid color spaces.
"""
return [ColorSpace.linear_rgb, ColorSpace.srgb]

@classmethod
def _to_internal(cls, value):
if value == ColorSpace.linear_rgb:
return _zivid.point_cloud_export.ColorSpace.linear_rgb
if value == ColorSpace.srgb:
return _zivid.point_cloud_export.ColorSpace.srgb
raise ValueError(
"Invalid color space '{}'. Valid color spaces are: {}".format(
value, cls.valid_values()
)
)


class ZDF: # pylint: disable=too-few-public-methods
"""Specification for saving frame in ZDF (*.zdf) format."""

def __init__(self, file_name):
"""Create a ZDF file format specification with file name.
Args:
file_name: File name.
Raises:
TypeError: If file_name is not a string.
"""
if not isinstance(file_name, str):
raise TypeError(
"Unsupported type for argument file_name. Got {}, expected {}".format(
type(file_name), str
)
)
self.__impl = _zivid.point_cloud_export.file_format.ZDF(file_name)

def __str__(self):
return str(self.__impl)


class PLY: # pylint: disable=too-few-public-methods
"""Specification for saving frame in PLY (*.ply) format.
PLY is a file format developed at Stanford. To learn more about the PLY file format,
see https://paulbourke.net/dataformats/ply/.
"""

class Layout:
"""Layout for saving point cloud."""

ordered = "ordered"
unordered = "unordered"

@staticmethod
def valid_values():
"""Get valid values for layout.
Returns:
List of valid layouts.
"""
return [PLY.Layout.ordered, PLY.Layout.unordered]

@classmethod
def _to_internal(cls, value):
if value == PLY.Layout.ordered:
return _zivid.point_cloud_export.file_format.PLY.Layout.ordered
if value == PLY.Layout.unordered:
return _zivid.point_cloud_export.file_format.PLY.Layout.unordered
raise ValueError(
"Invalid layout '{}'. Valid layouts are: {}".format(
value, cls.valid_values()
)
)

def __init__(self, file_name, layout=Layout.ordered, color_space=ColorSpace.srgb):
"""Create a PLY file format specification with file name.
Args:
file_name: File name.
layout: Layout of point cloud. Default is ordered.
color_space: Color space of point cloud. Default is sRGB.
Raises:
TypeError: If file_name, layout, or color_space are not strings.
"""
if not isinstance(file_name, str):
raise TypeError(
"Unsupported type for argument file_name. Got {}, expected {}".format(
type(file_name), str
)
)
if not isinstance(layout, str):
raise TypeError(
"Unsupported type for argument layout. Got {}, expected {}".format(
type(layout), str
)
)
if not isinstance(color_space, str):
raise TypeError(
"Unsupported type for argument color_space. Got {}, expected {}".format(
type(color_space), str
)
)
self.__impl = _zivid.point_cloud_export.file_format.PLY(
file_name,
PLY.Layout._to_internal(layout),
ColorSpace._to_internal(color_space),
)

def __str__(self):
return str(self.__impl)


class XYZ: # pylint: disable=too-few-public-methods
"""Specification for saving frame in ASCII (*.xyz) format.
ASCII characters are used to store cartesian coordinates of XYZ points and RGB color values.
"""

def __init__(self, file_name, color_space=ColorSpace.srgb):
"""Create a XYZ file format specification with file name.
Sets color space to linear RGB.
Args:
file_name: File name.
color_space: Color space of point cloud. Default is sRGB.
Raises:
TypeError: If file_name or color_space are not strings.
"""
if not isinstance(file_name, str):
raise TypeError(
"Unsupported type for argument file_name. Got {}, expected {}".format(
type(file_name), str
)
)
if not isinstance(color_space, str):
raise TypeError(
"Unsupported type for argument color_space. Got {}, expected {}".format(
type(color_space), str
)
)
self.__impl = _zivid.point_cloud_export.file_format.XYZ(
file_name, ColorSpace._to_internal(color_space)
)

def __str__(self):
return str(self.__impl)


class PCD: # pylint: disable=too-few-public-methods
"""Specification for saving frame in PCD (*.pcd) format.
PCD is a file format native to the Point Cloud Library (PCL). To learn more about
the PCD file format, see
https://pcl.readthedocs.io/projects/tutorials/en/latest/pcd_file_format.html#pcd-file-format.
"""

def __init__(self, file_name, color_space=ColorSpace.srgb):
"""Create a PCD file format specification with file name.
Args:
file_name: File name.
color_space: Color space of point cloud. Default is sRGB.
Raises:
TypeError: If file_name or color_space are not strings.
"""
if not isinstance(file_name, str):
raise TypeError(
"Unsupported type for argument file_name. Got {}, expected {}".format(
type(file_name), str
)
)
if not isinstance(color_space, str):
raise TypeError(
"Unsupported type for argument color_space. Got {}, expected {}".format(
type(color_space), str
)
)
self.__impl = _zivid.point_cloud_export.file_format.PCD(
file_name,
ColorSpace._to_internal(color_space),
)

def __str__(self):
return str(self.__impl)
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,13 @@ def _main():
author="Zivid AS",
author_email="[email protected]",
license="BSD 3-Clause",
packages=["zivid", "zivid._calibration", "zivid.experimental", "_zivid"],
packages=[
"zivid",
"zivid._calibration",
"zivid.experimental",
"zivid.experimental.point_cloud_export",
"_zivid",
],
package_dir={"": "modules"},
install_requires=["numpy"],
cmake_args=[
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(SOURCES
InfieldCorrection/InfieldCorrection.cpp
NodeType.cpp
PixelMapping.cpp
PointCloudExport.cpp
Projection.cpp
Presets.cpp
ReleasableArray2D.cpp
Expand Down
Loading

0 comments on commit d54ebeb

Please sign in to comment.