IDS peak ICV v1.2.0
Loading...
Searching...
No Matches
region.py
Go to the documentation of this file.
1from __future__ import annotations
2
3import ctypes
4from typing import Union, TypeAlias, Any, cast, TYPE_CHECKING
5
6import numpy as np
7import numpy.typing as npt
8
11
12from ids_peak_icv import lib_loader
13from ids_peak_icv.backend.datatypes import (peak_icv_region_handle, peak_icv_point, peak_icv_point_f,
14 peak_icv_drawing_options, peak_common_rectangle)
15from ids_peak_icv.backend.utils import create_array, execute_and_map_return_codes
16from ids_peak_icv.exceptions import (NotSupportedException, OutOfRangeException, NotPossibleException,
17 ICVException)
18from ids_peak_icv.painting.drawable import IDrawable
19from ids_peak_icv.painting.drawing_options import DrawingOptions
20
21if TYPE_CHECKING:
22 from ids_peak_icv.datatypes.image import Image
23
24# Note: We loose type info here, as it is stored as a pointer to a python object
25PointArray: TypeAlias = npt.NDArray[np.object_]
26RegionArray: TypeAlias = npt.NDArray[np.object_]
27
28
30 """!
31 @brief
32 Represents a region consisting of a set of points in the image plane.
33
34 @see @ref concept_type_region
35
36 @since ids_peak_icv 1.0
37 @ingroup ids_peak_icv_python_types
38 """
39
40 @classmethod
41 def create_from_points(cls, points: Union[PointArray, npt.NDArray[np.int_]]) -> Region:
42 """!
43 @brief
44 Creates a region from points.
45
46 @param points: NumPy Array of Point objects representing the region's points OR NumPy Array of Points [x,y]
47 @return A region instance.
48
49 @exception OutOfRangeException: If a point at a given index has an invalid length.
50 @exception NotSupportedException: If the parameter `points` is not a NumPy array of Point objects or
51 a two-dimensional NumPy array.
52
53 @since ids_peak_icv 1.0
54 """
55 size = len(points)
56 c_points = create_array(peak_icv_point, size)
57
58 for i, p in enumerate(points):
59 if isinstance(p, Point) and isinstance(points, np.ndarray):
60 c_points[i] = peak_icv_point(p.x, p.y)
61 elif isinstance(p, np.ndarray):
62 if len(p) == 2:
63 c_points[i] = peak_icv_point(p[0], p[1])
64 else:
66 "Point {index} has invalid length {length}".format(index=i, length=len(p)))
67 else:
69 "Parameter points has to be a numpy array of Point objects or a two-dimensional numpy array.")
70
71 handle = peak_icv_region_handle()
72 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_CreateFromPoints,
73 ctypes.pointer(handle), c_points, size)
74
75 return Region(_handle=handle)
76
77 @classmethod
78 def create_from_rectangle(cls, rect: Rectangle) -> Region:
79 """!
80 @brief
81 Creates a Region from a Rectangle.
82 @param rect: Rectangle to create the region from
83
84 @return A region instance.
85
86 @exception NotSupportedException: If the contents of rectangle are not integers.
87
88 @since ids_peak_icv 1.0
89 """
90
91 c_rect = None
92 try:
93 c_rect = peak_common_rectangle(rect.x, rect.y, rect.width, rect.height)
94 except TypeError:
95 raise NotSupportedException("The contents of rectangle must be integer.")
96
97 handle = peak_icv_region_handle()
98 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_CreateFromRectangle,
99 ctypes.pointer(handle), c_rect)
100
101 return Region(_handle=handle)
102
103 def __init__(self, *args: Any, **kwargs: Any) -> None:
104 """!
105 @brief
106 In order to create an _empty Region_ create it with no arguments, in all other cases please refer to the
107 classmethods.
108
109 @since ids_peak_icv 1.0
110 """
111 super().__init__()
112
114
115 if len(args) or len(kwargs):
116 def _check_init_for_classes_with_classmethods_only(**kwargs: Any) -> peak_icv_region_handle:
117 try:
118 return cast(peak_icv_region_handle, kwargs["_handle"])
119 except KeyError:
121 "Cannot initialize Region with arguments. Please use a classmethod or create an empty Region "
122 "using Region().")
123
124 self._handle = _check_init_for_classes_with_classmethods_only(**kwargs)
125 else:
126 handle = peak_icv_region_handle()
127 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Create,
128 ctypes.pointer(handle))
129 self._handle = handle
130
131 self._points: np.ndarray | None = None
132 self._connected_components: np.ndarray | None = None
133
134 def __eq__(self, other: object) -> bool:
135 """!
136 @brief
137 Check if two regions are equal.
138
139 @param other: Another Region object
140
141 @return True if regions are equal, False otherwise
142
143 @since ids_peak_icv 1.0
144 """
145 if not isinstance(other, Region):
146 return False
147
148 if self._handle == other._handle:
149 return True
150
151 is_equal = ctypes.c_bool()
152 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Compare, self._handle, other._handle,
153 ctypes.pointer(is_equal))
154
155 return is_equal.value
156
157 def __ne__(self, other: object) -> bool:
158 """!
159 @brief
160 Check if two regions are not equal.
161
162 @param other: Another Region object
163
164 @return True if regions are not equal, False otherwise
165
166 @since ids_peak_icv 1.0
167 """
168 return not self.__eq__(other)
169
170 @property
171 def points(self) -> PointArray:
172 """!
173 @brief
174 Get the points of the region.
175
176 @return List of Point objects representing the region's points.
177
178 @since ids_peak_icv 1.0
179 """
180 if self._points is not None:
181 return self._points
182
183 c_num_points = ctypes.c_size_t()
184 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_GetPoints_GetCount,
185 self._handle, ctypes.pointer(c_num_points))
186
187 if c_num_points.value == 0:
188 self._points = np.ndarray(0)
189 return cast(PointArray, self._points)
190
191 c_points = create_array(peak_icv_point, c_num_points.value)
192 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_GetPoints,
193 self._handle, c_points, c_num_points)
194
195 iterable = (Point(p.x, p.y) for p in c_points)
196
197 # workaround to fill the array on python3.7 with numpy
198 self._points = np.empty(len(c_points), dtype=Point)
199 for i, it in enumerate(iterable):
200 self._points[i] = it
201
202 return cast(PointArray, self._points)
203
204 @property
205 def connected_components(self) -> RegionArray:
206 """!
207 @brief
208 Calculate the connected components of the region.
209
210 The 8-neighborhood is used to determine the components.
211
212 @see @ref concept_region_connected-components
213
214 @return List of connected components as Region objects.
215
216 @since ids_peak_icv 1.0
217 """
218 if self._connected_components is not None:
219 return self._connected_components
220
221 c_num_regions = ctypes.c_size_t()
222 execute_and_map_return_codes(
223 lib_loader.dll().peak_icv_Region_GetConnectedComponents_GetCount,
224 self._handle, ctypes.pointer(c_num_regions))
225
226 if c_num_regions.value == 0:
227 self._connected_components = np.ndarray(0)
228 return cast(RegionArray, self._connected_components)
229
230 c_regions = create_array(peak_icv_region_handle, c_num_regions.value)
231
232 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Array_Create, c_regions,
233 c_num_regions)
234 try:
235 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_GetConnectedComponents,
236 self._handle, c_regions, c_num_regions)
237 iterable = (Region(_handle=h) for h in c_regions)
238 except ICVException as e:
239 lib_loader.dll().peak_icv_Region_Array_Destroy(c_regions, c_num_regions)
240 raise e
241
242 # workaround to fill the array on python3.7 with numpy
243 self._connected_components = np.empty(len(c_regions), dtype=Region)
244 for i, it in enumerate(iterable):
245 self._connected_components[i] = it
246
247 return cast(RegionArray, self._connected_components)
248
249 @property
250 def area(self) -> int:
251 """!
252 @brief
253 Calculate the area of the region. The area is the number of pixels in the region.
254
255 @return Area of the region.
256
257 @since ids_peak_icv 1.0
258 """
259 c_area = ctypes.c_size_t()
260 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_GetArea,
261 self._handle, ctypes.pointer(c_area))
262
263 return c_area.value
264
265 @property
266 def center_of_gravity(self) -> Point:
267 """!
268 @brief
269 Calculate the center of gravity of the region.
270
271 @return Center of gravity as a PointF object.
272
273 @exception MathErrorException: If the center of gravity could not be computed.
274
275 @since ids_peak_icv 1.0
276 """
277 c_center_of_gravity = peak_icv_point_f()
278 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_GetCenterOfGravity,
279 self._handle, ctypes.pointer(c_center_of_gravity))
280
281 return Point(c_center_of_gravity.x, c_center_of_gravity.y)
282
283 def intersection(self, other_region: Region) -> Region:
284 """!
285 @brief
286 Computes the intersection of two regions.
287
288 @return The intersection of two regions.
289
290 @since ids_peak_icv 1.0
291 """
292 output_region = Region()
293 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Intersection,
294 self._handle, other_region._handle, ctypes.pointer(output_region._handle))
295
296 return output_region
297
298 def difference(self, other_region: Region) -> Region:
299 """!
300 @brief
301 Computes the difference of two regions.
302
303 @return The difference of two regions.
304
305 @since ids_peak_icv 1.0
306 """
307 output_region = Region()
308 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Difference,
309 self._handle, other_region._handle, ctypes.pointer(output_region._handle))
310
311 return output_region
312
313 def union(self, other_region: Region) -> Region:
314 """!
315 @brief
316 Computes the union of two regions.
317
318 @return The united region.
319
320 @since ids_peak_icv 1.0
321 """
322 output_region = Region()
323 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Union,
324 self._handle, other_region._handle, ctypes.pointer(output_region._handle))
325
326 return output_region
327
328 def dilation(self, structuring_element: Region) -> Region:
329 """!
330 @brief
331 Computes the dilation of the input region with the given structuring element.
332
333 The reference point of the structuring element is at (0, 0).
334
335 @param structuring_element:
336
337 @return The dilated region.
338
339 @since ids_peak_icv 1.0
340 """
341 output_region = Region()
342 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Dilation, self._handle,
343 structuring_element._handle, ctypes.pointer(output_region._handle))
344 return output_region
345
346 def erosion(self, structuring_element: Region) -> Region:
347 """!
348 @brief
349 Computes the erosion of the input region with the given structuring element.
350
351 The reference point of the structuring element is at (0, 0).
352
353 @param structuring_element:
354
355 @return The eroded region.
356
357 @since ids_peak_icv 1.0
358 """
359 output_region = Region()
360 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Erosion, self._handle,
361 structuring_element._handle, ctypes.pointer(output_region._handle))
362 return output_region
363
364 def __del__(self) -> None:
365 """!
366 @brief
367 Destructor for Region class.
368 """
369 if self._handle is not None:
370 lib_loader.dll().peak_icv_Region_Destroy(self._handle)
371
372 def _draw(self, image: Image, drawing_options: DrawingOptions) -> None:
373 """!
374 @brief
375 Only region points inside the image are painted.
376 Region points outside the image are ignored. In case of an empty region, the image is not modified.
377
378 @since ids_peak_icv 1.0
379 """
380 from ids_peak_icv.datatypes.image import _check_and_get_icv_image
381
382 drawing_options_c = peak_icv_drawing_options(drawing_options.color.color_code,
383 drawing_options.opacity.value)
384
385 _image = _check_and_get_icv_image(image)
386
387 execute_and_map_return_codes(lib_loader.dll().peak_icv_Region_Draw, _image._handle,
388 self._handle, drawing_options_c)
Represents a point in 2D space with x and y coordinates.
Definition point.py:6
Represents a region consisting of a set of points in the image plane.
Definition region.py:29
np.ndarray|None _connected_components
Definition region.py:132
int area(self)
Calculate the area of the region.
Definition region.py:250
None __init__(self, *Any args, **Any kwargs)
In order to create an empty Region create it with no arguments, in all other cases please refer to th...
Definition region.py:103
bool __eq__(self, object other)
Check if two regions are equal.
Definition region.py:134
Region union(self, Region other_region)
Computes the union of two regions.
Definition region.py:313
Point center_of_gravity(self)
Calculate the center of gravity of the region.
Definition region.py:266
Region dilation(self, Region structuring_element)
Computes the dilation of the input region with the given structuring element.
Definition region.py:328
RegionArray connected_components(self)
Calculate the connected components of the region.
Definition region.py:205
Region intersection(self, Region other_region)
Computes the intersection of two regions.
Definition region.py:283
None __del__(self)
Destructor for Region class.
Definition region.py:364
Region create_from_points(cls, Union[PointArray, npt.NDArray[np.int_]] points)
Creates a region from points.
Definition region.py:41
Region create_from_rectangle(cls, Rectangle rect)
Creates a Region from a Rectangle.
Definition region.py:78
Region difference(self, Region other_region)
Computes the difference of two regions.
Definition region.py:298
PointArray points(self)
Get the points of the region.
Definition region.py:171
Region erosion(self, Region structuring_element)
Computes the erosion of the input region with the given structuring element.
Definition region.py:346
None _draw(self, Image image, DrawingOptions drawing_options)
Only region points inside the image are painted.
Definition region.py:372
bool __ne__(self, object other)
Check if two regions are not equal.
Definition region.py:157
Thrown when an operation cannot be performed due to logical constraints.
Definition exceptions.py:85
Thrown when a requested operation is not implemented or supported.
Definition exceptions.py:71
Thrown when a value is outside the allowed range.
Interface for drawable objects.
Definition drawable.py:7