1from __future__
import annotations
4from enum
import IntEnum
5from typing
import Union, TypeAlias, Callable, Iterable, Any, cast, Type, TYPE_CHECKING
8import numpy.typing
as npt
10from ids_peak_ipl
import ids_peak_ipl
12from ids_peak_icv
import lib_loader
26XYZArray: TypeAlias = npt.NDArray[np.object_]
27XYZIArray: TypeAlias = npt.NDArray[np.object_]
33 Enumeration representing different types of point cloud points.
36 XYZ: Represents a point with X, Y and Z coordinates.
37 XYZ_I8: Represents a point with X, Y, Z coordinates and 8-bit intensity.
38 XYZ_I10: Represents a point with X, Y, Z coordinates and 10-bit intensity.
39 XYZ_I12: Represents a point with X, Y, Z coordinates and 12-bit intensity.
41 @ingroup ids_peak_icv_python_types
42 @since ids_peak_icv 1.1
53 A point cloud is a set of data points defined in a three-dimensional (3D) coordinate system.
55 Each point is represented by its X, Y, and Z coordinates,
56 specifying its exact position in 3D space. Optionally it can contain intensity metadata.
57 Point clouds are often generated from depth maps,
58 such as those obtained from a time-of-flight camera.
59 They serve as a detailed digital representation of the shape and size of physical objects,
60 enabling precise measurements and analysis.
62 @ingroup ids_peak_icv_python_types
63 @since ids_peak_icv 1.1
68 intensity_image: Union[Image, ids_peak_ipl.Image,
None] =
None) -> PointCloud:
71 Creates a point cloud from a xyz image and optional metadata.
73 @note The region of the metadata image is disregarded.
75 @param xyz_image: XYZ image providing the spatial information.
76 @param intensity_image: Image that holds the meta information (e.g. an intensity image).
77 @return Created point cloud instance
79 @exception MismatchException: If the xyz_image and the intensity image don't have the same size.
81 @since ids_peak_icv 1.1
83 return cls(_xyz_image=xyz_image, _intensity_image=intensity_image)
87 intensity_image: Union[
88 Image, ids_peak_ipl.Image,
None] =
None) -> PointCloud:
91 Creates a point cloud from an undistorted depth map and optional metadata.
93 @note The region of the metadata image is disregarded.
95 @param undistorted_depth_map: Undistorted image providing the radial depth information.
96 @param intensity_image: Image that holds the meta information (e.g. an intensity image).
97 @return Created point cloud instance
99 @exception MismatchException: If the undistorted_depth_map and the intensity image don't have the same size.
101 @since ids_peak_icv 1.1
103 return cls(_xyz_image=XYZImage.create_from_undistorted_image(undistorted_depth_map),
104 _intensity_image=intensity_image)
108 point_type: PointCloudPointType) -> PointCloud:
111 Creates a point cloud from points.
113 @param points: An array of PointXYZ or PointXYZI.
114 @param point_type: The type of point.
116 @return Created point cloud instance
118 @exception MismatchException: If Parameter points is not a numpy array of PointXYZ or PointXYZI objects
119 matching the point_type.
121 @since ids_peak_icv 1.1
125 c_point_type: Type[peak_icv_point_xyz] | Type[peak_icv_point_xyzi]
126 if point_type == PointCloudPointType.XYZ:
127 c_point_type = peak_icv_point_xyz
128 elif (point_type == PointCloudPointType.XYZ_I8
or
129 point_type == PointCloudPointType.XYZ_I10
or
130 point_type == PointCloudPointType.XYZ_I12):
131 c_point_type = peak_icv_point_xyzi
136 c_points = create_array(c_point_type, size)
138 for i, p
in enumerate(points):
139 if isinstance(p, PointXYZI)
and isinstance(points,
140 np.ndarray)
and point_type != PointCloudPointType.XYZ:
142 elif isinstance(p, PointXYZ)
and isinstance(points,
143 np.ndarray)
and point_type == PointCloudPointType.XYZ:
147 "Parameter points has to be a numpy array of PointXYZ or PointXYZI objects and "
148 "the point_type has to match it.")
150 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_CreateFromPoints,
151 ctypes.pointer(handle), c_points, ctypes.c_size_t(size),
152 ctypes.c_int(point_type))
154 return cls(_handle=handle, _point_type=point_type)
158 point_type: PointCloudPointType = PointCloudPointType.XYZ) -> PointCloud:
161 Loads a point cloud from a file.
163 The chosen file type is determined by the given extension.
165 The following file formats are supported:
168 @param file_path: The file is loaded from the file path, which should encompass both the directory path and the
170 @param point_type: Defines whether the point cloud is loaded with or without meta information and how the meta
171 information is interpreted.
173 @return The point cloud, which was loaded from the file.
175 @exception IOException: If the specified file path is invalid, you lack the necessary permissions, or the file
178 @since ids_peak_icv 1.1
182 c_file_path = file_path.encode(
'utf-8')
183 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_CreateFromFile,
184 ctypes.pointer(handle),
185 ctypes.c_int(point_type),
186 ctypes.c_char_p(c_file_path),
187 ctypes.c_size_t(len(c_file_path)))
189 return PointCloud(_handle=handle, _point_type=point_type)
191 def __init__(self, *args: Any, **kwargs: Any) ->
None:
194 In order to create a point cloud, please refer to the class methods.
196 @since ids_peak_icv 1.1
204 if "_handle" in kwargs:
206 self.
_handle = kwargs[
"_handle"]
208 elif "_xyz_image" not in kwargs:
210 "Cannot initialize {} with __init__. Please use a classmethod.".format(self.__class__.__name__))
212 xyz_image = _check_and_get_icv_image(kwargs[
"_xyz_image"])
213 if "_intensity_image" in kwargs:
214 meta_data = _check_and_get_icv_image(kwargs[
"_intensity_image"])
218 xyz_image_handle = xyz_image._handle
223 if meta_data
is None:
224 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_CreateFromXYZImage,
225 ctypes.pointer(self.
_handle), xyz_image_handle)
227 image_handle = meta_data._handle
228 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_CreateFromXYZImageAndIntensityImage,
229 ctypes.pointer(self.
_handle), xyz_image_handle, image_handle)
230 except Exception
as e:
241 Applies a transformation of a point cloud based on the given matrix.
243 @param matrix: The transformation matrix
244 @return A new transformed point cloud.
246 @exception NotPossibleException: If the dtype of numpy array isn't type float32 or the matrix size isn't 4x4.
248 @since ids_peak_icv 1.1
250 if matrix.dtype != np.float32:
252 "Matrix has wrong numpy array type. dtype has to be float32.")
254 if matrix.shape != (4, 4):
256 f
"Matrix has wrong shape {matrix.shape}. Shape has to be (4,4).")
259 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_Create,
260 ctypes.pointer(handle))
261 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_Transform,
262 self.
_handle, matrix.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
263 ctypes.pointer(handle))
270 Transforms a point cloud to the workspace coordinate system using the extrinsic parameters.
272 The point cloud is transformed from the camera coordinate system to the workspace coordinate system
273 using the inverse of the extrinsic parameters.
275 @param extrinsic_parameters: Extrinsic parameters of the workspace calibration.
276 @return A new transformed point cloud.
278 @since ids_peak_icv 1.1
282 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_Create,
283 ctypes.pointer(handle))
284 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_TransformToWorkspace,
285 self.
_handle, extrinsic_parameters._parameters,
286 ctypes.sizeof(extrinsic_parameters._parameters),
287 ctypes.pointer(handle))
292 def points(self) -> Union[XYZArray, XYZIArray]:
295 Get the points of the point cloud.
297 @return List of Point objects representing the point clouds points.
299 @since ids_peak_icv 1.1
302 return cast(Union[XYZArray, XYZIArray], self.
_points)
306 lambda c_points: (
PointXYZ(p.x, p.y, p.z)
for p
in c_points))
307 elif (self.
_point_type == PointCloudPointType.XYZ_I8
or
311 lambda c_points: (
PointXYZI(p.x, p.y, p.z, p.i)
for p
in c_points))
315 return cast(Union[XYZArray, XYZIArray], self.
_points)
320 Check if two point clouds are equal.
322 @param other: Another point cloud object
324 @return True if point clouds are equal, False otherwise
326 @since ids_peak_icv 1.1
328 if isinstance(other, PointCloud):
329 if self.
_handle == other._handle:
335 if other._sorted_points_cache
is None:
336 other._sorted_points_cache = np.sort(other.points, axis=
None)
340 if isinstance(other, list)
or isinstance(other, tuple):
347 other_points = [
PointXYZ(p[0], p[1], p[2])
for p
in other]
348 sorted_other_points = sorted(other_points)
360 Check if two point clouds are not equal.
362 @param other: Another point cloud object
364 @return True if point clouds are not equal, False otherwise
366 @since ids_peak_icv 1.1
368 return not self.
__eq__(other)
372 lib_loader.dll().peak_icv_PointCloud_Destroy(self.
_handle)
377 Get a string representation of the Point Cloud represented by points.
379 @return String representation of the Point Cloud
381 @since ids_peak_icv 1.1
383 return f
"({', '.join(map(str, self.points))})"
385 def save(self, file_path: str) ->
None:
388 Saves point cloud to file.
390 The chosen file type is determined by the given extension.
392 The following file formats are supported:
395 If no file extension is provided, .ply is automatically added.
397 @note If a point cloud does not contain any points it cannot be written to file.
399 @param file_path: The file is saved to the file_path, which should encompass both the directory path and the
402 @exception IOException: If the specified file path is invalid or lacks write permissions.
403 @exception NotSupportedException: If the specified file extension or point type is not supported or the point cloud is empty.
405 @since ids_peak_icv 1.1
407 c_file_path = file_path.encode(
'utf-8')
408 lib_loader.dll().peak_icv_PointCloud_SaveToFile(self.
_handle,
409 ctypes.c_char_p(c_file_path),
410 ctypes.c_size_t(len(c_file_path)))
412 def _init_points(self, c_type: Type[ctypes.Structure], python_type: Type,
413 func: Callable[[ctypes.Array], Iterable[Any]]) ->
None:
415 c_num_points = ctypes.c_size_t()
416 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_GetPoints_GetCount,
417 self.
_handle, ctypes.pointer(c_num_points))
419 if c_num_points.value == 0:
423 c_size_in_bytes_points = ctypes.c_size_t()
424 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_GetPoints_GetSizeInBytes,
425 self.
_handle, ctypes.pointer(c_size_in_bytes_points))
427 c_points = create_array(c_type, c_num_points.value)
428 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_GetPoints,
429 self.
_handle, c_points, c_size_in_bytes_points)
430 iterable = func(c_points)
432 self.
_points = np.empty(len(c_points), dtype=python_type)
434 for i, it
in enumerate(iterable):
438 point_type = ctypes.c_int()
439 execute_and_map_return_codes(lib_loader.dll().peak_icv_PointCloud_GetType, self.
_handle,
440 ctypes.pointer(point_type))
Represents a point in 3D space.
Represents a point in 3D space.
A point cloud is a set of data points defined in a three-dimensional (3D) coordinate system.
PointCloud transform_to_workspace(self, ExtrinsicParameters extrinsic_parameters)
Transforms a point cloud to the workspace coordinate system using the extrinsic parameters.
None __init__(self, *Any args, **Any kwargs)
In order to create a point cloud, please refer to the class methods.
None save(self, str file_path)
Saves point cloud to file.
bool __eq__(self, object other)
Check if two point clouds are equal.
None _init_point_type(self)
PointCloud create_from_xyz_image(cls, XYZImage xyz_image, Union[Image, ids_peak_ipl.Image, None] intensity_image=None)
Creates a point cloud from a xyz image and optional metadata.
PointCloud create_from_file(cls, str file_path, PointCloudPointType point_type=PointCloudPointType.XYZ)
Loads a point cloud from a file.
PointCloud transform(self, npt.NDArray matrix)
Applies a transformation of a point cloud based on the given matrix.
Union[XYZArray, XYZIArray] points(self)
Get the points of the point cloud.
None _init_points(self, Type[ctypes.Structure] c_type, Type python_type, Callable[[ctypes.Array], Iterable[Any]] func)
list _sorted_points_cache
PointCloud create_from_points(cls, Union[XYZArray, XYZIArray] points, PointCloudPointType point_type)
Creates a point cloud from points.
str __repr__(self)
Get a string representation of the Point Cloud represented by points.
bool __ne__(self, object other)
Check if two point clouds are not equal.
PointCloud create_from_undistorted_depth_map(cls, UndistortedImage undistorted_depth_map, Union[Image, ids_peak_ipl.Image, None] intensity_image=None)
Creates a point cloud from an undistorted depth map and optional metadata.
np.ndarray|None _sorted_points_cache
Enumeration representing different types of point cloud points.
Thrown when input parameters do not match the expected conditions.
Thrown when an operation cannot be performed due to logical constraints.
Thrown when a requested operation is not implemented or supported.