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
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from numba import njit as _real_njit
|
|
3
|
+
|
|
4
|
+
USE_NUMBA = os.getenv("USE_NUMBA", "1") == "1"
|
|
5
|
+
|
|
6
|
+
def njit(*args, **kwargs):
|
|
7
|
+
""" numba.njit decorator that can be disabled. Useful for testing.
|
|
8
|
+
"""
|
|
9
|
+
if USE_NUMBA:
|
|
10
|
+
return _real_njit(*args, **kwargs)
|
|
11
|
+
# test mode: return identity decorator
|
|
12
|
+
def deco(func):
|
|
13
|
+
return func
|
|
14
|
+
return deco
|
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Statistical and geometric analysis tools for numerical arrays.
|
|
4
|
+
|
|
5
|
+
This module provides a collection of functions and unit tests for calculating distances,
|
|
6
|
+
statistical properties (skewness, kurtosis), array transformations, and image moment-based
|
|
7
|
+
analysis. The tools are optimized for applications involving binary images, coordinate data,
|
|
8
|
+
and mathematical modeling operations where performance-critical calculations benefit from
|
|
9
|
+
vectorized or JIT-compiled implementations.
|
|
10
|
+
|
|
11
|
+
Functions:
|
|
12
|
+
eudist : Calculate Euclidean distance between two vectors
|
|
13
|
+
to_uint8 : Convert array to 8-bit unsigned integers using NumBA acceleration
|
|
14
|
+
translate_dict : Transform dictionary structures into alternative formats
|
|
15
|
+
linear_model : Compute y = a*x + b regression model values (JIT-compiled)
|
|
16
|
+
moving_average : Calculate sliding window averages with specified step size
|
|
17
|
+
get_var : Derive variance from image moments and spatial coordinates
|
|
18
|
+
find_common_coord : Identify shared coordinate pairs between two arrays
|
|
19
|
+
get_skewness/get_kurtosis : Calculate third/fourth standardized moment statistics
|
|
20
|
+
sum_of_abs_differences : Compute total absolute differences between arrays (JIT)
|
|
21
|
+
bracket_to_uint8_image_contrast : Convert images to 8-bit with contrast normalization
|
|
22
|
+
find_duplicates_coord : Locate rows with duplicate coordinate values
|
|
23
|
+
get_power_dists : Generate radial distance measures from image centers
|
|
24
|
+
get_inertia_axes : Calculate principal axes of inertia for binary shapes
|
|
25
|
+
|
|
26
|
+
Notes:
|
|
27
|
+
- All Numba-accelerated functions require congruent NumPy arrays as inputs
|
|
28
|
+
- Image processing functions expect binary (boolean/int8) input matrices
|
|
29
|
+
"""
|
|
30
|
+
from copy import deepcopy
|
|
31
|
+
from cellects.utils.decorators import njit
|
|
32
|
+
import numpy as np
|
|
33
|
+
from numpy.typing import NDArray
|
|
34
|
+
from typing import Tuple
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@njit()
|
|
38
|
+
def sum_of_abs_differences(array1: NDArray, array2: NDArray):
|
|
39
|
+
"""
|
|
40
|
+
Compute the sum of absolute differences between two arrays.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
array1 : NDArray
|
|
45
|
+
The first input array.
|
|
46
|
+
array2 : NDArray
|
|
47
|
+
The second input array.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
int
|
|
52
|
+
Sum of absolute differences between elements of `array1` and `array2`.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> arr1 = np.array([1.2, 2.5, -3.7])
|
|
57
|
+
>>> arr2 = np.array([12, 25, -37])
|
|
58
|
+
>>> result = sum_of_abs_differences(arr1, arr2)
|
|
59
|
+
>>> print(result)
|
|
60
|
+
66.6
|
|
61
|
+
"""
|
|
62
|
+
return np.sum(np.absolute(array1 - array2))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@njit()
|
|
66
|
+
def to_uint8(an_array: NDArray):
|
|
67
|
+
"""
|
|
68
|
+
Convert an array to unsigned 8-bit integers.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
an_array : ndarray
|
|
73
|
+
Input array to be converted. It can be of any numeric dtype.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
ndarray
|
|
78
|
+
The input array rounded to the nearest integer and then cast to
|
|
79
|
+
unsigned 8-bit integers.
|
|
80
|
+
|
|
81
|
+
Raises
|
|
82
|
+
------
|
|
83
|
+
TypeError
|
|
84
|
+
If `an_array` is not a ndarray.
|
|
85
|
+
|
|
86
|
+
Notes
|
|
87
|
+
-----
|
|
88
|
+
This function uses Numba's `@njit` decorator for performance optimization.
|
|
89
|
+
|
|
90
|
+
Examples
|
|
91
|
+
--------
|
|
92
|
+
>>> result = to_uint8(np.array([1.2, 2.5, -3.7]))
|
|
93
|
+
>>> print(result)
|
|
94
|
+
[1 3 0]
|
|
95
|
+
"""
|
|
96
|
+
out = np.empty_like(an_array)
|
|
97
|
+
return np.round(an_array, 0, out).astype(np.uint8)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@njit()
|
|
101
|
+
def bracket_to_uint8_image_contrast(image: NDArray):
|
|
102
|
+
"""
|
|
103
|
+
Convert an image with bracket contrast values to uint8 type.
|
|
104
|
+
|
|
105
|
+
This function normalizes an input image by scaling the minimum and maximum
|
|
106
|
+
values of the image to the range [0, 255] and then converts it to uint8
|
|
107
|
+
data type.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
image : ndarray
|
|
112
|
+
Input image as a numpy array with floating-point values.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
ndarray of uint8
|
|
117
|
+
Output image converted to uint8 type after normalization.
|
|
118
|
+
"""
|
|
119
|
+
image -= np.min(image)
|
|
120
|
+
return to_uint8(255 * (image / np.max(image)))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@njit()
|
|
124
|
+
def linear_model(x: NDArray, a: float, b: float) -> float:
|
|
125
|
+
"""
|
|
126
|
+
Perform a linear transformation on input data using slope and intercept.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
x : array_like
|
|
131
|
+
Input data.
|
|
132
|
+
a : float
|
|
133
|
+
Slope coefficient.
|
|
134
|
+
b : float
|
|
135
|
+
Intercept.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
float
|
|
140
|
+
Resulting value from linear transformation: `a` * `x` + `b`.
|
|
141
|
+
|
|
142
|
+
Examples
|
|
143
|
+
--------
|
|
144
|
+
>>> result = linear_model(5, 2.0, 1.5)
|
|
145
|
+
>>> print(result) # doctest: +SKIP
|
|
146
|
+
11.5
|
|
147
|
+
|
|
148
|
+
Notes
|
|
149
|
+
-----
|
|
150
|
+
This function uses Numba's @njit decorator for performance.
|
|
151
|
+
"""
|
|
152
|
+
return a * x + b
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@njit()
|
|
156
|
+
def get_power_dists(binary_image: np.ndarray, cx: float, cy: float, n: int):
|
|
157
|
+
"""
|
|
158
|
+
Calculate the power distributions based on the given center coordinates and exponent.
|
|
159
|
+
|
|
160
|
+
This function computes the `n`th powers of x and y distances from
|
|
161
|
+
a given center point `(cx, cy)` for each pixel in the binary image.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
binary_image : np.ndarray
|
|
166
|
+
A 2D array (binary image) where the power distributions are calculated.
|
|
167
|
+
cx : float
|
|
168
|
+
The x-coordinate of the center point.
|
|
169
|
+
cy : float
|
|
170
|
+
The y-coordinate of the center point.
|
|
171
|
+
n : int
|
|
172
|
+
The exponent for power distribution calculation.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
tuple[np.ndarray, np.ndarray]
|
|
177
|
+
A tuple containing two arrays:
|
|
178
|
+
- The first array contains the `n`th power of x distances from the center.
|
|
179
|
+
- The second array contains the `n`th power of y distances from the center.
|
|
180
|
+
|
|
181
|
+
Notes
|
|
182
|
+
-----
|
|
183
|
+
This function uses Numba's `@njit` decorator for performance optimization.
|
|
184
|
+
Ensure that `binary_image` is a NumPy ndarray to avoid type issues.
|
|
185
|
+
|
|
186
|
+
Examples
|
|
187
|
+
--------
|
|
188
|
+
>>> binary_image = np.zeros((10, 10))
|
|
189
|
+
>>> xn, yn = get_power_dists(binary_image, 5.0, 5.0, 2)
|
|
190
|
+
>>> print(xn.shape), print(yn.shape)
|
|
191
|
+
(10,) (10,)
|
|
192
|
+
"""
|
|
193
|
+
xn = (np.arange(binary_image.shape[1]) - cx) ** n
|
|
194
|
+
yn = (np.arange(binary_image.shape[0]) - cy) ** n
|
|
195
|
+
return xn, yn
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@njit()
|
|
199
|
+
def get_var(mo: dict, binary_image: NDArray, Xn: NDArray, Yn: NDArray) -> Tuple[float, float]:
|
|
200
|
+
"""
|
|
201
|
+
Compute the center of mass in 2D space.
|
|
202
|
+
|
|
203
|
+
This function calculates the weighted average position (centroid) of
|
|
204
|
+
a binary image using given pixel coordinates and moments.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
mo : dict
|
|
209
|
+
Dictionary containing moments of binary image.
|
|
210
|
+
binary_image : ndarray
|
|
211
|
+
2D binary image where non-zero pixels are considered.
|
|
212
|
+
Xn : ndarray
|
|
213
|
+
Array of x-coordinates for each pixel in `binary_image`.
|
|
214
|
+
Yn : ndarray
|
|
215
|
+
Array of y-coordinates for each pixel in `binary_image`.
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
tuple
|
|
220
|
+
A tuple of two floats `(vx, vy)` representing the centroid coordinates.
|
|
221
|
+
|
|
222
|
+
Raises
|
|
223
|
+
------
|
|
224
|
+
ZeroDivisionError
|
|
225
|
+
If `mo['m00']` is zero, indicating no valid pixels in the image.
|
|
226
|
+
The function raises a `ZeroDivisionError`.
|
|
227
|
+
|
|
228
|
+
Notes
|
|
229
|
+
-----
|
|
230
|
+
Performance considerations: This function uses Numba's `@njit` decorator for performance.
|
|
231
|
+
"""
|
|
232
|
+
vx = np.sum(binary_image * Xn) / mo["m00"]
|
|
233
|
+
vy = np.sum(binary_image * Yn) / mo["m00"]
|
|
234
|
+
return vx, vy
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@njit()
|
|
238
|
+
def get_skewness_kurtosis(mnx: float, mny: float, sx: float, sy: float, n: int) -> Tuple[float, float]:
|
|
239
|
+
"""
|
|
240
|
+
Calculates skewness and kurtosis of a distribution.
|
|
241
|
+
|
|
242
|
+
This function computes the skewness and kurtosis from given statistical
|
|
243
|
+
moments, standard deviations, and order of moments.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
mnx : float
|
|
248
|
+
The third moment about the mean for x.
|
|
249
|
+
mny : float
|
|
250
|
+
The fourth moment about the mean for y.
|
|
251
|
+
sx : float
|
|
252
|
+
The standard deviation of x.
|
|
253
|
+
sy : float
|
|
254
|
+
The standard deviation of y.
|
|
255
|
+
n : int
|
|
256
|
+
Order of the moment (3 for skewness, 4 for kurtosis).
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
skewness : float
|
|
261
|
+
The computed skewness.
|
|
262
|
+
kurtosis : float
|
|
263
|
+
The computed kurtosis.
|
|
264
|
+
|
|
265
|
+
Notes
|
|
266
|
+
-----
|
|
267
|
+
This function uses Numba's `@njit` decorator for performance.
|
|
268
|
+
Ensure that the values of `mnx`, `mny`, `sx`, and `sy` are non-zero to avoid division by zero.
|
|
269
|
+
If `n = 3`, the function calculates skewness. If `n = 4`, it calculates kurtosis.
|
|
270
|
+
|
|
271
|
+
Examples
|
|
272
|
+
--------
|
|
273
|
+
>>> skewness, kurtosis = get_skewness_kurtosis(1.5, 2.0, 0.5, 0.75, 3)
|
|
274
|
+
>>> print("Skewness:", skewness)
|
|
275
|
+
Skewness: 8.0
|
|
276
|
+
>>> print("Kurtosis:", kurtosis)
|
|
277
|
+
Kurtosis: nan
|
|
278
|
+
|
|
279
|
+
"""
|
|
280
|
+
return mnx / sx ** n, mny / sy ** n
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def get_standard_deviations(mo: dict, binary_image: NDArray, cx: float, cy: float) -> Tuple[float, float]:
|
|
284
|
+
"""
|
|
285
|
+
Return spatial standard deviations for a given moment and binary image.
|
|
286
|
+
|
|
287
|
+
This function computes the square root of variances along `x` (horizontal)
|
|
288
|
+
and `y` (vertical) axes for the given binary image and moment.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
mo : dict
|
|
293
|
+
Dictionary containing moments of binary image.
|
|
294
|
+
binary_image : ndarray of bool or int8
|
|
295
|
+
The binary input image where the moments are computed.
|
|
296
|
+
cx : float64
|
|
297
|
+
X-coordinate of center of mass (horizontal position).
|
|
298
|
+
cy : float64
|
|
299
|
+
Y-coordinate of center of mass (vertical position).
|
|
300
|
+
|
|
301
|
+
Returns
|
|
302
|
+
-------
|
|
303
|
+
tuple[ndarray of float64, ndarray of float64]
|
|
304
|
+
Tuple containing the standard deviations along the x and y axes.
|
|
305
|
+
|
|
306
|
+
Raises
|
|
307
|
+
------
|
|
308
|
+
ValueError
|
|
309
|
+
If `binary_image` is not a binary image or has an invalid datatype.
|
|
310
|
+
|
|
311
|
+
Notes
|
|
312
|
+
-----
|
|
313
|
+
This function uses the `get_power_dists` and `get_var` functions to compute
|
|
314
|
+
the distributed variances, which are then transformed into standard deviations.
|
|
315
|
+
|
|
316
|
+
Examples
|
|
317
|
+
--------
|
|
318
|
+
>>> import numpy as np
|
|
319
|
+
>>> binary_image = np.array([[0, 1], [1, 0]], dtype=np.int8)
|
|
320
|
+
>>> mo = np.array([[2.0], [3.0]])
|
|
321
|
+
>>> cx, cy = 1.5, 1.5
|
|
322
|
+
>>> stdx, stdy = get_standard_deviations(mo, binary_image, cx, cy)
|
|
323
|
+
>>> print(stdx)
|
|
324
|
+
[1.1]
|
|
325
|
+
>>> print(stdy)
|
|
326
|
+
[0.8366600265...]
|
|
327
|
+
"""
|
|
328
|
+
x2, y2 = get_power_dists(binary_image, cx, cy, 2)
|
|
329
|
+
X2, Y2 = np.meshgrid(x2, y2)
|
|
330
|
+
vx, vy = get_var(mo, binary_image, X2, Y2)
|
|
331
|
+
return np.sqrt(vx), np.sqrt(vy)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def get_skewness(mo: dict, binary_image: NDArray, cx: float, cy: float, sx: float, sy: float) -> Tuple[float, float]:
|
|
335
|
+
"""Calculate skewness of the given moment.
|
|
336
|
+
|
|
337
|
+
This function computes the skewness based on the third moments
|
|
338
|
+
and the central moments of a binary image.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
mo : dict
|
|
343
|
+
Dictionary containing moments of binary image.
|
|
344
|
+
binary_image : ndarray
|
|
345
|
+
Binary image as a 2D numpy array.
|
|
346
|
+
cx : float
|
|
347
|
+
Description of parameter `cx`.
|
|
348
|
+
cy : float
|
|
349
|
+
Description of parameter `cy`.
|
|
350
|
+
sx : float
|
|
351
|
+
Description of parameter `sx`.
|
|
352
|
+
sy : float
|
|
353
|
+
Description of parameter `sy`.
|
|
354
|
+
|
|
355
|
+
Returns
|
|
356
|
+
-------
|
|
357
|
+
Tuple[float, float]
|
|
358
|
+
Tuple containing skewness values.
|
|
359
|
+
|
|
360
|
+
Examples
|
|
361
|
+
--------
|
|
362
|
+
>>> result = get_skewness(mo=example_mo, binary_image=binary_img,
|
|
363
|
+
... cx=0.5, cy=0.5, sx=1.0, sy=1.0)
|
|
364
|
+
>>> print(result)
|
|
365
|
+
(skewness_x, skewness_y) # Example output
|
|
366
|
+
"""
|
|
367
|
+
x3, y3 = get_power_dists(binary_image, cx, cy, 3)
|
|
368
|
+
X3, Y3 = np.meshgrid(x3, y3)
|
|
369
|
+
m3x, m3y = get_var(mo, binary_image, X3, Y3)
|
|
370
|
+
return get_skewness_kurtosis(m3x, m3y, sx, sy, 3)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def get_kurtosis(mo: dict, binary_image: NDArray, cx: float, cy: float, sx: float, sy: float) -> Tuple[float, float]:
|
|
374
|
+
"""
|
|
375
|
+
Calculate the kurtosis of a binary image.
|
|
376
|
+
|
|
377
|
+
The function calculates the fourth moment (kurtosis) of the given
|
|
378
|
+
binary image around the specified center coordinates with an option
|
|
379
|
+
to specify the size of the square window.
|
|
380
|
+
|
|
381
|
+
Parameters
|
|
382
|
+
----------
|
|
383
|
+
mo : dict
|
|
384
|
+
Dictionary containing moments of binary image.
|
|
385
|
+
binary_image : np.ndarray
|
|
386
|
+
A 2D numpy ndarray representing a binary image.
|
|
387
|
+
cx : int or float
|
|
388
|
+
The x-coordinate of the center point of the square window.
|
|
389
|
+
cy : int or float
|
|
390
|
+
The y-coordinate of the center point of the square window.
|
|
391
|
+
sx : int or float
|
|
392
|
+
The x-length of the square window (width).
|
|
393
|
+
sy : int or float
|
|
394
|
+
The y-length of the square window (height).
|
|
395
|
+
|
|
396
|
+
Returns
|
|
397
|
+
-------
|
|
398
|
+
float
|
|
399
|
+
The kurtosis value calculated from the moments.
|
|
400
|
+
|
|
401
|
+
Examples
|
|
402
|
+
--------
|
|
403
|
+
>>> mo = np.array([[0, 1], [2, 3]])
|
|
404
|
+
>>> binary_image = np.array([[1, 0], [0, 1]])
|
|
405
|
+
>>> cx = 2
|
|
406
|
+
>>> cy = 3
|
|
407
|
+
>>> sx = 5
|
|
408
|
+
>>> sy = 6
|
|
409
|
+
>>> result = get_kurtosis(mo, binary_image, cx, cy, sx, sy)
|
|
410
|
+
>>> print(result)
|
|
411
|
+
expected output
|
|
412
|
+
"""
|
|
413
|
+
x4, y4 = get_power_dists(binary_image, cx, cy, 4)
|
|
414
|
+
X4, Y4 = np.meshgrid(x4, y4)
|
|
415
|
+
m4x, m4y = get_var(mo, binary_image, X4, Y4)
|
|
416
|
+
return get_skewness_kurtosis(m4x, m4y, sx, sy, 4)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
@njit()
|
|
420
|
+
def get_inertia_axes(mo: dict) -> Tuple[float, float, float, float, float]:
|
|
421
|
+
"""
|
|
422
|
+
Calculate the inertia axes of a moment object.
|
|
423
|
+
|
|
424
|
+
This function computes the barycenters, central moments,
|
|
425
|
+
and the lengths of the major and minor axes, as well as
|
|
426
|
+
their orientation.
|
|
427
|
+
|
|
428
|
+
Parameters
|
|
429
|
+
----------
|
|
430
|
+
mo : dict
|
|
431
|
+
Dictionary containing moments, which should include keys:
|
|
432
|
+
'm00', 'm10', 'm01', 'm20', and 'm11'.
|
|
433
|
+
|
|
434
|
+
Returns
|
|
435
|
+
-------
|
|
436
|
+
tuple
|
|
437
|
+
A tuple containing:
|
|
438
|
+
- cx : float
|
|
439
|
+
The x-coordinate of the barycenter.
|
|
440
|
+
- cy : float
|
|
441
|
+
The y-coordinate of the barycenter.
|
|
442
|
+
- major_axis_len : float
|
|
443
|
+
The length of the major axis.
|
|
444
|
+
- minor_axis_len : float
|
|
445
|
+
The length of the minor axis.
|
|
446
|
+
- axes_orientation : float
|
|
447
|
+
The orientation of the axes in radians.
|
|
448
|
+
|
|
449
|
+
Notes
|
|
450
|
+
-----
|
|
451
|
+
This function uses Numba's @njit decorator for performance.
|
|
452
|
+
The moments in the input dictionary should be computed from
|
|
453
|
+
the same image region.
|
|
454
|
+
|
|
455
|
+
Examples
|
|
456
|
+
--------
|
|
457
|
+
>>> mo = {'m00': 1.0, 'm10': 2.0, 'm01': 3.0, 'm20': 4.0, 'm11': 5.0}
|
|
458
|
+
>>> get_inertia_axes(mo)
|
|
459
|
+
(2.0, 3.0, 9.165151389911677, 0.8421875803239, 0.7853981633974483)
|
|
460
|
+
|
|
461
|
+
"""
|
|
462
|
+
#L. Rocha, L. Velho and P.C.P. Calvalho (2002)
|
|
463
|
+
#http://sibgrapi.sid.inpe.br/col/sid.inpe.br/banon/2002/10.23.11.34/doc/35.pdf
|
|
464
|
+
# http://raphael.candelier.fr/?blog=Image%20Moments
|
|
465
|
+
|
|
466
|
+
# Calculate barycenters
|
|
467
|
+
cx = mo["m10"] / mo["m00"]
|
|
468
|
+
cy = mo["m01"] / mo["m00"]
|
|
469
|
+
# Calculate central moments
|
|
470
|
+
c20 = (mo["m20"] / mo["m00"]) - np.square(cx)
|
|
471
|
+
c02 = (mo["m02"] / mo["m00"]) - np.square(cy)
|
|
472
|
+
c11 = (mo["m11"] / mo["m00"]) - (cx * cy)
|
|
473
|
+
# Calculate major and minor axi lengths OK
|
|
474
|
+
major_axis_len = np.sqrt(6 * (c20 + c02 + np.sqrt(np.square(2 * c11) + np.square(c20 - c02))))
|
|
475
|
+
minor_axis_len = np.sqrt(6 * (c20 + c02 - np.sqrt(np.square(2 * c11) + np.square(c20 - c02))))
|
|
476
|
+
if (c20 - c02) != 0:
|
|
477
|
+
axes_orientation = (0.5 * np.arctan((2 * c11) / (c20 - c02))) + ((c20 < c02) * (np.pi /2))
|
|
478
|
+
else:
|
|
479
|
+
axes_orientation = 0.
|
|
480
|
+
return cx, cy, major_axis_len, minor_axis_len, axes_orientation
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def eudist(v1, v2) -> float:
|
|
484
|
+
"""
|
|
485
|
+
Calculate the Euclidean distance between two points in n-dimensional space.
|
|
486
|
+
|
|
487
|
+
Parameters
|
|
488
|
+
----------
|
|
489
|
+
v1 : iterable of float
|
|
490
|
+
The coordinates of the first point.
|
|
491
|
+
v2 : iterable of float
|
|
492
|
+
The coordinates of the second point.
|
|
493
|
+
|
|
494
|
+
Returns
|
|
495
|
+
-------
|
|
496
|
+
float
|
|
497
|
+
The Euclidean distance between `v1` and `v2`.
|
|
498
|
+
|
|
499
|
+
Raises
|
|
500
|
+
------
|
|
501
|
+
ValueError
|
|
502
|
+
If `v1` and `v2` do not have the same length.
|
|
503
|
+
|
|
504
|
+
Notes
|
|
505
|
+
-----
|
|
506
|
+
The Euclidean distance is calculated using the standard formula:
|
|
507
|
+
√((x2 − x1)^2 + (y2 − y1)^2 + ...).
|
|
508
|
+
|
|
509
|
+
Examples
|
|
510
|
+
--------
|
|
511
|
+
>>> v1 = [1.0, 2.0]
|
|
512
|
+
>>> v2 = [4.0, 6.0]
|
|
513
|
+
>>> eudist(v1, v2)
|
|
514
|
+
5.0
|
|
515
|
+
|
|
516
|
+
>>> v1 = [1.0, 2.0, 3.0]
|
|
517
|
+
>>> v2 = [4.0, 6.0, 8.0]
|
|
518
|
+
>>> eudist(v1, v2)
|
|
519
|
+
7.0710678118654755
|
|
520
|
+
"""
|
|
521
|
+
dist = [(a - b)**2 for a, b in zip(v1, v2)]
|
|
522
|
+
dist = np.sqrt(np.sum(dist))
|
|
523
|
+
return dist
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def moving_average(vector: NDArray, step: int) -> NDArray[float]:
|
|
527
|
+
"""
|
|
528
|
+
Calculate the moving average of a given vector with specified step size.
|
|
529
|
+
|
|
530
|
+
Computes the moving average of input `vector` using specified `step`
|
|
531
|
+
size. NaN values are treated as zeros in the calculation to allow
|
|
532
|
+
for continuous averaging.
|
|
533
|
+
|
|
534
|
+
Parameters
|
|
535
|
+
----------
|
|
536
|
+
vector : ndarray
|
|
537
|
+
Input vector for which to calculate the moving average.
|
|
538
|
+
step : int
|
|
539
|
+
Size of the window for computing the moving average.
|
|
540
|
+
|
|
541
|
+
Returns
|
|
542
|
+
-------
|
|
543
|
+
numpy.ndarray
|
|
544
|
+
Vector containing the moving averages of the input vector.
|
|
545
|
+
|
|
546
|
+
Raises
|
|
547
|
+
------
|
|
548
|
+
ValueError
|
|
549
|
+
If `step` is less than 1.
|
|
550
|
+
ValueError
|
|
551
|
+
If the input vector has no valid (non-NaN) elements.
|
|
552
|
+
|
|
553
|
+
Notes
|
|
554
|
+
-----
|
|
555
|
+
- The function considers NaN values as zeros during the averaging process.
|
|
556
|
+
- If `step` is greater than or equal to the length of the vector, a warning will be raised.
|
|
557
|
+
|
|
558
|
+
Examples
|
|
559
|
+
--------
|
|
560
|
+
>>> import numpy as np
|
|
561
|
+
>>> vector = np.array([1.0, 2.0, np.nan, 4.0, 5.0])
|
|
562
|
+
>>> step = 3
|
|
563
|
+
>>> result = moving_average(vector, step)
|
|
564
|
+
>>> print(result)
|
|
565
|
+
[1.5 2.33333333 3.66666667 4. nan]
|
|
566
|
+
"""
|
|
567
|
+
substep = np.array((- int(np.floor((step - 1) / 2)), int(np.ceil((step - 1) / 2))))
|
|
568
|
+
sums = np.zeros(vector.shape)
|
|
569
|
+
n_okays = deepcopy(sums)
|
|
570
|
+
true_numbers = np.logical_not(np.isnan(vector))
|
|
571
|
+
vector[np.logical_not(true_numbers)] = 0
|
|
572
|
+
for step_i in np.arange(substep[1] + 1):
|
|
573
|
+
sums[step_i: (sums.size - step_i)] = sums[step_i: (sums.size - step_i)] + vector[(2 * step_i):]
|
|
574
|
+
n_okays[step_i: (sums.size - step_i)] = n_okays[step_i: (sums.size - step_i)] + true_numbers[(2 * step_i):]
|
|
575
|
+
if np.logical_and(step_i > 0, step_i < np.absolute(substep[0])):
|
|
576
|
+
sums[step_i: (sums.size - step_i)] = sums[step_i: (sums.size - step_i)] + vector[:(sums.size - (2 * step_i)):]
|
|
577
|
+
n_okays[step_i: (sums.size - step_i)] = n_okays[step_i: (sums.size - step_i)] + true_numbers[:(
|
|
578
|
+
true_numbers.size - (2 * step_i))]
|
|
579
|
+
vector = sums / n_okays
|
|
580
|
+
return vector
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def find_common_coord(array1: NDArray[int], array2: NDArray[int]) -> NDArray[bool]:
|
|
584
|
+
"""Find common coordinates between two arrays.
|
|
585
|
+
|
|
586
|
+
This function compares the given 2D `array1` and `array2`
|
|
587
|
+
to determine if there are any common coordinates.
|
|
588
|
+
|
|
589
|
+
Parameters
|
|
590
|
+
----------
|
|
591
|
+
array1 : ndarray of int
|
|
592
|
+
A 2D numpy ndarray.
|
|
593
|
+
array2 : ndarray of int
|
|
594
|
+
Another 2D numpy ndarray.
|
|
595
|
+
|
|
596
|
+
Returns
|
|
597
|
+
-------
|
|
598
|
+
out : ndarray of bool
|
|
599
|
+
A boolean numpy ndarray where True indicates common
|
|
600
|
+
coordinates.
|
|
601
|
+
|
|
602
|
+
Examples
|
|
603
|
+
--------
|
|
604
|
+
>>> array1 = np.array([[1, 2], [3, 4]])
|
|
605
|
+
>>> array2 = np.array([[5, 6], [1, 2]])
|
|
606
|
+
>>> result = find_common_coord(array1, array2)
|
|
607
|
+
>>> print(result)
|
|
608
|
+
array([ True, False])"""
|
|
609
|
+
return (array1[:, None, :] == array2[None, :, :]).all(-1).any(-1)
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def find_duplicates_coord(array1: NDArray[int]) -> NDArray[bool]:
|
|
613
|
+
"""
|
|
614
|
+
Find duplicate rows in a 2D array and return their coordinate indices.
|
|
615
|
+
|
|
616
|
+
Given a 2D NumPy array, this function identifies rows that are duplicated (i.e., appear more than once) and returns a boolean array indicating their positions.
|
|
617
|
+
|
|
618
|
+
Parameters
|
|
619
|
+
----------
|
|
620
|
+
array1 : ndarray of int
|
|
621
|
+
Input 2D array of shape (n_rows, n_columns) from which to find duplicate rows.
|
|
622
|
+
|
|
623
|
+
Returns
|
|
624
|
+
-------
|
|
625
|
+
duplicates : ndarray of bool
|
|
626
|
+
Boolean array of shape (n_rows,), where `True` indicates that the corresponding row in `array1` is a duplicate.
|
|
627
|
+
|
|
628
|
+
Examples
|
|
629
|
+
--------
|
|
630
|
+
>>> import numpy as np
|
|
631
|
+
>>> array1 = np.array([[1, 2], [3, 4], [1, 2], [5, 6]])
|
|
632
|
+
>>> find_duplicates_coord(array1)
|
|
633
|
+
array([ True, False, True, False])"""
|
|
634
|
+
unique_rows, inverse_indices = np.unique(array1, axis=0, return_inverse=True)
|
|
635
|
+
counts = np.bincount(inverse_indices)
|
|
636
|
+
# A row is duplicate if its count > 1
|
|
637
|
+
return counts[inverse_indices] > 1
|