IDS peak ICV v1.1.0
Loading...
Searching...
No Matches
image.py
Go to the documentation of this file.
1# @package image
2
3from __future__ import annotations
4
5import ctypes
6from typing import Any, cast
7import math
8
9import numpy as np
10from ids_peak_ipl import ids_peak_ipl
11
13from ids_peak_common.datatypes.iimageview import IImageView
14from ids_peak_common.datatypes.metadata import Metadata
15from ids_peak_common.datatypes.pixelformat import PixelFormat, Channel
16
17from ids_peak_icv import lib_loader
19 peak_common_size,
20 peak_icv_image_handle,
21 peak_icv_image_info,
22 peak_icv_capture_information,
23 peak_icv_binning_factor,
24)
26 execute_and_map_return_codes,
27 check_init_for_classes_with_classmethods_only,
28 metadata_to_capture_information,
29 metadata_from_capture_information,
30)
31from ids_peak_icv.exceptions import OutOfRangeException
32
33from typing import TYPE_CHECKING
34
35if TYPE_CHECKING:
36 from ids_peak_icv.datatypes.region import Region
37
38
39class Image:
40 """!
41 @brief
42 Image Class for working with algorithms.
43
44 @since ids_peak_icv 1.0
45 @ingroup ids_peak_icv_python_types
46 """
47
48 _data: np.ndarray
49
50 @classmethod
51 def create_from_pixel_format_and_size(cls, pixel_format: PixelFormat, size: Size) -> Image:
52 """!
53 @brief
54 Creates an instance of Image using a size.Size and a pixel format
55
56 @param size: Image size in pixels
57 @param pixel_format: Pixel format of the image
58 @return Returns an image instance.
59
60 @since ids_peak_icv 1.0
61 """
62
63 image_handle = peak_icv_image_handle()
64 image_size = peak_common_size(size.width, size.height)
65
66 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_Create,
67 ctypes.pointer(image_handle),
68 ctypes.c_int(pixel_format.value),
69 image_size)
70
71 return Image(_handle=image_handle)
72
73 @classmethod
74 def create_from_ids_peak_ipl_image(cls, ids_peak_ipl_image: ids_peak_ipl.Image) -> Image:
75 """!
76 @brief
77 Creates an instance of Image using a ids_peak_ipl image.
78
79 @param ids_peak_ipl_image: ids_peak_ipl image
80 @return Created an image instance.
81
82 @since ids_peak_icv 1.0
83 """
84 numpy_data = (np.array(ids_peak_ipl_image.DataView(), dtype=np.uint8) if
85 ids_peak_ipl_image.PixelFormat().IsPacked() else ids_peak_ipl_image.get_numpy())
86 image_info = peak_icv_image_info(
87 pixel_format=ids_peak_ipl_image.PixelFormat().PixelFormatName(),
88 size=peak_common_size(ids_peak_ipl_image.Width(), ids_peak_ipl_image.Height()),
89 buffer_size=ids_peak_ipl_image.ByteCount(),
90 buffer=numpy_data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
91 )
92 image_handle = peak_icv_image_handle()
93
94 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_CreateFromImageInfo,
95 ctypes.pointer(image_handle),
96 image_info,
97 ctypes.sizeof(image_info))
98 c_capture_information = peak_icv_capture_information(binning_factor=peak_icv_binning_factor(x=1, y=1),
99 time_stamp=ids_peak_ipl_image.Timestamp())
100 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_SetCaptureInformation, image_handle,
101 c_capture_information, ctypes.sizeof(c_capture_information))
102
103 return cls(_handle=image_handle, _ids_peak_ipl_image=ids_peak_ipl_image)
104
105 @classmethod
106 def create_from_np_array(cls, pixel_format: PixelFormat, size: Size, data: np.ndarray) -> Image:
107 """!
108 @brief
109 Creates an instance of Image using a PixelFormat, size.Size and numpy image buffer.
110
111 @param pixel_format: Pixel format of the image
112 @param size: Image size in pixels
113 @param data: Numpy array holding the buffer data
114
115 @return Created an image instance.
116
117 @exception OutOfRangeException: If the input array is 2D and the expected number of channels is not 1,
118 or if it is 3D and the channel count does not match, or if it has a number of dimensions other than 2 or 3.
119
120 @since ids_peak_icv 1.0
121 """
122 if data.ndim == 2:
123 if pixel_format.number_of_channels != 1:
124 raise OutOfRangeException("Number of channels has to be 1")
125 elif data.ndim == 3:
126 if data.shape[2] != pixel_format.number_of_channels:
128 "Number of channels must be {}".format(pixel_format.number_of_channels))
129 elif data.ndim == 1:
130 pass
131 else:
132 raise OutOfRangeException("Numpy array has wrong dimension")
133
134 image_handle = peak_icv_image_handle()
135 image_info = peak_icv_image_info(
136 pixel_format=pixel_format.value,
137 size=peak_common_size(size.width, size.height),
138 buffer_size=data.size * data.itemsize,
139 buffer=data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
140 )
141
142 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_CreateFromImageInfo,
143 ctypes.pointer(image_handle),
144 image_info,
145 ctypes.sizeof(image_info))
146
147 return cls(_handle=image_handle, _data=data)
148
149 @classmethod
150 def create_from_file(cls, image_path: str, forced_pixel_format: PixelFormat | None = None) -> Image:
151 """!
152 @brief
153 Creates an Image from a file path.
154
155 This method loads an image from the given file path.
156
157 If a PixelFormat is provided via ``forced_pixel_format``,
158 the method attempts to interpret the file using that format.
159 If interpretation fails, an exception is raised.
160 Explicit conversion between image formats must be handled manually by the user.
161
162 The following image formats are supported:
163
164 - Bitmap (BMP)
165 - Joint Photographic Experts Group (JPEG)
166 - Portable Network Graphics (PNG)
167 - @htmlonly Tag Image File Format (TIFF)@endhtmlonly
168 (Only if ``forced_pixel_format`` is specified)
169 - Raw Binary Format (RAW)
170
171 ### Supported image and pixel formats
172
173 #### BMP:
174 @supportedPixelformats{ImageLoad_BMP}
175 #### JPEG:
176 @supportedPixelformats{ImageLoad_JPEG}
177 #### PNG:
178 @supportedPixelformats{ImageLoad_PNG}
179 #### RAW:
180 If ``forced_pixel_format`` is ``None`` any raw image can be loaded.
181
182 If ``forced_pixel_format`` is specified, only the following formats are supported
183 and must match the format used when saving:
184 @supportedPixelformats{ImageLoad_RAW_WithPixelFormat}
185
186 See the @ref concept_type_pixel_format for a detailed description of the pixel formats.
187
188 @param image_path: File path on disk, pointing to an image.
189 @param forced_pixel_format: Pixel format the loaded image is forced to use.
190 @return The loaded Image as a new instance
191
192 @since ids_peak_icv 1.0
193 """
194
195 c_image_path = image_path.encode('utf-8')
196 c_handle = peak_icv_image_handle()
197
198 if forced_pixel_format is None:
199 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_CreateFromFile,
200 ctypes.pointer(c_handle), ctypes.c_char_p(c_image_path))
201 else:
202 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_CreateFromFileWithPixelFormat,
203 ctypes.pointer(c_handle), ctypes.c_char_p(c_image_path),
204 ctypes.c_int(forced_pixel_format.value))
205 return cls(_handle=c_handle)
206
207 @classmethod
208 def create_from_image_view(cls, image_view: IImageView) -> Image:
209 """!
210 @brief
211 Creates an instance of Image using an IImageView interface.
212
213 @param image_view: An object implementing the IImageView interface,
214 which can be constructed from a buffer provided by a camera.
215 @return A new `Image` instance created from the given image view.
216
217 @since ids_peak_icv 1.0
218 """
219
220 image = cls.create_from_np_array(image_view.pixel_format, image_view.size, image_view.to_numpy_array())
221 image.metadata = image_view.metadata
222 return image
223
224 def __init__(self, *args: Any, **kwargs: Any) -> None:
225 """!
226 @brief
227 In order to create an Image, please refer to the classmethods.
228
229 @since ids_peak_icv 1.0
230 """
231 self._handle_handle = check_init_for_classes_with_classmethods_only(self.__class__.__name__,
232 peak_icv_image_handle, *args, **kwargs)
233
234 if len(kwargs):
235 self._handle_handle = kwargs["_handle"]
236
237 image_info = peak_icv_image_info()
238 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_GetInfo,
239 self._handle_handle, ctypes.pointer(image_info), ctypes.sizeof(image_info))
240
241 self._metadata: Metadata | None = None
242
243 self._pixel_format = PixelFormat(image_info.pixel_format)
244 self._size = Size(image_info.size.width, image_info.size.height)
245
246 num_channels = self._pixel_format.number_of_channels
247 _shape = [int(self._size.height), int(self._size.width)]
248 if num_channels > 1:
249 _shape.append(num_channels)
250
251 self._ids_peak_ipl_image = kwargs["_ids_peak_ipl_image"] if "_ids_peak_ipl_image" in kwargs else None
252
253 if "_data" in kwargs:
254 self._data = kwargs["_data"]
255 self._raw_data = None
256 else:
257 packed_or_yuv = (self._pixel_format.is_packed or
258 self._pixel_format.has_channel(Channel.CHROMA_U))
259
260 # Create a numpy array from the ctypes buffer with dtype uint8
261 self._raw_data = np.ctypeslib.as_array(image_info.buffer, shape=(
263 self._pixel_format.allocated_bits_per_pixel,
264 packed_or_yuv),))
265
266 if packed_or_yuv:
267 self._data = self._raw_data.view(dtype=np.uint8)
268 else:
269 self._data = self._raw_data.view(dtype=self._pixel_format.numpy_dtype)
270 self._data = self._data.reshape(_shape)
271
272 def __del__(self) -> None:
273 """!
274 @brief
275 Destructor for Image class.
276 """
277 if hasattr(self, '_handle'):
278 lib_loader.dll().peak_icv_Image_Destroy(self._handle_handle)
279
280 def save(self, output_file_path: str) -> None:
281 """!
282 @brief
283 Saves an instance to a file on disk. The type is specified by the given file ending in file name.
284
285 The following image formats are supported:
286 - Portable Network Graphics (PNG)
287 - Bitmap (BMP)
288 - Joint Photographic Experts Group (JPEG)
289 - @htmlonly Tag Image File Format@endhtmlonly (TIFF)
290 - Raw Binary Format (RAW)
291
292 The compression settings for PNG and JPEG images are as follows:
293 For JPEG, the image quality is set to 75%, and for PNG, the image quality is set to 100%.
294
295 @note The file extension for the given file name must match the specified image format when an image is saved.
296 Otherwise, an error will occur when saving an image. The file extensions are:
297 - PNG: .png
298 - BMP: .bmp
299 - JPEG: .jpeg
300 - TIFF: .tiff
301 - RAW: .raw
302
303 ### Supported image and pixel formats
304
305 #### BMP:
306 @supportedPixelformats{ImageSave_BMP}
307 #### JPEG:
308 @supportedPixelformats{ImageSave_JPEG}
309 #### PNG:
310 @supportedPixelformats{ImageSave_PNG}
311 #### TIFF:
312 @supportedPixelformats{ImageSave_TIFF}
313 #### RAW:
314 @supportedPixelformats{ImageSave_RAW}
315
316 See the @ref concept_type_pixel_format for a detailed description of the pixel formats.
317
318 @param output_file_path: The path of the file to store the image to disk.
319
320 @since ids_peak_icv 1.0
321 """
322 c_output_file_path = output_file_path.encode('utf-8')
323 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_SaveToFile, self._handle_handle,
324 ctypes.c_char_p(c_output_file_path))
325
326 @property
327 def size(self) -> Size:
328 """!
329 @brief
330 Provides the size.Size of the image.
331
332 @return Image size in pixels
333
334 @since ids_peak_icv 1.0
335 """
336 return self._size
337
338 @property
339 def width(self) -> int:
340 """!
341 @brief
342 Provides the width of the image.
343
344 @return Image width in pixels
345
346 @since ids_peak_icv 1.0
347 """
348 return int(self.size.width)
349
350 @property
351 def height(self) -> int:
352 """!
353 @brief
354 Provides the height of the image.
355
356 @return Image height in pixels
357
358 @since ids_peak_icv 1.0
359 """
360 return int(self.size.height)
361
362 def to_numpy_array(self, copy: bool = True) -> np.ndarray:
363 """!
364 @brief
365 Provides a numpy representation of the image.
366
367 Note that the image is copied by default and the caller must not take care of the original Instance being still
368 available.
369
370 @param copy: True: The image is copied, False: The image only references the internal memory.
371 @return Image data as a numpy array. Shape is [height, width] for single channel images.
372 Shape is [height, width, number of channels] for multichannel images.
373
374 @since ids_peak_icv 1.0
375 """
376
377 if copy:
378 return cast(np.ndarray, self._data.copy())
379
380 return self._data
381
382 def convert_pixel_format(self, pixel_format: PixelFormat) -> Image:
383 """!
384 @brief
385 Converts the image to a different pixel format.
386
387 If the input image has the same pixel format as the specified pixel format parameter,
388 the image will be copied.
389
390 **Restrictions**:
391 - Conversion to Bayer formats is only supported from a bayer format with the same pattern,
392 e.g. from BayerRG10p to BayerRG10 or BayerRG8.
393 - Conversion to packed formats is not supported.
394 - Conversions from RGB or BGR formats to RGBa or BGRa formats is not supported.
395 - RGBa and BGRa formats support only conversions into different bit depths of the same channel layout
396 or to Mono formats.
397 - Confidence and Coord3D formats can only be converted to other bit depths of the same format type.
398
399 @note
400 For a full list of supported conversions see \ref concept_supported_pixel_format_conversion.
401
402 @param pixel_format: The target pixel format.
403
404 @return A new Image with the target pixel format.
405
406 @since ids_peak_icv 1.0
407 """
408
409 output_image_handle = peak_icv_image_handle()
410 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_Create,
411 ctypes.pointer(output_image_handle),
412 ctypes.c_int(pixel_format.value),
413 peak_common_size(self._size.width, self._size.height))
414
415 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_ConvertPixelFormat,
416 self._handle_handle, ctypes.c_int(pixel_format.value),
417 output_image_handle)
418
419 return Image(_handle=output_image_handle)
420
421 def convert_pixel_format_with_factor(self, pixel_format: PixelFormat, factor: float) -> Image:
422 """!
423 @brief
424 Converts an image into another pixel format with a factor multiplied to every pixel.
425
426 Supported conversions:
427 From PixelFormat::Coord3D_C8 or PixelFormat::Coord3D_C16 to PixelFormat::Coord3D_C32f
428
429 @param pixel_format: The target pixel format.
430 @param factor: The multiplication factor.
431 @return A new instance of Image with the target pixel format.
432
433 @since ids_peak_icv 1.1
434 """
435
436 output_image_handle = peak_icv_image_handle()
437 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_Create,
438 ctypes.pointer(output_image_handle),
439 ctypes.c_int(pixel_format.value),
440 peak_common_size(self._size.width, self._size.height))
441
442 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_ConvertPixelFormatWithFactor,
443 self._handle_handle, ctypes.c_int(pixel_format.value), ctypes.c_double(factor),
444 output_image_handle)
445
446 return Image(_handle=output_image_handle)
447
448 @property
449 def region(self) -> Region:
450 """!
451 @brief
452 Retrieves the region.Region associated with the specified image_handle.
453 @return The image region.
454
455 @since ids_peak_icv 1.0
456 """
457 from ids_peak_icv.datatypes.region import Region
458
459 region = Region()
460 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_GetRegion, self._handle_handle,
461 ctypes.pointer(region._handle))
462 return region
463
464 @region.setter
465 def region(self, region: Region) -> None:
466 """!
467 @brief
468 Updates the region.Region on the image.
469
470 Sets a region of interest (ROI) for the specified image handle,
471 which defines a sub-area of the image where operations or analyses are focused.
472 Any points in the region that fall outside the bounds of the image are automatically discarded (clipped).
473
474 @param region: The region to be set.
475
476 @since ids_peak_icv 1.0
477 """
478 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_SetRegion, self._handle_handle,
479 region._handle)
480
481 def reset_region(self) -> None:
482 """!
483 @brief
484 Resets the region.Region on the image to the complete image.
485
486 @since ids_peak_icv 1.0
487 """
488 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_ResetRegion, self._handle_handle)
489
490 @property
491 def metadata(self) -> Metadata:
492 """!
493 @brief
494 Retrieves the metadata.Metadata of an image which contains information about
495 the image capture settings.
496
497 @return The metadata of the image.
498
499 @since ids_peak_icv 1.0
500 """
501
502 if self._metadata is not None:
503 return self._metadata
504
505 c_capture_information = peak_icv_capture_information()
506 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_GetCaptureInformation, self._handle_handle,
507 ctypes.pointer(c_capture_information), ctypes.sizeof(c_capture_information))
508
509 self._metadata = metadata_from_capture_information(c_capture_information)
510 return self._metadata
511
512 @metadata.setter
513 def metadata(self, metadata: Metadata) -> None:
514 """!
515 @brief
516 Updates the metadata.Metadata of an image which is used to store and
517 manage capture settings.
518
519 @param metadata: The metadata to be updated.
520
521 @since ids_peak_icv 1.0
522 """
523 c_capture_information = metadata_to_capture_information(metadata, size=self._size)
524
525 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_SetCaptureInformation, self._handle_handle,
526 c_capture_information, ctypes.sizeof(c_capture_information))
527 self._metadata = metadata_from_capture_information(c_capture_information)
528
529 def subtract(self, subtrahend: Image) -> Image:
530 """!
531 @brief
532 Subtracts the specified subtrahend image pixelwise from the current image and returns the resulting image.
533
534 This function performs a pixelwise subtraction of the given subtrahend image from the current image.
535 The resulting image is returned as a new `Image` object.
536 The operation requires that both images have the same pixel format and dimensions.
537
538 @param subtrahend: The image to subtract from the current image.
539 @return The resulting image after pixelwise subtraction.
540
541 @since ids_peak_icv 1.0
542 """
543 difference = Image.create_from_pixel_format_and_size(self._pixel_format, self.size)
544 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_Subtract, self._handle_handle,
545 subtrahend._handle, difference._handle)
546 return difference
547
548 def __sub__(self, subtrahend: Image) -> Image:
549 """!
550 @brief
551 Subtracts the specified subtrahend image pixelwise from the current image and returns the resulting image.
552
553 This function performs a pixelwise subtraction of the given subtrahend image from the current image.
554 The resulting image is returned as a new `Image` object.
555 The operation requires that both images have the same pixel format and dimensions.
556
557 @param subtrahend: The image to subtract from the current image.
558 @return The resulting image after pixelwise subtraction.
559
560 @since ids_peak_icv 1.0
561 """
562 return self.subtract(subtrahend)
563
564 def __eq__(self, other: object) -> bool:
565 if not isinstance(other, Image):
566 return False
567 if self._handle_handle == other._handle:
568 return True
569 if self.size != other.size:
570 return False
571 if self._pixel_format != other._pixel_format:
572 return False
573 if self.regionregion != other.region:
574 return False
575
576 is_equal = ctypes.c_bool()
577 execute_and_map_return_codes(lib_loader.dll().peak_icv_Image_Compare, self._handle_handle, other._handle,
578 ctypes.pointer(is_equal))
579
580 return is_equal.value
581
582 @property
583 def pixel_format(self) -> PixelFormat:
584 return self._pixel_format
585
586
587def _get_byte_size(size: Size, bits_per_pixel: int, packed_or_yuv: bool) -> int:
588 """!
589 @brief
590 Calculate the byte size of an image buffer for a given size and pixel format.
591
592 @param size: The image dimensions (width, height) in pixels.
593 @param bits_per_pixel: The number of storage bits per pixel for the pixel format.
594 @param packed_or_yuv: If True, assumes packed or yuv format (truncate bits to full bytes).
595 If False, assumes other format (round up to nearest byte).
596 @return The byte size of the buffer.
597 """
598 total_bits = int(size.width) * int(size.height) * bits_per_pixel
599 if packed_or_yuv:
600 return math.ceil(total_bits / 8)
601 else:
602 return (total_bits + 7) // 8
603
604
605def _check_and_get_icv_image(image_var: Any) -> Any:
606 if isinstance(image_var, ids_peak_ipl.Image):
607 return Image.create_from_ids_peak_ipl_image(image_var)
608
609 return image_var
A size consists of width and height where width and height have to be greater than or equal to zero.
Definition size.py:9
Enum listing all supported pixel formats and their internal IDs.
Image Class for working with algorithms.
Definition image.py:39
Region region(self)
Retrieves the region.Region associated with the specified image_handle.
Definition image.py:449
Image create_from_ids_peak_ipl_image(cls, ids_peak_ipl.Image ids_peak_ipl_image)
Creates an instance of Image using a ids_peak_ipl image.
Definition image.py:74
Image create_from_np_array(cls, PixelFormat pixel_format, Size size, np.ndarray data)
Creates an instance of Image using a PixelFormat, size.Size and numpy image buffer.
Definition image.py:106
None region(self, Region region)
Updates the region.Region on the image.
Definition image.py:465
Image __sub__(self, Image subtrahend)
Subtracts the specified subtrahend image pixelwise from the current image and returns the resulting i...
Definition image.py:548
None __init__(self, *Any args, **Any kwargs)
In order to create an Image, please refer to the classmethods.
Definition image.py:224
None save(self, str output_file_path)
Saves an instance to a file on disk.
Definition image.py:280
Image create_from_pixel_format_and_size(cls, PixelFormat pixel_format, Size size)
Creates an instance of Image using a size.Size and a pixel format.
Definition image.py:51
bool __eq__(self, object other)
Definition image.py:564
None reset_region(self)
Resets the region.Region on the image to the complete image.
Definition image.py:481
Image convert_pixel_format_with_factor(self, PixelFormat pixel_format, float factor)
Converts an image into another pixel format with a factor multiplied to every pixel.
Definition image.py:421
PixelFormat pixel_format(self)
Definition image.py:583
None __del__(self)
Destructor for Image class.
Definition image.py:272
Metadata metadata(self)
Retrieves the metadata.Metadata of an image which contains information about the image capture settin...
Definition image.py:491
int height(self)
Provides the height of the image.
Definition image.py:351
Image convert_pixel_format(self, PixelFormat pixel_format)
Converts the image to a different pixel format.
Definition image.py:382
Size size(self)
Provides the size.Size of the image.
Definition image.py:327
Image subtract(self, Image subtrahend)
Subtracts the specified subtrahend image pixelwise from the current image and returns the resulting i...
Definition image.py:529
Image create_from_file(cls, str image_path, PixelFormat|None forced_pixel_format=None)
Creates an Image from a file path.
Definition image.py:150
Image create_from_image_view(cls, IImageView image_view)
Creates an instance of Image using an IImageView interface.
Definition image.py:208
np.ndarray to_numpy_array(self, bool copy=True)
Provides a numpy representation of the image.
Definition image.py:362
int width(self)
Provides the width of the image.
Definition image.py:339
Represents a region consisting of a set of points in the image plane.
Definition region.py:29
Thrown when a value is outside the allowed range.
int _get_byte_size(Size size, int bits_per_pixel, bool packed_or_yuv)
Calculate the byte size of an image buffer for a given size and pixel format.
Definition image.py:587
Any _check_and_get_icv_image(Any image_var)
Definition image.py:605