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
Hi, I tried to obtain data from MotionCamera 3D M+ using this code:
but I get an empty cloud, can you please tell me how can I get the data from the payload correctly? thanks