IDS peak ICV v1.3.0
Loading...
Searching...
No Matches
utils.py
Go to the documentation of this file.
1from __future__ import annotations
2import atexit
3import ctypes
4import os
5import site
6import sys
7from ctypes import c_double
8from typing import Callable, Type, TypeVar, Sequence, Any, MutableSequence, Union
9
10from ids_peak_common import Metadata, MetadataKey, Rectangle, Point, Size
11from ids_peak_icv.backend.datatypes import (peak_icv_status, peak_icv_point_f, peak_icv_point_type,
12 peak_icv_capture_information)
13from ids_peak_icv.exceptions import (InvalidConfigurationException, MismatchException, NotSupportedException,
14 NotPossibleException, OutOfRangeException, MathErrorException,
15 TargetNotFoundException, CorruptedException, IOException, InternalErrorException,
16 ICVException, NullPointerException)
17from numpy.typing import NDArray
18
19
20def create_array(c_type: Any, size: int) -> ctypes.Array:
21 array_class: Type[ctypes.Array] = c_type * size
22 return array_class()
23
24
25def metadata_to_capture_information(metadata: Metadata, size: Size | None = None) -> peak_icv_capture_information:
26 c_capture_information = peak_icv_capture_information()
27
28 if metadata.has_entry_by_key(MetadataKey.DEVICE_TIMESTAMP):
29 c_capture_information.relative_timestamp = metadata.get_value_by_key(MetadataKey.DEVICE_TIMESTAMP)
30 else:
31 c_capture_information.relative_timestamp = 0
32 if metadata.has_entry_by_key(MetadataKey.BINNING_HORIZONTAL):
33 c_capture_information.binning_factor.x = metadata.get_value_by_key(MetadataKey.BINNING_HORIZONTAL)
34 else:
35 c_capture_information.binning_factor.x = 1
36 if metadata.has_entry_by_key(MetadataKey.BINNING_VERTICAL):
37 c_capture_information.binning_factor.y = metadata.get_value_by_key(MetadataKey.BINNING_VERTICAL)
38 else:
39 c_capture_information.binning_factor.y = 1
40 if metadata.has_entry_by_key(MetadataKey.ROI):
41 roi = metadata.get_value_by_key(MetadataKey.ROI)
42 c_capture_information.region_of_interest.x = ctypes.c_uint32(roi.x)
43 c_capture_information.region_of_interest.y = ctypes.c_uint32(roi.y)
44 c_capture_information.region_of_interest.width = ctypes.c_uint32(roi.width)
45 c_capture_information.region_of_interest.height = ctypes.c_uint32(roi.height)
46 else:
47 c_capture_information.region_of_interest.x = 0
48 c_capture_information.region_of_interest.y = 0
49
50 if size is not None:
51 c_capture_information.region_of_interest.width = size.width
52 c_capture_information.region_of_interest.height = size.height
53 else:
54 c_capture_information.region_of_interest.width = 0
55 c_capture_information.region_of_interest.height = 0
56 if metadata.has_entry_by_key(MetadataKey.DEVICE_EXPOSURE_TIME):
57 c_capture_information.exposure_time = metadata.get_value_by_key(MetadataKey.DEVICE_EXPOSURE_TIME)
58 else:
59 c_capture_information.exposure_time = 0
60 if metadata.has_entry_by_key(MetadataKey.DEVICE_EXPOSURE_TIME_SEQUENCE):
61 exposure_time_sequence = metadata.get_value_by_key(MetadataKey.DEVICE_EXPOSURE_TIME_SEQUENCE)
62
63 if len(exposure_time_sequence) > 4:
64 raise NotSupportedException("Exposure time sequence has to be smaller than or equal to 4.")
65 exposure_time_sequence_padded = list(exposure_time_sequence) + [0.0] * (4 - len(exposure_time_sequence))
66
67 c_array = c_double * 4
68 c_capture_information.exposure_time_sequence = c_array(*exposure_time_sequence_padded)
69
70 else:
71 c_capture_information.exposure_time_sequence = (0, 0, 0, 0)
72
73 if metadata.has_entry_by_key(MetadataKey.DEVICE_GAIN):
74 c_capture_information.gain = metadata.get_value_by_key(MetadataKey.DEVICE_GAIN)
75 else:
76 c_capture_information.gain = 0
77 if metadata.has_entry_by_key(MetadataKey.DEVICE_GAIN_SEQUENCE):
78 gain_sequence = metadata.get_value_by_key(MetadataKey.DEVICE_GAIN_SEQUENCE)
79
80 if len(gain_sequence) > 4:
81 raise NotSupportedException("Gain sequence has to be smaller than or equal to 4.")
82 gain_sequence_padded = list(gain_sequence) + [0.0] * (4 - len(gain_sequence))
83
84 c_array = c_double * 4
85 c_capture_information.gain_sequence = c_array(*gain_sequence_padded)
86
87 else:
88 c_capture_information.gain_sequence = (0, 0, 0, 0)
89
90 return c_capture_information
91
92
93def metadata_from_capture_information(capture_information: peak_icv_capture_information) -> Metadata:
94 return add_metadata_from_capture_information_to_metadata(Metadata(), capture_information)
95
96
98 capture_information: peak_icv_capture_information) -> Metadata:
99 metadata.set_value_by_key(MetadataKey.DEVICE_TIMESTAMP, capture_information.relative_timestamp)
100 metadata.set_value_by_key(MetadataKey.BINNING_HORIZONTAL, capture_information.binning_factor.x)
101 metadata.set_value_by_key(MetadataKey.BINNING_VERTICAL, capture_information.binning_factor.y)
102 metadata.set_value_by_key(MetadataKey.ROI, Rectangle(Point(capture_information.region_of_interest.x,
103 capture_information.region_of_interest.y),
104 Size(capture_information.region_of_interest.width,
105 capture_information.region_of_interest.height)))
106 if not capture_information.exposure_time == 0:
107 metadata.set_value_by_key(MetadataKey.DEVICE_EXPOSURE_TIME, capture_information.exposure_time)
108
109 exposure_time_sequence = []
110 for v in list(capture_information.exposure_time_sequence):
111 if v == 0:
112 break
113 exposure_time_sequence.append(v)
114 if exposure_time_sequence:
115 metadata.set_value_by_key(MetadataKey.DEVICE_EXPOSURE_TIME_SEQUENCE,
116 list(capture_information.exposure_time_sequence))
117
118 if not capture_information.gain == 0:
119 metadata.set_value_by_key(MetadataKey.DEVICE_GAIN, capture_information.gain)
120
121 gain_sequence = []
122 for v in list(capture_information.gain_sequence):
123 if v == 0:
124 break
125 gain_sequence.append(v)
126 if gain_sequence:
127 metadata.set_value_by_key(MetadataKey.DEVICE_GAIN_SEQUENCE,
128 list(capture_information.gain_sequence))
129
130 return metadata
131
132
133def image_handle_to_ctypes(image_handle: int) -> ctypes.c_void_p:
134 return ctypes.c_void_p(int(image_handle))
135
136
137def map_point_type_to_native_type(point_type: Type) -> Type:
138 if point_type == peak_icv_point_f:
139 return float
140
142 "Point type not supported.")
143
144
145def map_point_type_to_enum(point_type: Type) -> peak_icv_point_type:
146 if point_type == peak_icv_point_f:
147 return peak_icv_point_type.PEAK_ICV_POINT_TYPE_XY
148
150 "Point type not supported.")
151
152
153def map_enum_to_point_type(point_enum: peak_icv_point_type) -> Type:
154 if point_enum == peak_icv_point_type.PEAK_ICV_POINT_TYPE_XY:
155 return peak_icv_point_f
156
158 "point_enum not supported " + str(point_enum.value))
159
160
161T = TypeVar('T', bound=Any)
162InputType = Union[Sequence[T], NDArray[T], ctypes.Array[Any]]
163OutputType = Union[MutableSequence[T], NDArray[T], ctypes.Array[Any]]
164
165
166def transform(input_: InputType, output: OutputType, func: Callable) -> None:
167 for i in range(len(input_)):
168 output[i] = func(input_[i])
169
170
171def execute_and_map_return_codes(func: Callable, *args: Any, **kwargs: Any) -> None:
172 def execute_and_map_return_codes_light(func_light: Callable, *args_light: Any, **kwargs_light: Any) -> None:
173 ec = func_light(*args_light, **kwargs_light)
174 ec = peak_icv_status(ec)
175
176 if ec != peak_icv_status.PEAK_ICV_STATUS_SUCCESS:
177 raise ICVException("Could not query the last error!", ec)
178
179 error_code = func(*args, **kwargs)
180
181 try:
182 error_code = peak_icv_status(error_code)
183 except ValueError:
184 # ignore key error here and throw the exception later, which then contains the error message
185 pass
186
187 if error_code != peak_icv_status.PEAK_ICV_STATUS_SUCCESS:
188
189 c_count = ctypes.c_size_t()
190 execute_and_map_return_codes_light(
191 Loader().dll().peak_icv_GetLastErrorMessage_GetCount,
192 ctypes.pointer(c_count))
193
194 if c_count.value == 0:
195 exception_msg = ""
196 else:
197 c_array = create_array(ctypes.c_char, c_count.value)
198 execute_and_map_return_codes_light(Loader().dll().peak_icv_GetLastErrorMessage,
199 c_array, c_count)
200 exception_msg = c_array.value
201
202 if error_code in {
203 peak_icv_status.PEAK_ICV_STATUS_LIBRARY_NOT_INITIALIZED,
204 peak_icv_status.PEAK_ICV_STATUS_DYNAMIC_DEPENDENCY_MISSING,
205 }:
206 raise InvalidConfigurationException(exception_msg, error_code)
207
208 elif error_code == peak_icv_status.PEAK_ICV_STATUS_NULL_POINTER:
209 raise NullPointerException(exception_msg)
210
211 elif error_code == peak_icv_status.PEAK_ICV_STATUS_MISMATCH:
212 raise MismatchException(exception_msg)
213
214 elif error_code == peak_icv_status.PEAK_ICV_STATUS_NOT_SUPPORTED:
215 raise NotSupportedException(exception_msg)
216
217 elif error_code == peak_icv_status.PEAK_ICV_STATUS_NOT_POSSIBLE:
218 raise NotPossibleException(exception_msg)
219
220 elif error_code == peak_icv_status.PEAK_ICV_STATUS_OUT_OF_RANGE:
221 raise OutOfRangeException(exception_msg)
222
223 elif error_code == peak_icv_status.PEAK_ICV_STATUS_MATH_ERROR:
224 raise MathErrorException(exception_msg)
225
226 elif error_code == peak_icv_status.PEAK_ICV_STATUS_TARGET_NOT_FOUND:
227 raise TargetNotFoundException(exception_msg)
228
229 elif error_code == peak_icv_status.PEAK_ICV_STATUS_CORRUPTED:
230 raise CorruptedException(exception_msg)
231
232 elif error_code == peak_icv_status.PEAK_ICV_STATUS_IO_ERROR:
233 raise IOException(exception_msg)
234
235 elif error_code in {
236 peak_icv_status.PEAK_ICV_STATUS_INTERNAL_ERROR,
237 peak_icv_status.PEAK_ICV_STATUS_INVALID_BUFFER_SIZE,
238 peak_icv_status.PEAK_ICV_STATUS_INVALID_HANDLE,
239 }:
240 raise InternalErrorException(exception_msg)
241 else:
242 raise ICVException(exception_msg, error_code)
243
244
246 for path in site.getsitepackages():
247 package_path = os.path.join(path, "ids_peak_ipl")
248 if os.path.isdir(package_path):
249 return package_path
250 raise InvalidConfigurationException("ids_peak_ipl.dll not found.",
251 peak_icv_status.PEAK_ICV_STATUS_DYNAMIC_DEPENDENCY_MISSING)
252
253
254class Loader:
255 _instance = None
256 _dll: ctypes.CDLL | None = None
257
258 def __new__(cls) -> Loader:
259 if cls._instance is None:
260 cls._instance = super(Loader, cls).__new__(cls)
261 file_dir = os.path.dirname(os.path.realpath(__file__))
262
263 if sys.platform.startswith('win'):
264 lib_name = "ids_peak_icv.dll"
265 dll_path = os.path.join(file_dir, "..")
266 os.add_dll_directory(dll_path)
267 ids_peak_ipl_dll_path = find_ids_peak_ipl_dll()
268 os.add_dll_directory(ids_peak_ipl_dll_path)
269 else:
270 lib_name = "libids_peak_icv.so"
271
272 lib_path = os.path.abspath(os.path.join(file_dir, "..", lib_name))
273
274 cls._instance._dll = ctypes.cdll.LoadLibrary(lib_path)
275
276 execute_and_map_return_codes(cls._instance._dll.peak_icv_Init)
277
278 atexit.register(Loader._library_exit)
279
280 return cls._instance
281
282 def dll(self) -> ctypes.CDLL:
283 if self._dll is None:
284 raise NotPossibleException("DLL is not loaded")
285 return self._dll
286
287 # https://docs.python.org/3/reference/datamodel.html From Python Docs: It is not guaranteed that __del__() methods
288 # are called for objects that still exist when the interpreter exits.
289 @staticmethod
290 def _library_exit() -> None:
291 execute_and_map_return_codes(Loader().dll().peak_icv_Exit)
292
293
294ReturnType = TypeVar('ReturnType', bound=ctypes.c_void_p)
295
296
297def check_init_for_classes_with_classmethods_only(class_name: str, return_type: Type[ReturnType],
298 *args: Any, **kwargs: Any) -> ReturnType:
299 try:
300 handle = kwargs["_handle"]
301 if type(handle) is not return_type:
302 raise NotPossibleException(f"Invalid handle type {type(handle)} found.")
303
304 return handle
305 except KeyError:
307 f"Cannot initialize {class_name} with __init__. Please use a classmethod.")
Occurs when reading from a file or stream that is incomplete, damaged, or in an unexpected format.
Base class for all exceptions thrown by the library.
Definition exceptions.py:5
Occurs when a file operation fails (e.g., missing file, insufficient permissions, or inaccessible dir...
Occurs when a failure happens during processing due to an unhandled internal routine condition.
Definition exceptions.py:33
Occurs when a required dependency is missing, the library is not initialized, or a misconfiguration a...
Definition exceptions.py:18
Occurs when a mathematical computation fails (e.g., division by zero or invalid matrix operations).
Definition exceptions.py:99
Occurs when input parameters do not match expected conditions (e.g., two input images have different ...
Definition exceptions.py:45
Occurs when an operation cannot be performed due to logical constraints (e.g., camera calibration wit...
Definition exceptions.py:72
Occurs when a requested operation is not implemented or supported (e.g., exporting to an unsupported ...
Definition exceptions.py:60
Occurs when attempting to access an object or value via an uninitialized or null pointer.
Occurs when a provided value falls outside the allowed valid range.
Definition exceptions.py:87
Occurs when an expected target object (e.g., calibration plate or marker) is not detected in the inpu...
ctypes.Array create_array(Any c_type, int size)
Definition utils.py:20
Metadata metadata_from_capture_information(peak_icv_capture_information capture_information)
Definition utils.py:93
Type map_enum_to_point_type(peak_icv_point_type point_enum)
Definition utils.py:153
ctypes.c_void_p image_handle_to_ctypes(int image_handle)
Definition utils.py:133
Metadata add_metadata_from_capture_information_to_metadata(Metadata metadata, peak_icv_capture_information capture_information)
Definition utils.py:98
ReturnType check_init_for_classes_with_classmethods_only(str class_name, Type[ReturnType] return_type, *Any args, **Any kwargs)
Definition utils.py:298
Type map_point_type_to_native_type(Type point_type)
Definition utils.py:137
None transform(InputType input_, OutputType output, Callable func)
Definition utils.py:166
None execute_and_map_return_codes(Callable func, *Any args, **Any kwargs)
Definition utils.py:171
peak_icv_point_type map_point_type_to_enum(Type point_type)
Definition utils.py:145
peak_icv_capture_information metadata_to_capture_information(Metadata metadata, Size|None size=None)
Definition utils.py:25