Skip to content

empty pointcloud while obtaining data #5

Description

@meldose

Hi, I tried to obtain data from MotionCamera 3D M+ using this code:

import os
from typing import List
import open3d as o3d
from harvesters.core import Harvester, Component2DImage


class PhotoneoCamera:
    """
    A class to interface with a Photoneo camera, allowing software triggering
    and retrieval of various data types such as point cloud, RGB, depth, texture, and normals.
    """

    def __init__(
        self, device_id="PhotoneoTL_DEV_TER-008", cti_env_var="PHOXI_CONTROL_PATH"
    ):
        """
        Initializes the PhotoneoCamera instance by setting up the Harvester,
        loading the CTI file, configuring camera features, and starting the interface.

        :param device_id: The ID of the Photoneo camera device.
        :param cti_env_var: The environment variable that contains the path to the CTI files.
        """
        self.device_id = device_id
        cti_path = os.getenv(cti_env_var)
        if not cti_path:
            raise EnvironmentError(f"Environment variable '{cti_env_var}' is not set.")

        self.cti_file_path = os.path.join(cti_path, "API", "lib", "photoneo.cti")
        if not os.path.isfile(self.cti_file_path):
            raise FileNotFoundError(f"CTI file not found at '{self.cti_file_path}'.")

        # Initialize Harvester and add CTI file
        self.harvester = Harvester()
        self.harvester.add_file(self.cti_file_path, True, True)
        self.harvester.update()

        # Create interface for the specified device
        self.interface = self.harvester.create({"id_": self.device_id})
        if not self.interface:
            raise ValueError(f"No device found with ID '{self.device_id}'.")

        # Access the node map to configure camera features
        self.features = self.interface.remote_device.node_map
        self._configure_features()

        # Start the camera interface
        self.interface.start()
        self.payload = None

    def _configure_features(self):
        """
        Configures the camera features such as trigger mode and data streams.
        """
        # TODO: investigate all these params
        self.features.PhotoneoTriggerMode.value = "Software"
        self.features.SendTexture.value = True
        self.features.SendPointCloud.value = True
        self.features.SendNormalMap.value = True
        self.features.SendDepthMap.value = True
        self.features.SendConfidenceMap.value = True

    def trigger(self):
        """
        Triggers the camera to capture a frame and fetches the payload.

        Raises:
            RuntimeError: If triggering or fetching the frame fails.
        """
        try:
            # Execute the software trigger
            self.features.TriggerFrame.execute()

            # Fetch the buffer with a timeout of 10 seconds
            with self.interface.fetch(timeout=10.0) as buffer:
                self.payload = buffer.payload
                # TODO: remove later
                component_list: List[Component2DImage] = self.payload.components
                for i, component in enumerate(component_list):
                    print(
                        f"Component index: {i}\n"
                        f"DataFormat: {component.data_format}\n"
                        f"Element count per pixel: {component.num_components_per_pixel}\n"
                        f"Width x Height: {component.width} x {component.height}\n"
                        f"Length: {len(component.data)}\n"
                        f"Raw data: {component.data}\n"
                    )
        except Exception as e:
            raise RuntimeError(f"Failed to trigger and fetch data: {e}")

    def get_pointcloud(self):
        """
        Retrieves the point cloud data from the latest payload.

        :return: Point cloud data as a NumPy array or appropriate format.
        :raises ValueError: If no payload is available.
        """
        if self.payload is None:
            raise ValueError("No data available. Please call trigger() first.")

        try:
            point_cloud_component = self.payload.components[2]
            if point_cloud_component.width == 0 or point_cloud_component.height == 0:
                print("Point cloud is empty!")
                return

            point_cloud_data = point_cloud_component.data.reshape(
                point_cloud_component.height, point_cloud_component.width, -1
            )

            xyz = point_cloud_data[:, :, :3]
            xyz = xyz.reshape(-1, 3)
            pcd = o3d.geometry.PointCloud()
            pcd.points = o3d.utility.Vector3dVector(xyz)
            return pcd
        except Exception as ex:
            print(f"PointCloud component not found in payload. {ex}")

    def get_rgb(self):
        """
        Retrieves the RGB texture data from the latest payload.

        :return: RGB data as a NumPy array or appropriate format.
        :raises ValueError: If no payload is available.
        """
        if self.payload is None:
            raise ValueError("No data available. Please call trigger() first.")

        try:
            rgb_component = self.payload.components[1]
            return rgb_component.data
        except Exception as ex:
            print(f"TextureRGB component not found in payload.")

    def get_depth(self):
        """
        Retrieves the depth map data from the latest payload.

        :return: Depth map data as a NumPy array or appropriate format.
        :raises ValueError: If no payload is available.
        """
        if self.payload is None:
            raise ValueError("No data available. Please call trigger() first.")

        try:
            depth_component = self.payload.components[4]
            return depth_component.data
        except Exception as ex:
            print(f"DepthMap component not found in payload. {ex}")

    def get_confidence(self):
        """
        Retrieves the confidence map data from the latest payload.

        :return: Depth map data as a NumPy array or appropriate format.
        :raises ValueError: If no payload is available.
        """
        if self.payload is None:
            raise ValueError("No data available. Please call trigger() first.")

        try:
            depth_component = self.payload.components[5]
            return depth_component.data
        except Exception as ex:
            print(f"DepthMap component not found in payload. {ex}")

    def get_texture(self):
        """
        Retrieves the texture data from the latest payload.

        :return: Texture data as a NumPy array or appropriate format.
        :raises ValueError: If no payload is available.
        """
        if self.payload is None:
            raise ValueError("No data available. Please call trigger() first.")

        try:
            texture_component = self.payload.components[0]
            return texture_component.data
        except Exception as ex:
            print(f"Texture component not found in payload. {ex}")

    def get_normals(self):
        """
        Retrieves the normals map data from the latest payload.

        :return: Normals map data as a NumPy array or appropriate format.
        :raises ValueError: If no payload is available.
        """
        if self.payload is None:
            raise ValueError("No data available. Please call trigger() first.")

        try:
            normals_component = self.payload.components[6]
            return normals_component.data
        except Exception as ex:
            print(f"NormalMap component not found in payload. {ex}")

    def close(self):
        """
        Stops the camera interface and resets the Harvester.
        """
        if self.interface:
            self.interface.stop()
        if self.harvester:
            self.harvester.reset()

    def __enter__(self):
        """
        Enables the use of the class with the 'with' statement.
        """
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """
        Ensures resources are cleaned up when exiting the 'with' block.
        """
        self.close()


if __name__ == "__main__":
    camera = PhotoneoCamera()
    camera.trigger()
    pointcloud = camera.get_pointcloud()
    #
    o3d.visualization.draw_geometries([pointcloud])

but I get an empty cloud, can you please tell me how can I get the data from the payload correctly? thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions