From ae571af59d95b7d39a75502692ee9787e22222d0 Mon Sep 17 00:00:00 2001 From: Vaibhav Awale Date: Tue, 10 Dec 2024 12:11:21 +0100 Subject: [PATCH] Implement point_cloud.copy_image() function This is equivalent to C++ API `PointCloud::copyImageRGBA`, `PointCloud::copyImageBGRA` and `PointCloud::copyImageSRGB`. The function accepts a color format as an argument and returns `Image` object with the specified color format. This is helpful when saving a color image from point cloud data. --- modules/zivid/point_cloud.py | 34 ++++++++++++++++++ src/ReleasablePointCloud.cpp | 12 ++++--- .../ZividPython/ReleasablePointCloud.h | 4 +++ test/test_point_cloud.py | 35 +++++++++++++++++++ 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/modules/zivid/point_cloud.py b/modules/zivid/point_cloud.py index 648f34f9..a152d2d3 100644 --- a/modules/zivid/point_cloud.py +++ b/modules/zivid/point_cloud.py @@ -3,6 +3,7 @@ import numpy import _zivid +from zivid.image import Image class PointCloud: @@ -102,6 +103,39 @@ def copy_data(self, data_format): ) from ex return numpy.array(data_format_class(self.__impl)) + def copy_image(self, data_format): + """Copy the point cloud colors as 8-bit image in input format. + + Supported data formats: + rgba: Image(Height,Width,4) of uint8 + bgra: Image(Height,Width,4) of uint8 + srgb: Image(Height,Width,4) of uint8 + + Args: + data_format: A string specifying the image data format + + Returns: + An image instance containing color data + + Raises: + ValueError: if the requested data format does not exist + """ + self.__impl.assert_not_released() + + supported_color_formats = ["rgba", "bgra", "srgb"] + + if data_format == "rgba": + return Image(self.__impl.copy_image_rgba()) + if data_format == "bgra": + return Image(self.__impl.copy_image_bgra()) + if data_format == "srgb": + return Image(self.__impl.copy_image_srgb()) + raise ValueError( + "Unsupported color format: {data_format}. Supported formats: {all_formats}".format( + data_format=data_format, all_formats=supported_color_formats + ) + ) + def transform(self, matrix): """Transform the point cloud in-place by a 4x4 transformation matrix. diff --git a/src/ReleasablePointCloud.cpp b/src/ReleasablePointCloud.cpp index b7bb684f..bbc40071 100644 --- a/src/ReleasablePointCloud.cpp +++ b/src/ReleasablePointCloud.cpp @@ -1,7 +1,7 @@ +#include #include #include -#include #include @@ -22,9 +22,13 @@ namespace ZividPython [](ReleasablePointCloud &pointCloud, Zivid::PointCloud::Downsampling downsampling) { pointCloud.downsample(downsampling); }) - .def("downsampled", [](ReleasablePointCloud &pointCloud, Zivid::PointCloud::Downsampling downsampling) { - return pointCloud.downsampled(downsampling); - }); + .def("downsampled", + [](ReleasablePointCloud &pointCloud, Zivid::PointCloud::Downsampling downsampling) { + return pointCloud.downsampled(downsampling); + }) + .def("copy_image_rgba", &ReleasablePointCloud::copyImageRGBA) + .def("copy_image_bgra", &ReleasablePointCloud::copyImageBGRA) + .def("copy_image_srgb", &ReleasablePointCloud::copyImageSRGB); py::enum_{ pyClass, "Downsampling" } .value("by2x2", Zivid::PointCloud::Downsampling::by2x2) diff --git a/src/include/ZividPython/ReleasablePointCloud.h b/src/include/ZividPython/ReleasablePointCloud.h index 983bef6f..cb68986d 100644 --- a/src/include/ZividPython/ReleasablePointCloud.h +++ b/src/include/ZividPython/ReleasablePointCloud.h @@ -2,6 +2,7 @@ #include #include +#include #include namespace ZividPython @@ -19,6 +20,9 @@ namespace ZividPython downsampled, Zivid::PointCloud::Downsampling, downsampling) + ZIVID_PYTHON_FORWARD_0_ARGS_WRAP_RETURN(ReleasableImageRGBA, copyImageRGBA) + ZIVID_PYTHON_FORWARD_0_ARGS_WRAP_RETURN(ReleasableImageBGRA, copyImageBGRA) + ZIVID_PYTHON_FORWARD_0_ARGS_WRAP_RETURN(ReleasableImageSRGB, copyImageSRGB) }; void wrapClass(pybind11::class_ pyClass); diff --git a/test/test_point_cloud.py b/test/test_point_cloud.py index e2e55f5c..25d8d2f2 100644 --- a/test/test_point_cloud.py +++ b/test/test_point_cloud.py @@ -103,6 +103,41 @@ def test_point_cloud_rgba(point_cloud): np.testing.assert_array_equal(bgra[:, :, 3], rgba[:, :, 3]) +def test_point_cloud_copy_image(point_cloud): + import numpy as np + import zivid + + image_rgba = point_cloud.copy_image("rgba") + assert isinstance(image_rgba, zivid.Image) + assert image_rgba.height == point_cloud.height + assert image_rgba.width == point_cloud.width + + image_bgra = point_cloud.copy_image("bgra") + assert isinstance(image_bgra, zivid.Image) + assert image_bgra.height == point_cloud.height + assert image_bgra.width == point_cloud.width + + image_srgb = point_cloud.copy_image("srgb") + assert isinstance(image_srgb, zivid.Image) + assert image_srgb.height == point_cloud.height + assert image_srgb.width == point_cloud.width + + rgba = point_cloud.copy_data("rgba") + np.testing.assert_array_equal(image_rgba.copy_data(), rgba) + + bgra = point_cloud.copy_data("bgra") + np.testing.assert_array_equal(image_bgra.copy_data(), bgra) + + srgb = point_cloud.copy_data("srgb") + np.testing.assert_array_equal(image_srgb.copy_data(), srgb) + + # Check errors when argument is wrong or missing + with pytest.raises(ValueError): + point_cloud.copy_image("bogus-format") + with pytest.raises(TypeError): + point_cloud.copy_image() + + def test_point_cloud_normals(point_cloud): import numpy as np