cellects 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cellects/__init__.py +0 -0
- cellects/__main__.py +49 -0
- cellects/config/__init__.py +0 -0
- cellects/config/all_vars_dict.py +155 -0
- cellects/core/__init__.py +0 -0
- cellects/core/cellects_paths.py +31 -0
- cellects/core/cellects_threads.py +1451 -0
- cellects/core/motion_analysis.py +2010 -0
- cellects/core/one_image_analysis.py +1061 -0
- cellects/core/one_video_per_blob.py +540 -0
- cellects/core/program_organizer.py +1316 -0
- cellects/core/script_based_run.py +154 -0
- cellects/gui/__init__.py +0 -0
- cellects/gui/advanced_parameters.py +1258 -0
- cellects/gui/cellects.py +189 -0
- cellects/gui/custom_widgets.py +790 -0
- cellects/gui/first_window.py +449 -0
- cellects/gui/if_several_folders_window.py +239 -0
- cellects/gui/image_analysis_window.py +2066 -0
- cellects/gui/required_output.py +232 -0
- cellects/gui/video_analysis_window.py +656 -0
- cellects/icons/__init__.py +0 -0
- cellects/icons/cellects_icon.icns +0 -0
- cellects/icons/cellects_icon.ico +0 -0
- cellects/image_analysis/__init__.py +0 -0
- cellects/image_analysis/cell_leaving_detection.py +54 -0
- cellects/image_analysis/cluster_flux_study.py +102 -0
- cellects/image_analysis/image_segmentation.py +706 -0
- cellects/image_analysis/morphological_operations.py +1635 -0
- cellects/image_analysis/network_functions.py +1757 -0
- cellects/image_analysis/one_image_analysis_threads.py +289 -0
- cellects/image_analysis/progressively_add_distant_shapes.py +508 -0
- cellects/image_analysis/shape_descriptors.py +1016 -0
- cellects/utils/__init__.py +0 -0
- cellects/utils/decorators.py +14 -0
- cellects/utils/formulas.py +637 -0
- cellects/utils/load_display_save.py +1054 -0
- cellects/utils/utilitarian.py +490 -0
- cellects-0.1.2.dist-info/LICENSE.odt +0 -0
- cellects-0.1.2.dist-info/METADATA +132 -0
- cellects-0.1.2.dist-info/RECORD +44 -0
- cellects-0.1.2.dist-info/WHEEL +5 -0
- cellects-0.1.2.dist-info/entry_points.txt +2 -0
- cellects-0.1.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
"""Module for computing shape descriptors from binary images.
|
|
2
|
+
|
|
3
|
+
This module provides a framework for calculating various geometric and statistical
|
|
4
|
+
descriptors of shapes in binary images through configurable dictionaries and a core class.
|
|
5
|
+
Supported metrics include area, perimeter, axis lengths, orientation, and more.
|
|
6
|
+
Descriptor computation is controlled via category dictionaries (e.g., `descriptors_categories`)
|
|
7
|
+
and implemented as methods in the ShapeDescriptors class.
|
|
8
|
+
|
|
9
|
+
Classes
|
|
10
|
+
-------
|
|
11
|
+
ShapeDescriptors : Class to compute various descriptors for a binary image
|
|
12
|
+
|
|
13
|
+
Notes
|
|
14
|
+
-----
|
|
15
|
+
Relies on OpenCV and NumPy for image processing operations.
|
|
16
|
+
Shape descriptors: The following names, lists and computes all the variables describing a shape in a binary image.
|
|
17
|
+
If you want to allow the software to compute another variable:
|
|
18
|
+
1) In the following dicts and list, you need to:
|
|
19
|
+
add the variable name and whether to compute it (True/False) by default
|
|
20
|
+
2) In the ShapeDescriptors class:
|
|
21
|
+
add a method to compute that variable
|
|
22
|
+
3) In the init method of the ShapeDescriptors class
|
|
23
|
+
attribute a None value to the variable that store it
|
|
24
|
+
add a if condition in the for loop to compute that variable when its name appear in the wanted_descriptors_list
|
|
25
|
+
"""
|
|
26
|
+
import cv2
|
|
27
|
+
import numpy as np
|
|
28
|
+
from copy import deepcopy
|
|
29
|
+
from cellects.utils.utilitarian import translate_dict
|
|
30
|
+
from cellects.utils.formulas import get_inertia_axes, get_standard_deviations, get_skewness, get_kurtosis
|
|
31
|
+
|
|
32
|
+
descriptors_categories = {'area': True, 'perimeter': False, 'circularity': False, 'rectangularity': False,
|
|
33
|
+
'total_hole_area': False, 'solidity': False, 'convexity': False, 'eccentricity': False,
|
|
34
|
+
'euler_number': False, 'standard_deviation_xy': False, 'skewness_xy': False,
|
|
35
|
+
'kurtosis_xy': False, 'major_axes_len_and_angle': True, 'iso_digi_analysis': False,
|
|
36
|
+
'oscilacyto_analysis': False, 'network_analysis': False, 'graph_extraction': False,
|
|
37
|
+
'fractal_analysis': False
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
descriptors_names_to_display = ['Area', 'Perimeter', 'Circularity', 'Rectangularity', 'Total hole area',
|
|
41
|
+
'Solidity', 'Convexity', 'Eccentricity', 'Euler number', 'Standard deviation xy',
|
|
42
|
+
'Skewness xy', 'Kurtosis xy', 'Major axes lengths and angle',
|
|
43
|
+
'Growth transitions', 'Oscillations', 'Network', 'Graph',
|
|
44
|
+
'Fractals'
|
|
45
|
+
]#, 'Oscillating cluster nb and size'
|
|
46
|
+
|
|
47
|
+
from_shape_descriptors_class = {'area': True, 'perimeter': False, 'circularity': False, 'rectangularity': False,
|
|
48
|
+
'total_hole_area': False, 'solidity': False, 'convexity': False, 'eccentricity': False,
|
|
49
|
+
'euler_number': False, 'standard_deviation_y': False, 'standard_deviation_x': False,
|
|
50
|
+
'skewness_y': False, 'skewness_x': False, 'kurtosis_y': False, 'kurtosis_x': False,
|
|
51
|
+
'major_axis_len': True, 'minor_axis_len': True, 'axes_orientation': True
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
descriptors = deepcopy(from_shape_descriptors_class)
|
|
55
|
+
descriptors.update({'cluster_number': False, 'mean_cluster_area': False, 'minkowski_dimension': False,
|
|
56
|
+
'vertices_number': False, 'edges_number': False})
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ShapeDescriptors:
|
|
61
|
+
"""
|
|
62
|
+
This class takes :
|
|
63
|
+
- a binary image of 0 and 1 drawing one shape
|
|
64
|
+
- a list of descriptors to calculate from that image
|
|
65
|
+
["area", "perimeter", "circularity", "rectangularity", "total_hole_area", "solidity", "convexity",
|
|
66
|
+
"eccentricity", "euler_number",
|
|
67
|
+
|
|
68
|
+
"standard_deviation_y", "standard_deviation_x", "skewness_y", "skewness_x", "kurtosis_y", "kurtosis_x",
|
|
69
|
+
"major_axis_len", "minor_axis_len", "axes_orientation",
|
|
70
|
+
|
|
71
|
+
"mo", "contours", "min_bounding_rectangle", "convex_hull"]
|
|
72
|
+
|
|
73
|
+
Be careful! mo, contours, min_bounding_rectangle, convex_hull,
|
|
74
|
+
standard_deviations, skewness and kurtosis are not atomics
|
|
75
|
+
https://www.researchgate.net/publication/27343879_Estimators_for_Orientation_and_Anisotropy_in_Digitized_Images
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, binary_image, wanted_descriptors_list):
|
|
79
|
+
"""
|
|
80
|
+
Class to compute various descriptors for a binary image.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
binary_image : ndarray
|
|
85
|
+
Binary image used to compute the descriptors.
|
|
86
|
+
wanted_descriptors_list : list
|
|
87
|
+
List of strings with the names of the wanted descriptors.
|
|
88
|
+
|
|
89
|
+
Attributes
|
|
90
|
+
----------
|
|
91
|
+
binary_image : ndarray
|
|
92
|
+
The binary image.
|
|
93
|
+
descriptors : dict
|
|
94
|
+
Dictionary containing the computed descriptors.
|
|
95
|
+
mo : float or None, optional
|
|
96
|
+
Moment of inertia (default is `None`).
|
|
97
|
+
area : int or None, optional
|
|
98
|
+
Area of the object (default is `None`).
|
|
99
|
+
contours : ndarray or None, optional
|
|
100
|
+
Contours of the object (default is `None`).
|
|
101
|
+
min_bounding_rectangle : tuple or None, optional
|
|
102
|
+
Minimum bounding rectangle of the object (default is `None`).
|
|
103
|
+
convex_hull : ndarray or None, optional
|
|
104
|
+
Convex hull of the object (default is `None`).
|
|
105
|
+
major_axis_len : float or None, optional
|
|
106
|
+
Major axis length of the object (default is `None`).
|
|
107
|
+
minor_axis_len : float or None, optional
|
|
108
|
+
Minor axis length of the object (default is `None`).
|
|
109
|
+
axes_orientation : float or None, optional
|
|
110
|
+
Orientation of the axes (default is `None`).
|
|
111
|
+
sx : float or None, optional
|
|
112
|
+
Standard deviation in x-axis (default is `None`).
|
|
113
|
+
kx : float or None, optional
|
|
114
|
+
Kurtosis in x-axis (default is `None`).
|
|
115
|
+
skx : float or None, optional
|
|
116
|
+
Skewness in x-axis (default is `None`).
|
|
117
|
+
perimeter : float or None, optional
|
|
118
|
+
Perimeter of the object (default is `None`).
|
|
119
|
+
convexity : float or None, optional
|
|
120
|
+
Convexity of the object (default is `None`).
|
|
121
|
+
|
|
122
|
+
Examples
|
|
123
|
+
--------
|
|
124
|
+
>>> binary_image = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8)
|
|
125
|
+
>>> wanted_descriptors_list = ["area", "perimeter"]
|
|
126
|
+
>>> SD = ShapeDescriptors(binary_image, wanted_descriptors_list)
|
|
127
|
+
>>> SD.descriptors
|
|
128
|
+
{'area': np.uint64(9), 'perimeter': 8.0}
|
|
129
|
+
"""
|
|
130
|
+
# Give a None value to each parameters whose presence is assessed before calculation (less calculus for speed)
|
|
131
|
+
self.mo = None
|
|
132
|
+
self.area = None
|
|
133
|
+
self.contours = None
|
|
134
|
+
self.min_bounding_rectangle = None
|
|
135
|
+
self.convex_hull = None
|
|
136
|
+
self.major_axis_len = None
|
|
137
|
+
self.minor_axis_len = None
|
|
138
|
+
self.axes_orientation = None
|
|
139
|
+
self.sx = None
|
|
140
|
+
self.kx = None
|
|
141
|
+
self.skx = None
|
|
142
|
+
self.perimeter = None
|
|
143
|
+
self.convexity = None
|
|
144
|
+
|
|
145
|
+
self.binary_image = binary_image
|
|
146
|
+
if self.binary_image.dtype == 'bool':
|
|
147
|
+
self.binary_image = self.binary_image.astype(np.uint8)
|
|
148
|
+
|
|
149
|
+
self.descriptors = {i: np.empty(0, dtype=np.float64) for i in wanted_descriptors_list}
|
|
150
|
+
self.get_area()
|
|
151
|
+
|
|
152
|
+
for name in self.descriptors.keys():
|
|
153
|
+
if self.area == 0:
|
|
154
|
+
self.descriptors[name] = 0
|
|
155
|
+
else:
|
|
156
|
+
if name == "mo":
|
|
157
|
+
self.get_mo()
|
|
158
|
+
self.descriptors[name] = self.mo
|
|
159
|
+
elif name == "area":
|
|
160
|
+
self.descriptors[name] = self.area
|
|
161
|
+
elif name == "contours":
|
|
162
|
+
self.get_contours()
|
|
163
|
+
self.descriptors[name] = self.contours
|
|
164
|
+
elif name == "min_bounding_rectangle":
|
|
165
|
+
self.get_min_bounding_rectangle()
|
|
166
|
+
self.descriptors[name] = self.min_bounding_rectangle
|
|
167
|
+
elif name == "major_axis_len":
|
|
168
|
+
self.get_major_axis_len()
|
|
169
|
+
self.descriptors[name] = self.major_axis_len
|
|
170
|
+
elif name == "minor_axis_len":
|
|
171
|
+
self.get_minor_axis_len()
|
|
172
|
+
self.descriptors[name] = self.minor_axis_len
|
|
173
|
+
elif name == "axes_orientation":
|
|
174
|
+
self.get_inertia_axes()
|
|
175
|
+
self.descriptors[name] = self.axes_orientation
|
|
176
|
+
elif name == "standard_deviation_y":
|
|
177
|
+
self.get_standard_deviations()
|
|
178
|
+
self.descriptors[name] = self.sy
|
|
179
|
+
elif name == "standard_deviation_x":
|
|
180
|
+
self.get_standard_deviations()
|
|
181
|
+
self.descriptors[name] = self.sx
|
|
182
|
+
elif name == "skewness_y":
|
|
183
|
+
self.get_skewness()
|
|
184
|
+
self.descriptors[name] = self.sky
|
|
185
|
+
elif name == "skewness_x":
|
|
186
|
+
self.get_skewness()
|
|
187
|
+
self.descriptors[name] = self.skx
|
|
188
|
+
elif name == "kurtosis_y":
|
|
189
|
+
self.get_kurtosis()
|
|
190
|
+
self.descriptors[name] = self.ky
|
|
191
|
+
elif name == "kurtosis_x":
|
|
192
|
+
self.get_kurtosis()
|
|
193
|
+
self.descriptors[name] = self.kx
|
|
194
|
+
elif name == "convex_hull":
|
|
195
|
+
self.get_convex_hull()
|
|
196
|
+
self.descriptors[name] = self.convex_hull
|
|
197
|
+
elif name == "perimeter":
|
|
198
|
+
self.get_perimeter()
|
|
199
|
+
self.descriptors[name] = self.perimeter
|
|
200
|
+
elif name == "circularity":
|
|
201
|
+
self.get_circularity()
|
|
202
|
+
self.descriptors[name] = self.circularity
|
|
203
|
+
elif name == "rectangularity":
|
|
204
|
+
self.get_rectangularity()
|
|
205
|
+
self.descriptors[name] = self.rectangularity
|
|
206
|
+
elif name == "total_hole_area":
|
|
207
|
+
self.get_total_hole_area()
|
|
208
|
+
self.descriptors[name] = self.total_hole_area
|
|
209
|
+
elif name == "solidity":
|
|
210
|
+
self.get_solidity()
|
|
211
|
+
self.descriptors[name] = self.solidity
|
|
212
|
+
elif name == "convexity":
|
|
213
|
+
self.get_convexity()
|
|
214
|
+
self.descriptors[name] = self.convexity
|
|
215
|
+
elif name == "eccentricity":
|
|
216
|
+
self.get_eccentricity()
|
|
217
|
+
self.descriptors[name] = self.eccentricity
|
|
218
|
+
elif name == "euler_number":
|
|
219
|
+
self.get_euler_number()
|
|
220
|
+
self.descriptors[name] = self.euler_number
|
|
221
|
+
|
|
222
|
+
"""
|
|
223
|
+
The following methods can be called to compute parameters for descriptors requiring it
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
def get_mo(self):
|
|
227
|
+
"""
|
|
228
|
+
Get moments of a binary image.
|
|
229
|
+
|
|
230
|
+
Calculate the image moments for a given binary image using OpenCV's
|
|
231
|
+
`cv2.moments` function and then translate these moments into a formatted
|
|
232
|
+
dictionary.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
self : object
|
|
237
|
+
The instance of the class containing this method.
|
|
238
|
+
binary_image : numpy.ndarray
|
|
239
|
+
A binary image (2D array) where pixels are 0 or 255.
|
|
240
|
+
|
|
241
|
+
Other Parameters
|
|
242
|
+
----------------
|
|
243
|
+
None
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
dict
|
|
248
|
+
A dictionary containing the translated moments of the binary image.
|
|
249
|
+
|
|
250
|
+
Raises
|
|
251
|
+
------
|
|
252
|
+
TypeError
|
|
253
|
+
If `binary_image` is not a NumPy array.
|
|
254
|
+
|
|
255
|
+
Notes
|
|
256
|
+
-----
|
|
257
|
+
This function assumes the binary image has already been processed and is in a
|
|
258
|
+
suitable format for moment calculation.
|
|
259
|
+
|
|
260
|
+
Examples
|
|
261
|
+
--------
|
|
262
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["mo"])
|
|
263
|
+
>>> print(SD.mo["m00"])
|
|
264
|
+
9.0
|
|
265
|
+
"""
|
|
266
|
+
self.mo = translate_dict(cv2.moments(self.binary_image))
|
|
267
|
+
|
|
268
|
+
def get_area(self):
|
|
269
|
+
"""
|
|
270
|
+
Calculate the area of a binary image by summing its pixel values.
|
|
271
|
+
|
|
272
|
+
This function computes the area covered by white pixels (value 1) in a binary image,
|
|
273
|
+
which is equivalent to counting the number of 'on' pixels.
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
self : object
|
|
278
|
+
The instance of a class containing the binary_image attribute.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
int
|
|
283
|
+
The total number of white pixels in the binary image, representing its area.
|
|
284
|
+
|
|
285
|
+
Notes
|
|
286
|
+
-----
|
|
287
|
+
This function assumes the binary_image attribute is a NumPy array containing only 0s and 1s.
|
|
288
|
+
If the image contains other values, this function might not produce accurate results.
|
|
289
|
+
|
|
290
|
+
Examples
|
|
291
|
+
--------
|
|
292
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["area"])
|
|
293
|
+
>>> print(SD.area)
|
|
294
|
+
9.0
|
|
295
|
+
"""
|
|
296
|
+
self.area = self.binary_image.sum()
|
|
297
|
+
|
|
298
|
+
def get_contours(self):
|
|
299
|
+
"""
|
|
300
|
+
Find and process the largest contour in a binary image.
|
|
301
|
+
|
|
302
|
+
Retrieves contours from a binary image, calculates the Euler number,
|
|
303
|
+
and identifies the largest contour based on its length.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
self : ImageProcessingObject
|
|
308
|
+
The image processing object containing the binary image.
|
|
309
|
+
|
|
310
|
+
Other Parameters
|
|
311
|
+
----------------
|
|
312
|
+
None
|
|
313
|
+
|
|
314
|
+
Returns
|
|
315
|
+
-------
|
|
316
|
+
None
|
|
317
|
+
|
|
318
|
+
Raises
|
|
319
|
+
------
|
|
320
|
+
None
|
|
321
|
+
|
|
322
|
+
Notes
|
|
323
|
+
-----
|
|
324
|
+
This function modifies the internal state of the `self` object to store
|
|
325
|
+
the largest contour and Euler number.
|
|
326
|
+
|
|
327
|
+
Examples
|
|
328
|
+
--------
|
|
329
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["euler_number"])
|
|
330
|
+
>>> print(len(SD.contours))
|
|
331
|
+
8
|
|
332
|
+
"""
|
|
333
|
+
contours, hierarchy = cv2.findContours(self.binary_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
|
334
|
+
nb, shapes = cv2.connectedComponents(self.binary_image, ltype=cv2.CV_16U)
|
|
335
|
+
self.euler_number = (nb - 1) - len(contours)
|
|
336
|
+
self.contours = contours[0]
|
|
337
|
+
if len(contours) > 1:
|
|
338
|
+
all_lengths = np.zeros(len(contours))
|
|
339
|
+
for i, contour in enumerate(contours):
|
|
340
|
+
all_lengths[i] = len(contour)
|
|
341
|
+
self.contours = contours[np.argmax(all_lengths)]
|
|
342
|
+
|
|
343
|
+
def get_min_bounding_rectangle(self):
|
|
344
|
+
"""
|
|
345
|
+
Retrieve the minimum bounding rectangle from the contours of an image.
|
|
346
|
+
|
|
347
|
+
This method calculates the smallest area rectangle that can enclose
|
|
348
|
+
the object outlines present in the image, which is useful for
|
|
349
|
+
object detection and analysis tasks.
|
|
350
|
+
|
|
351
|
+
Parameters
|
|
352
|
+
----------
|
|
353
|
+
None
|
|
354
|
+
|
|
355
|
+
Returns
|
|
356
|
+
-------
|
|
357
|
+
tuple
|
|
358
|
+
A tuple containing the following elements:
|
|
359
|
+
- (cx, cy): The center point of the rectangle.
|
|
360
|
+
- (width, height): Width and height of the bounding rectangle.
|
|
361
|
+
- angle: Angle in degrees describing how much the ellipse is rotated.
|
|
362
|
+
|
|
363
|
+
Raises
|
|
364
|
+
------
|
|
365
|
+
RuntimeError
|
|
366
|
+
If the contours are not available and cannot be retrieved,
|
|
367
|
+
indicating a problem with the image or preprocessing steps.
|
|
368
|
+
|
|
369
|
+
Notes
|
|
370
|
+
-----
|
|
371
|
+
- The bounding rectangle is calculated only if contours are available.
|
|
372
|
+
If not, they will be retrieved first before calculating the rectangle.
|
|
373
|
+
|
|
374
|
+
Examples
|
|
375
|
+
--------
|
|
376
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["rectangularity"])
|
|
377
|
+
>>> print(len(SD.min_bounding_rectangle))
|
|
378
|
+
3
|
|
379
|
+
"""
|
|
380
|
+
if self.contours is None:
|
|
381
|
+
self.get_contours()
|
|
382
|
+
self.min_bounding_rectangle = cv2.minAreaRect(self.contours) # ((cx, cy), (width, height), angle)
|
|
383
|
+
|
|
384
|
+
def get_inertia_axes(self):
|
|
385
|
+
"""
|
|
386
|
+
Calculate and set the moments of inertia properties of an object.
|
|
387
|
+
|
|
388
|
+
This function computes the centroid, major axis length,
|
|
389
|
+
minor axis length, and axes orientation for an object. It
|
|
390
|
+
first ensures that the moments of inertia (`mo`) attribute is available,
|
|
391
|
+
computing them if necessary, before using the `get_inertia_axes` function.
|
|
392
|
+
|
|
393
|
+
Returns
|
|
394
|
+
-------
|
|
395
|
+
None
|
|
396
|
+
|
|
397
|
+
This method sets the following attributes:
|
|
398
|
+
- `cx` : float
|
|
399
|
+
The x-coordinate of the centroid.
|
|
400
|
+
- `cy` : float
|
|
401
|
+
The y-coordinate of the centroid.
|
|
402
|
+
- `major_axis_len` : float
|
|
403
|
+
The length of the major axis.
|
|
404
|
+
- `minor_axis_len` : float
|
|
405
|
+
The length of the minor axis.
|
|
406
|
+
- `axes_orientation` : float
|
|
407
|
+
The orientation angle of the axes.
|
|
408
|
+
|
|
409
|
+
Raises
|
|
410
|
+
------
|
|
411
|
+
ValueError
|
|
412
|
+
If there is an issue with the moments of inertia computation.
|
|
413
|
+
|
|
414
|
+
Notes
|
|
415
|
+
-----
|
|
416
|
+
This function modifies in-place the object's attributes related to its geometry.
|
|
417
|
+
|
|
418
|
+
Examples
|
|
419
|
+
--------
|
|
420
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["major_axis_len"])
|
|
421
|
+
>>> print(SD.axes_orientation)
|
|
422
|
+
0.0
|
|
423
|
+
"""
|
|
424
|
+
if self.mo is None:
|
|
425
|
+
self.get_mo()
|
|
426
|
+
|
|
427
|
+
self.cx, self.cy, self.major_axis_len, self.minor_axis_len, self.axes_orientation = get_inertia_axes(self.mo)
|
|
428
|
+
|
|
429
|
+
def get_standard_deviations(self):
|
|
430
|
+
"""
|
|
431
|
+
Calculate the standard deviations along x and y.
|
|
432
|
+
|
|
433
|
+
Parameters
|
|
434
|
+
----------
|
|
435
|
+
moment_order : int
|
|
436
|
+
The order of moments to consider in calculations.
|
|
437
|
+
binary_image : numpy.ndarray
|
|
438
|
+
A 2D binary image where the shape corresponds to `[height, width]`.
|
|
439
|
+
center_x : float
|
|
440
|
+
The x-coordinate of the centroid of the object in the image.
|
|
441
|
+
center_y : float
|
|
442
|
+
The y-coordinate of the centroid of the object in the image.
|
|
443
|
+
|
|
444
|
+
Returns
|
|
445
|
+
-------
|
|
446
|
+
tuple[float, float]
|
|
447
|
+
A tuple containing the standard deviations along x and y (sx, sy).
|
|
448
|
+
|
|
449
|
+
Notes
|
|
450
|
+
-----
|
|
451
|
+
The function calculates the standard deviations of a binary image about its centroid.
|
|
452
|
+
|
|
453
|
+
Examples
|
|
454
|
+
--------
|
|
455
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["standard_deviation_x", "standard_deviation_y"])
|
|
456
|
+
>>> print(SD.sx, SD.sy)
|
|
457
|
+
0.816496580927726 0.816496580927726
|
|
458
|
+
"""
|
|
459
|
+
if self.sx is None:
|
|
460
|
+
if self.axes_orientation is None:
|
|
461
|
+
self.get_inertia_axes()
|
|
462
|
+
self.sx, self.sy = get_standard_deviations(self.mo, self.binary_image, self.cx, self.cy)
|
|
463
|
+
|
|
464
|
+
def get_skewness(self):
|
|
465
|
+
"""
|
|
466
|
+
Calculate the skewness of an image.
|
|
467
|
+
|
|
468
|
+
This function computes the skewness about the x-axis and y-axis of
|
|
469
|
+
an image. Skewness is a measure of the asymmetry of the probability
|
|
470
|
+
distribution of values in an image.
|
|
471
|
+
|
|
472
|
+
Parameters
|
|
473
|
+
----------
|
|
474
|
+
binary_image : numpy.ndarray
|
|
475
|
+
A binary image represented as a 2D array of integers, where 0 represents
|
|
476
|
+
background and other values represent foreground.
|
|
477
|
+
mo : dict
|
|
478
|
+
Moments of the image.
|
|
479
|
+
cx, cy : float
|
|
480
|
+
The x and y coordinates of the centroid of the object in the image.
|
|
481
|
+
sx, sy : float
|
|
482
|
+
The standard deviations along the x and y axes.
|
|
483
|
+
|
|
484
|
+
Other Parameters
|
|
485
|
+
----------------
|
|
486
|
+
None
|
|
487
|
+
|
|
488
|
+
Returns
|
|
489
|
+
-------
|
|
490
|
+
skx, sky : tuple of float
|
|
491
|
+
The skewness about the x-axis and y-axis.
|
|
492
|
+
|
|
493
|
+
Notes
|
|
494
|
+
-----
|
|
495
|
+
This method internally calls `get_standard_deviations` if the standard deviation
|
|
496
|
+
values are not already computed.
|
|
497
|
+
|
|
498
|
+
Examples
|
|
499
|
+
--------
|
|
500
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["skewness_x", "skewness_y"])
|
|
501
|
+
>>> print(SD.skx, SD.sky)
|
|
502
|
+
0.0 0.0
|
|
503
|
+
"""
|
|
504
|
+
if self.skx is None:
|
|
505
|
+
if self.sx is None:
|
|
506
|
+
self.get_standard_deviations()
|
|
507
|
+
|
|
508
|
+
self.skx, self.sky = get_skewness(self.mo, self.binary_image, self.cx, self.cy, self.sx, self.sy)
|
|
509
|
+
|
|
510
|
+
def get_kurtosis(self):
|
|
511
|
+
"""
|
|
512
|
+
Calculates the kurtosis of the image moments.
|
|
513
|
+
|
|
514
|
+
Kurtosis is a statistical measure that describes the shape of
|
|
515
|
+
a distribution's tails in relation to its overall shape. It is
|
|
516
|
+
used here in the context of image moments analysis.
|
|
517
|
+
|
|
518
|
+
Notes
|
|
519
|
+
-----
|
|
520
|
+
This function first checks if the kurtosis values have already been calculated.
|
|
521
|
+
If not, it calculates them using the `get_kurtosis` function.
|
|
522
|
+
|
|
523
|
+
Examples
|
|
524
|
+
--------
|
|
525
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["kurtosis_x", "kurtosis_y"])
|
|
526
|
+
>>> print(SD.kx, SD.ky)
|
|
527
|
+
1.5 1.5
|
|
528
|
+
"""
|
|
529
|
+
if self.kx is None:
|
|
530
|
+
if self.sx is None:
|
|
531
|
+
self.get_standard_deviations()
|
|
532
|
+
|
|
533
|
+
self.kx, self.ky = get_kurtosis(self.mo, self.binary_image, self.cx, self.cy, self.sx, self.sy)
|
|
534
|
+
|
|
535
|
+
def get_convex_hull(self):
|
|
536
|
+
"""
|
|
537
|
+
Compute the convex hull of an object's contours.
|
|
538
|
+
|
|
539
|
+
This method calculates the convex hull for the object represented by its
|
|
540
|
+
contours. If the contours are not already computed, it will first compute them.
|
|
541
|
+
|
|
542
|
+
Parameters
|
|
543
|
+
----------
|
|
544
|
+
self : Object
|
|
545
|
+
The object containing the contours and convex hull attributes.
|
|
546
|
+
|
|
547
|
+
Returns
|
|
548
|
+
-------
|
|
549
|
+
None
|
|
550
|
+
|
|
551
|
+
Notes
|
|
552
|
+
-----
|
|
553
|
+
This method modifies the object in place by setting its `convex_hull`
|
|
554
|
+
attribute to the result of the convex hull computation.
|
|
555
|
+
|
|
556
|
+
Examples
|
|
557
|
+
--------
|
|
558
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["solidity"])
|
|
559
|
+
>>> print(len(SD.convex_hull))
|
|
560
|
+
4
|
|
561
|
+
"""
|
|
562
|
+
if self.contours is None:
|
|
563
|
+
self.get_contours()
|
|
564
|
+
self.convex_hull = cv2.convexHull(self.contours)
|
|
565
|
+
|
|
566
|
+
def get_perimeter(self):
|
|
567
|
+
"""
|
|
568
|
+
Get the perimeter of a contour.
|
|
569
|
+
|
|
570
|
+
Calculates the perimeter length of the contours present in
|
|
571
|
+
the image after determining them if they are not already available.
|
|
572
|
+
|
|
573
|
+
Parameters
|
|
574
|
+
----------
|
|
575
|
+
None
|
|
576
|
+
|
|
577
|
+
Returns
|
|
578
|
+
-------
|
|
579
|
+
float
|
|
580
|
+
The perimeter length of the contours in the image.
|
|
581
|
+
|
|
582
|
+
Notes
|
|
583
|
+
-----
|
|
584
|
+
This function retrieves the contours if they have not been determined
|
|
585
|
+
yet using `self.get_contours()`. This is crucial for accurate perimeter
|
|
586
|
+
measurement.
|
|
587
|
+
|
|
588
|
+
Examples
|
|
589
|
+
--------
|
|
590
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["perimeter"])
|
|
591
|
+
>>> print(SD.perimeter)
|
|
592
|
+
8.0
|
|
593
|
+
"""
|
|
594
|
+
if self.contours is None:
|
|
595
|
+
self.get_contours()
|
|
596
|
+
self.perimeter = cv2.arcLength(self.contours, True)
|
|
597
|
+
|
|
598
|
+
def get_circularity(self):
|
|
599
|
+
"""
|
|
600
|
+
Calculate and set the circularity of a binary image object.
|
|
601
|
+
|
|
602
|
+
Circularity is defined as (4 * pi * area) / perimeter^2.
|
|
603
|
+
If the perimeter has not been calculated yet, it will be computed first.
|
|
604
|
+
|
|
605
|
+
Parameters
|
|
606
|
+
----------
|
|
607
|
+
self : ShapeObject
|
|
608
|
+
The object which contains the binary image and its properties.
|
|
609
|
+
|
|
610
|
+
Attributes
|
|
611
|
+
----------
|
|
612
|
+
circularity : float
|
|
613
|
+
The calculated circularity value, set as an attribute of the object.
|
|
614
|
+
|
|
615
|
+
Returns
|
|
616
|
+
-------
|
|
617
|
+
None
|
|
618
|
+
|
|
619
|
+
Notes
|
|
620
|
+
-----
|
|
621
|
+
Circularity is a measure of how closely the shape of an object approximates a circle.
|
|
622
|
+
A perfect circle has a circularity of 1.0.
|
|
623
|
+
|
|
624
|
+
Examples
|
|
625
|
+
--------
|
|
626
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["circularity"])
|
|
627
|
+
>>> print(SD.circularity)
|
|
628
|
+
1.7671458676442586
|
|
629
|
+
"""
|
|
630
|
+
if self.perimeter is None:
|
|
631
|
+
self.get_perimeter()
|
|
632
|
+
if self.perimeter == 0:
|
|
633
|
+
self.circularity = 0
|
|
634
|
+
else:
|
|
635
|
+
self.circularity = (4 * np.pi * self.binary_image.sum()) / np.square(self.perimeter)
|
|
636
|
+
|
|
637
|
+
def get_rectangularity(self):
|
|
638
|
+
"""
|
|
639
|
+
Calculates the rectangularity of a binary image.
|
|
640
|
+
|
|
641
|
+
Rectangularity is defined as the ratio of the number of pixels in
|
|
642
|
+
the shape to the area of its bounding rectangle. This function
|
|
643
|
+
computes this value by considering the binary image stored in
|
|
644
|
+
`self.binary_image`.
|
|
645
|
+
|
|
646
|
+
Parameters
|
|
647
|
+
----------
|
|
648
|
+
None
|
|
649
|
+
|
|
650
|
+
Other Parameters
|
|
651
|
+
----------------
|
|
652
|
+
None
|
|
653
|
+
|
|
654
|
+
Returns
|
|
655
|
+
-------
|
|
656
|
+
float
|
|
657
|
+
The rectangularity of the binary image.
|
|
658
|
+
|
|
659
|
+
Raises
|
|
660
|
+
------
|
|
661
|
+
None
|
|
662
|
+
|
|
663
|
+
Notes
|
|
664
|
+
-----
|
|
665
|
+
This function uses `self.binary_image` to determine the number of pixels in
|
|
666
|
+
the shape and `self.min_bounding_rectangle` to determine the bounding rectangle area.
|
|
667
|
+
If the minimum bounding rectangle has not been computed yet, it will be calculated
|
|
668
|
+
through `self.get_min_bounding_rectangle()`.
|
|
669
|
+
|
|
670
|
+
Attributes
|
|
671
|
+
----------
|
|
672
|
+
None
|
|
673
|
+
|
|
674
|
+
Examples
|
|
675
|
+
--------
|
|
676
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["rectangularity"])
|
|
677
|
+
>>> print(SD.rectangularity)
|
|
678
|
+
2.25
|
|
679
|
+
"""
|
|
680
|
+
if self.min_bounding_rectangle is None:
|
|
681
|
+
self.get_min_bounding_rectangle()
|
|
682
|
+
bounding_rectangle_area = self.min_bounding_rectangle[1][0] * self.min_bounding_rectangle[1][1]
|
|
683
|
+
if bounding_rectangle_area != 0:
|
|
684
|
+
self.rectangularity = self.binary_image.sum() / bounding_rectangle_area
|
|
685
|
+
else:
|
|
686
|
+
self.rectangularity = 1
|
|
687
|
+
|
|
688
|
+
def get_total_hole_area(self):
|
|
689
|
+
"""
|
|
690
|
+
Calculate the total area of holes in a binary image.
|
|
691
|
+
|
|
692
|
+
This function uses connected component labeling to detect and
|
|
693
|
+
measure the area of holes in a binary image.
|
|
694
|
+
|
|
695
|
+
Returns
|
|
696
|
+
-------
|
|
697
|
+
float
|
|
698
|
+
The total area of all detected holes in the binary image.
|
|
699
|
+
|
|
700
|
+
Notes
|
|
701
|
+
-----
|
|
702
|
+
This function assumes that the binary image has been pre-processed
|
|
703
|
+
and that holes are represented as connected components of zero
|
|
704
|
+
pixels within the foreground
|
|
705
|
+
|
|
706
|
+
Examples
|
|
707
|
+
--------
|
|
708
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["total_hole_area"])
|
|
709
|
+
>>> print(SD.total_hole_area)
|
|
710
|
+
0
|
|
711
|
+
"""
|
|
712
|
+
# FIRST VERSION
|
|
713
|
+
# nb, new_order, stats, centers = cv2.connectedComponentsWithStats(1 - self.binary_image)
|
|
714
|
+
# if stats.shape[0] > 2:
|
|
715
|
+
# self.total_hole_area = stats[2:, 4].sum()
|
|
716
|
+
# else:
|
|
717
|
+
# self.total_hole_area = 0
|
|
718
|
+
# tic = default_timer()
|
|
719
|
+
# SECOND VERSION
|
|
720
|
+
# nb, new_order = cv2.connectedComponents(1 - self.binary_image)
|
|
721
|
+
# if nb <= 1:
|
|
722
|
+
# self.total_hole_area = 0
|
|
723
|
+
# else:
|
|
724
|
+
# label_counts = np.bincount(new_order.flatten())
|
|
725
|
+
# self.total_hole_area = label_counts[2:].sum()
|
|
726
|
+
# tac = default_timer()
|
|
727
|
+
# print( tac-tic)
|
|
728
|
+
# THIDS VERSION
|
|
729
|
+
nb, new_order = cv2.connectedComponents(1 - self.binary_image)
|
|
730
|
+
if nb > 2:
|
|
731
|
+
self.total_hole_area = (new_order > 2).sum()
|
|
732
|
+
else:
|
|
733
|
+
self.total_hole_area = 0
|
|
734
|
+
|
|
735
|
+
def get_solidity(self):
|
|
736
|
+
"""
|
|
737
|
+
Calculate the solidity of a contour, which is the ratio of the area
|
|
738
|
+
of the contour to the area of its convex hull.
|
|
739
|
+
|
|
740
|
+
Extended Summary
|
|
741
|
+
----------------
|
|
742
|
+
The solidity is a dimensionless measure that compares the area of a shape to
|
|
743
|
+
its convex hull. A solidity of 1 means the contour is fully convex, while a
|
|
744
|
+
value less than 1 indicates concavities.
|
|
745
|
+
|
|
746
|
+
Parameters
|
|
747
|
+
----------
|
|
748
|
+
None
|
|
749
|
+
|
|
750
|
+
Other Parameters
|
|
751
|
+
-----------------
|
|
752
|
+
None
|
|
753
|
+
|
|
754
|
+
Returns
|
|
755
|
+
-------
|
|
756
|
+
float
|
|
757
|
+
The solidity of the contour.
|
|
758
|
+
|
|
759
|
+
Raises
|
|
760
|
+
------
|
|
761
|
+
None
|
|
762
|
+
|
|
763
|
+
Notes
|
|
764
|
+
-----
|
|
765
|
+
The solidity is computed by dividing the area of the contour by the area of its
|
|
766
|
+
convex hull. If the convex hull is empty, solidity defaults to 1.
|
|
767
|
+
|
|
768
|
+
Attributes
|
|
769
|
+
----------
|
|
770
|
+
self.convex_hull : array-like or None
|
|
771
|
+
The convex hull of the contour.
|
|
772
|
+
self.solidity : float
|
|
773
|
+
The calculated solidity value of the contour.
|
|
774
|
+
|
|
775
|
+
Examples
|
|
776
|
+
--------
|
|
777
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["solidity"])
|
|
778
|
+
>>> print(SD.solidity)
|
|
779
|
+
1.0
|
|
780
|
+
"""
|
|
781
|
+
if self.convex_hull is None:
|
|
782
|
+
self.get_convex_hull()
|
|
783
|
+
conv_h_cont_area = cv2.contourArea(self.convex_hull)
|
|
784
|
+
if conv_h_cont_area != 0:
|
|
785
|
+
self.solidity = cv2.contourArea(self.contours) / cv2.contourArea(self.convex_hull)
|
|
786
|
+
else:
|
|
787
|
+
self.solidity = 1
|
|
788
|
+
|
|
789
|
+
def get_convexity(self):
|
|
790
|
+
"""
|
|
791
|
+
Calculate the convexity of a shape.
|
|
792
|
+
|
|
793
|
+
Convexity is defined as the ratio of the length of
|
|
794
|
+
the contour to the perimeter of the convex hull.
|
|
795
|
+
|
|
796
|
+
Parameters
|
|
797
|
+
----------
|
|
798
|
+
None
|
|
799
|
+
|
|
800
|
+
Returns
|
|
801
|
+
-------
|
|
802
|
+
float
|
|
803
|
+
The convexity ratio of the shape.
|
|
804
|
+
|
|
805
|
+
Raises
|
|
806
|
+
------
|
|
807
|
+
None
|
|
808
|
+
|
|
809
|
+
Notes
|
|
810
|
+
-----
|
|
811
|
+
This method requires that both `perimeter` and `convex_hull`
|
|
812
|
+
attributes are computed before calling this method.
|
|
813
|
+
Convexity is a dimensionless quantity and should always be in the range [0, 1].
|
|
814
|
+
|
|
815
|
+
Examples
|
|
816
|
+
--------
|
|
817
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["convexity"])
|
|
818
|
+
>>> print(SD.convexity)
|
|
819
|
+
1.0
|
|
820
|
+
"""
|
|
821
|
+
if self.perimeter is None:
|
|
822
|
+
self.get_perimeter()
|
|
823
|
+
if self.convex_hull is None:
|
|
824
|
+
self.get_convex_hull()
|
|
825
|
+
self.convexity = cv2.arcLength(self.convex_hull, True) / self.perimeter
|
|
826
|
+
|
|
827
|
+
def get_eccentricity(self):
|
|
828
|
+
"""
|
|
829
|
+
Calculate the eccentricity of an ellipsoid based on its major and minor axis lengths.
|
|
830
|
+
|
|
831
|
+
This function computes the eccentricity of an ellipsoid using its major
|
|
832
|
+
and minor axis lengths. The eccentricity is a measure of how much the
|
|
833
|
+
ellipsoid deviates from being circular.
|
|
834
|
+
|
|
835
|
+
Parameters
|
|
836
|
+
----------
|
|
837
|
+
self : Ellipsoid
|
|
838
|
+
The ellipsoid object containing the major and minor axis lengths.
|
|
839
|
+
These values are assumed to be already set.
|
|
840
|
+
|
|
841
|
+
Other Parameters
|
|
842
|
+
----------------
|
|
843
|
+
None
|
|
844
|
+
|
|
845
|
+
Returns
|
|
846
|
+
-------
|
|
847
|
+
float
|
|
848
|
+
The calculated eccentricity of the ellipsoid.
|
|
849
|
+
|
|
850
|
+
Raises
|
|
851
|
+
------
|
|
852
|
+
None
|
|
853
|
+
|
|
854
|
+
Notes
|
|
855
|
+
-----
|
|
856
|
+
- This function assumes that the major and minor axis lengths are already set.
|
|
857
|
+
If not, you must call `self.get_inertia_axes()` to update these values before
|
|
858
|
+
calling this function.
|
|
859
|
+
|
|
860
|
+
Examples
|
|
861
|
+
--------
|
|
862
|
+
>>> SD = ShapeDescriptors(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.uint8), ["eccentricity"])
|
|
863
|
+
>>> print(SD.eccentricity)
|
|
864
|
+
0.0
|
|
865
|
+
"""
|
|
866
|
+
self.get_inertia_axes()
|
|
867
|
+
self.eccentricity = np.sqrt(1 - np.square(self.minor_axis_len / self.major_axis_len))
|
|
868
|
+
|
|
869
|
+
def get_euler_number(self):
|
|
870
|
+
"""
|
|
871
|
+
Get Euler number from the contour data.
|
|
872
|
+
|
|
873
|
+
Calculate and return the Euler characteristic of the current contour or
|
|
874
|
+
contours, which is a topological invariant that describes the shape.
|
|
875
|
+
|
|
876
|
+
Parameters
|
|
877
|
+
----------
|
|
878
|
+
None
|
|
879
|
+
|
|
880
|
+
Returns
|
|
881
|
+
-------
|
|
882
|
+
int or None
|
|
883
|
+
The Euler number of the contour(s). Returns `None` if no contours exist.
|
|
884
|
+
|
|
885
|
+
Raises
|
|
886
|
+
------
|
|
887
|
+
ValueError
|
|
888
|
+
If the Euler number cannot be calculated due to invalid contour data.
|
|
889
|
+
|
|
890
|
+
Notes
|
|
891
|
+
-----
|
|
892
|
+
The Euler characteristic is computed as: ``vertices - edges + faces``.
|
|
893
|
+
This method handles both single contour and multiple contours cases.
|
|
894
|
+
|
|
895
|
+
Examples
|
|
896
|
+
--------
|
|
897
|
+
>>> contour_object.get_euler_number()
|
|
898
|
+
1
|
|
899
|
+
|
|
900
|
+
>>> no_contours = Contour() # Object with no contours
|
|
901
|
+
>>> no_contours.get_euler_number()
|
|
902
|
+
None
|
|
903
|
+
"""
|
|
904
|
+
if self.contours is None:
|
|
905
|
+
self.get_contours()
|
|
906
|
+
|
|
907
|
+
def get_major_axis_len(self):
|
|
908
|
+
"""
|
|
909
|
+
Get the length of the major axis.
|
|
910
|
+
|
|
911
|
+
Calculate or retrieve the length of the major axis, ensuring it is
|
|
912
|
+
computed if not already available.
|
|
913
|
+
|
|
914
|
+
Parameters
|
|
915
|
+
----------
|
|
916
|
+
None
|
|
917
|
+
|
|
918
|
+
Returns
|
|
919
|
+
-------
|
|
920
|
+
float or None
|
|
921
|
+
The length of the major axis. If the major_axis_len could not be
|
|
922
|
+
computed, returns `None`.
|
|
923
|
+
|
|
924
|
+
Raises
|
|
925
|
+
------
|
|
926
|
+
AttributeError
|
|
927
|
+
If the major axis length is not available and cannot be computed.
|
|
928
|
+
|
|
929
|
+
Notes
|
|
930
|
+
-----
|
|
931
|
+
- This method may trigger computation of inertia axes if they haven't been
|
|
932
|
+
precomputed to determine the major axis length.
|
|
933
|
+
|
|
934
|
+
Examples
|
|
935
|
+
--------
|
|
936
|
+
>>> SD = ShapeDescriptors(np.array([[0, 1, 0], [0, 1, 0], [0, 1, 0]], dtype=np.uint8), ["major_axis_len"])
|
|
937
|
+
>>> print(SD.major_axis_len)
|
|
938
|
+
2.8284271247461907
|
|
939
|
+
"""
|
|
940
|
+
if self.major_axis_len is None:
|
|
941
|
+
self.get_inertia_axes()
|
|
942
|
+
|
|
943
|
+
def get_minor_axis_len(self):
|
|
944
|
+
"""
|
|
945
|
+
Get the length of the minor axis.
|
|
946
|
+
|
|
947
|
+
This method returns the calculated length of the minor axis. If
|
|
948
|
+
`self.minor_axis_len` is `None`, it will first compute the inertia axes.
|
|
949
|
+
|
|
950
|
+
Parameters
|
|
951
|
+
----------
|
|
952
|
+
none
|
|
953
|
+
|
|
954
|
+
Returns
|
|
955
|
+
-------
|
|
956
|
+
float:
|
|
957
|
+
The length of the minor axis.
|
|
958
|
+
|
|
959
|
+
Raises
|
|
960
|
+
------
|
|
961
|
+
RuntimeError:
|
|
962
|
+
If the minor axis cannot be calculated. This might happen if there are not enough data points
|
|
963
|
+
to determine the inertia axes.
|
|
964
|
+
|
|
965
|
+
Notes
|
|
966
|
+
-----
|
|
967
|
+
This method will compute the inertia axes if `self.minor_axis_len` is not cached.
|
|
968
|
+
|
|
969
|
+
Examples
|
|
970
|
+
--------
|
|
971
|
+
>>> SD = ShapeDescriptors(np.array([[0, 1, 0], [0, 1, 0], [0, 1, 0]], dtype=np.uint8), ["minor_axis_len"])
|
|
972
|
+
>>> print(SD.minor_axis_len)
|
|
973
|
+
0.0
|
|
974
|
+
"""
|
|
975
|
+
if self.minor_axis_len is None:
|
|
976
|
+
self.get_inertia_axes()
|
|
977
|
+
|
|
978
|
+
def get_axes_orientation(self):
|
|
979
|
+
"""
|
|
980
|
+
|
|
981
|
+
Get the orientation of the axes.
|
|
982
|
+
|
|
983
|
+
Extended summary
|
|
984
|
+
----------------
|
|
985
|
+
|
|
986
|
+
This method retrieves the current orientation of the axes. If the orientation is not already computed, it will compute and store it by calling `get_inertia_axes()`.
|
|
987
|
+
|
|
988
|
+
Parameters
|
|
989
|
+
----------
|
|
990
|
+
None
|
|
991
|
+
|
|
992
|
+
Returns
|
|
993
|
+
-------
|
|
994
|
+
`np.ndarray`
|
|
995
|
+
A 3x3 matrix representing the orientation of the axes.
|
|
996
|
+
|
|
997
|
+
Raises
|
|
998
|
+
------
|
|
999
|
+
None
|
|
1000
|
+
|
|
1001
|
+
Notes
|
|
1002
|
+
-----
|
|
1003
|
+
This method may trigger a computation of inertia axes if they haven't been computed yet.
|
|
1004
|
+
|
|
1005
|
+
Examples
|
|
1006
|
+
--------
|
|
1007
|
+
|
|
1008
|
+
Examples
|
|
1009
|
+
--------
|
|
1010
|
+
>>> SD = ShapeDescriptors(np.array([[0, 1, 0], [0, 1, 0], [0, 1, 0]], dtype=np.uint8), ["axes_orientation"])
|
|
1011
|
+
>>> print(SD.axes_orientation)
|
|
1012
|
+
1.5707963267948966
|
|
1013
|
+
"""
|
|
1014
|
+
if self.axes_orientation is None:
|
|
1015
|
+
self.get_inertia_axes()
|
|
1016
|
+
|