Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

empty pointcloud while obtaining data #5

Open
meldose opened this issue Nov 25, 2024 · 5 comments
Open

empty pointcloud while obtaining data #5

meldose opened this issue Nov 25, 2024 · 5 comments

Comments

@meldose
Copy link

meldose commented Nov 25, 2024

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

@patrikdaniel
Copy link
Contributor

patrikdaniel commented Nov 26, 2024

Hi @meldose,
Could you confirm if the point cloud appears in the PhoXi Control GUI while running your code?

Please note that the GenTL consumer interface file ( photoneo.cti) is no longer actively maintained, and no new features will be added. Instead, we recommend using the GigE Vision-based examples, which are actively supported and offer improved functionality.

You can find helpful resources here:

Let us know if you have any questions or need further assistance!

@meldose
Copy link
Author

meldose commented Nov 26, 2024

i was able to attain the point cloud through the PhoXi Control GUI, : i have updated the code , i also want to get the coordinate X,Y,Z for the object captured! Is it possible?

@meldose
Copy link
Author

meldose commented Nov 27, 2024

i tried running connect_and_grab script but I'm getting this error:

Loading: /opt/mvIMPACT_Acquire/lib/x86_64/libmvGenTLProducer.so
Connecting to: TER-008
Traceback (most recent call last):
  File "/home/hrg/my_venv/lib/python3.8/site-packages/genicam/genapi.py", line 2231, in __getattr__
    return self.get_node(attribute)
  File "/home/hrg/my_venv/lib/python3.8/site-packages/genicam/genapi.py", line 2159, in get_node
    return _genapi.NodeMap_get_node(self, key)
_genapi.LogicalErrorException: Node not existing

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/hrg/git/photoneo_camera_py/src/photoneo_camera_py/Photoneo_gigv.py", line 88, in <module>
    main()
  File "/home/hrg/git/photoneo_camera_py/src/photoneo_camera_py/Photoneo_gigv.py", line 54, in main
    features.UserSetSelector.value = "Default"
  File "/home/hrg/my_venv/lib/python3.8/site-packages/genicam/genapi.py", line 2235, in __getattr__
    raise AttributeError from e
AttributeError

@meldose
Copy link
Author

meldose commented Nov 27, 2024

Guten tag,

I am having doubt regaridng the features that you are using in your code, if possible could you please explain what is happening:

def _setup_trigger(self):
    """
    Configure the camera's trigger settings.
    """
    self.features.TriggerSelector.value = "FrameStart"
    try:
        self.features.TriggerMode.value = "On"
        self.features.TriggerSource.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
        print("Trigger mode set to 'On' with source 'Software'.")
    except Exception as e:
        print(f"Failed to set trigger settings: {e}")

Waiting for your reply
Midhun

@illesD
Copy link
Collaborator

illesD commented Nov 27, 2024

Hi @meldose,
could you please provide the firmware version of the device?
The easiest way to check it is using PhoXi Control. When the device is on the same network, it should just show up in the network discovery window (this does not apply to DirectConnection). Could you post a screenshot of that window?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants