cellects 0.1.0.dev1__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 +154 -0
- cellects/core/__init__.py +0 -0
- cellects/core/cellects_paths.py +30 -0
- cellects/core/cellects_threads.py +1464 -0
- cellects/core/motion_analysis.py +1931 -0
- cellects/core/one_image_analysis.py +1065 -0
- cellects/core/one_video_per_blob.py +679 -0
- cellects/core/program_organizer.py +1347 -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 +789 -0
- cellects/gui/first_window.py +449 -0
- cellects/gui/if_several_folders_window.py +239 -0
- cellects/gui/image_analysis_window.py +1909 -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/extract_exif.py +61 -0
- cellects/image_analysis/fractal_analysis.py +184 -0
- cellects/image_analysis/fractal_functions.py +108 -0
- cellects/image_analysis/image_segmentation.py +272 -0
- cellects/image_analysis/morphological_operations.py +867 -0
- cellects/image_analysis/network_functions.py +1244 -0
- cellects/image_analysis/one_image_analysis_threads.py +289 -0
- cellects/image_analysis/progressively_add_distant_shapes.py +246 -0
- cellects/image_analysis/shape_descriptors.py +981 -0
- cellects/utils/__init__.py +0 -0
- cellects/utils/formulas.py +881 -0
- cellects/utils/load_display_save.py +1016 -0
- cellects/utils/utilitarian.py +516 -0
- cellects-0.1.0.dev1.dist-info/LICENSE.odt +0 -0
- cellects-0.1.0.dev1.dist-info/METADATA +131 -0
- cellects-0.1.0.dev1.dist-info/RECORD +46 -0
- cellects-0.1.0.dev1.dist-info/WHEEL +5 -0
- cellects-0.1.0.dev1.dist-info/entry_points.txt +2 -0
- cellects-0.1.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
|
|
2
|
+
import numpy as np
|
|
3
|
+
from timeit import default_timer
|
|
4
|
+
import time
|
|
5
|
+
from numba.typed import Dict
|
|
6
|
+
from numba import njit
|
|
7
|
+
from glob import glob
|
|
8
|
+
vectorized_len = np.vectorize(len)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@njit()
|
|
12
|
+
def equal_along_first_axis(array_in_1, array_in_2):
|
|
13
|
+
"""
|
|
14
|
+
Compare two arrays element-wise along the first axis and return a boolean array where elements are equal.
|
|
15
|
+
|
|
16
|
+
This function checks if corresponding elements in two arrays along the first axis
|
|
17
|
+
are equal, returning a boolean array of the same shape as `array_in_1`.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
array_in_1 : ndarray
|
|
22
|
+
First array to compare.
|
|
23
|
+
array_in_2 : ndarray
|
|
24
|
+
Second array to compare.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
ndarray
|
|
29
|
+
Boolean array indicating equality of elements along the first axis.
|
|
30
|
+
Has the same shape as `array_in_1` and dtype of `np.bool_`.
|
|
31
|
+
|
|
32
|
+
Raises
|
|
33
|
+
------
|
|
34
|
+
ValueError
|
|
35
|
+
If the shape of `array_in_1` and `array_in_2` do not match along all axes
|
|
36
|
+
except the first.
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
>>> array_in_1 = np.array([[1, 2], [3, 4]])
|
|
41
|
+
>>> array_in_2 = np.array([[1, 2], [0, 4]])
|
|
42
|
+
>>> equal_along_first_axis(array_in_1, array_in_2)
|
|
43
|
+
array([[ True, True],
|
|
44
|
+
[False, True]])
|
|
45
|
+
>>> equal_along_first_axis(array_in_1, array_in_2).dtype
|
|
46
|
+
dtype('bool')
|
|
47
|
+
"""
|
|
48
|
+
array_out = np.zeros_like(array_in_1)
|
|
49
|
+
for i, value in enumerate(array_in_2):
|
|
50
|
+
array_out[i, ...] = array_in_1[i, ...] == value
|
|
51
|
+
return array_out
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@njit()
|
|
55
|
+
def greater_along_first_axis(array_in_1, array_in_2):
|
|
56
|
+
"""
|
|
57
|
+
Compare two arrays element-wise along the first axis and return a boolean array,
|
|
58
|
+
where each element indicates whether the corresponding element in `array_in_1`
|
|
59
|
+
is greater than in `array_in_2`.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
array_in_1 : array_like
|
|
64
|
+
The first input array.
|
|
65
|
+
array_in_2 : array_like
|
|
66
|
+
The second input array.
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
out : ndarray, bool
|
|
71
|
+
A boolean array indicating where `array_in_1` elements are greater than
|
|
72
|
+
corresponding `array_in_2` elements.
|
|
73
|
+
|
|
74
|
+
Examples
|
|
75
|
+
--------
|
|
76
|
+
>>> array1 = np.array([[1, 2], [3, 4]])
|
|
77
|
+
>>> array2 = np.array([[0, 1], [2, 3]])
|
|
78
|
+
>>> result = greater_along_first_axis(array1, array2)
|
|
79
|
+
>>> print(result) # doctest: +NORMALIZE_WHITESPACE
|
|
80
|
+
[[ True False]
|
|
81
|
+
[ True False]]
|
|
82
|
+
"""
|
|
83
|
+
array_out = np.zeros_like(array_in_1)
|
|
84
|
+
for i, value in enumerate(array_in_2):
|
|
85
|
+
array_out[i, ...] = array_in_1[i, ...] > value
|
|
86
|
+
return array_out
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@njit()
|
|
90
|
+
def less_along_first_axis(array_in_1, array_in_2):
|
|
91
|
+
"""
|
|
92
|
+
Compare two arrays element-wise along the first axis, returning a boolean array.
|
|
93
|
+
|
|
94
|
+
This function performs an element-wise comparison between two arrays along
|
|
95
|
+
the first axis and returns a boolean array. The comparison is less than, i.e.,
|
|
96
|
+
element-wise `array_in_1 < array_in_2`.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
array_in_1 : numpy.ndarray
|
|
101
|
+
The first input array.
|
|
102
|
+
array_in_2 : numpy.ndarray
|
|
103
|
+
The second input array.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
numpy.ndarray[bool]
|
|
108
|
+
A boolean array where each element is the result of the comparison
|
|
109
|
+
`array_in_1[i, ...] < array_in_2[i, ...]` for all indices `i` along the first axis.
|
|
110
|
+
|
|
111
|
+
Notes
|
|
112
|
+
-----
|
|
113
|
+
This function uses Numba's `@njit` decorator for performance.
|
|
114
|
+
|
|
115
|
+
Examples
|
|
116
|
+
--------
|
|
117
|
+
>>> result = less_along_first_axis(np.array([[1, 2], [3, 4]]), np.array([2, 2]))
|
|
118
|
+
>>> print(result)
|
|
119
|
+
[[ True True]
|
|
120
|
+
[False False]]
|
|
121
|
+
|
|
122
|
+
>>> result = less_along_first_axis(np.array([[5, -1], [-3, 0]]), np.array([4.9, 2]))
|
|
123
|
+
>>> print(result)
|
|
124
|
+
[[False False]
|
|
125
|
+
[ True True]]
|
|
126
|
+
"""
|
|
127
|
+
array_out = np.zeros_like(array_in_1)
|
|
128
|
+
for i, value in enumerate(array_in_2):
|
|
129
|
+
array_out[i, ...] = array_in_1[i, ...] < value
|
|
130
|
+
return array_out
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def translate_dict(old_dict):
|
|
134
|
+
"""
|
|
135
|
+
Translate a dictionary to a typed dictionary and filter out non-string values.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
old_dict : dict
|
|
140
|
+
The input dictionary that may contain non-string values
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
dict
|
|
145
|
+
A typed dictionary containing only the items from `old_dict` where the value is not a string
|
|
146
|
+
|
|
147
|
+
Examples
|
|
148
|
+
--------
|
|
149
|
+
>>> result = translate_dict({'a': 1., 'b': 'string', 'c': 2.0})
|
|
150
|
+
>>> print(result)
|
|
151
|
+
DictType[unicode_type,float64]<iv=None>({a: 1.0, c: 2.0})
|
|
152
|
+
"""
|
|
153
|
+
numba_dict = Dict()
|
|
154
|
+
for k, v in old_dict.items():
|
|
155
|
+
if not isinstance(v, str):
|
|
156
|
+
numba_dict[k] = v
|
|
157
|
+
return numba_dict
|
|
158
|
+
|
|
159
|
+
def reduce_path_len(pathway, to_start, from_end):
|
|
160
|
+
"""
|
|
161
|
+
Reduce the length of a given pathway string by truncating it from both ends.
|
|
162
|
+
|
|
163
|
+
The function is used to shorten the `pathway` string if its length exceeds
|
|
164
|
+
a calculated maximum size. If it does, the function truncates it from both ends,
|
|
165
|
+
inserting an ellipsis ("...") in between.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
pathway : str or int
|
|
170
|
+
The pathway string to be reduced. If an integer is provided,
|
|
171
|
+
it will be converted into a string.
|
|
172
|
+
to_start : int
|
|
173
|
+
Number of characters from the start to keep in the pathway string.
|
|
174
|
+
from_end : int
|
|
175
|
+
Number of characters from the end to keep in the pathway string.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
str
|
|
180
|
+
The reduced version of the `pathway` string. If truncation is not necessary,
|
|
181
|
+
returns the original pathway string.
|
|
182
|
+
|
|
183
|
+
Examples
|
|
184
|
+
--------
|
|
185
|
+
>>> reduce_path_len("example/complicated/path/to/resource", 8, 12)
|
|
186
|
+
'example/.../to/resource'
|
|
187
|
+
"""
|
|
188
|
+
if not isinstance(pathway, str):
|
|
189
|
+
pathway = str(pathway)
|
|
190
|
+
max_size = to_start + from_end + 3
|
|
191
|
+
if len(pathway) > max_size:
|
|
192
|
+
pathway = pathway[:to_start] + "..." + pathway[-from_end:]
|
|
193
|
+
return pathway
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def find_nearest(array, value):
|
|
197
|
+
"""
|
|
198
|
+
Find the element in an array that is closest to a given value.
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
array : array_like
|
|
203
|
+
Input array. Can be any array-like data structure.
|
|
204
|
+
value :
|
|
205
|
+
The value to find the closest element to.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
:obj:`array` type
|
|
210
|
+
The element in `array` that is closest to `value`.
|
|
211
|
+
|
|
212
|
+
Examples
|
|
213
|
+
--------
|
|
214
|
+
>>> find_nearest([1, 2, 3, 4], 2.5)
|
|
215
|
+
2
|
|
216
|
+
"""
|
|
217
|
+
array = np.asarray(array)
|
|
218
|
+
idx = (np.abs(array - value)).argmin()
|
|
219
|
+
return array[idx]
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class PercentAndTimeTracker:
|
|
223
|
+
"""
|
|
224
|
+
Initialize a progress bar object to track and display the progress of an iteration.
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
total : int
|
|
229
|
+
The total number of iterations.
|
|
230
|
+
compute_with_elements_number : bool, optional
|
|
231
|
+
If True, create an element vector. Default is False.
|
|
232
|
+
core_number : int, optional
|
|
233
|
+
The number of cores to use. Default is 1.
|
|
234
|
+
|
|
235
|
+
Attributes
|
|
236
|
+
----------
|
|
237
|
+
starting_time : float
|
|
238
|
+
The time when the ProgressBar object is initialized.
|
|
239
|
+
total : int
|
|
240
|
+
The total number of iterations.
|
|
241
|
+
current_step : int
|
|
242
|
+
The current iteration step (initialized to 0).
|
|
243
|
+
element_vector : numpy.ndarray, optional
|
|
244
|
+
A vector of zeros with the same length as `total`, created if
|
|
245
|
+
`compute_with_elements_number` is True.
|
|
246
|
+
core_number : int
|
|
247
|
+
The number of cores.
|
|
248
|
+
|
|
249
|
+
Examples
|
|
250
|
+
--------
|
|
251
|
+
>>> p = PercentAndTimeTracker(10)
|
|
252
|
+
>>> print(p.total) # prints: 10
|
|
253
|
+
>>> p = PercentAndTimeTracker(10, compute_with_elements_number=True)
|
|
254
|
+
>>> print(p.element_vector) # prints: [0 0 0 0 0 0 0 0 0 0]
|
|
255
|
+
|
|
256
|
+
Notes
|
|
257
|
+
-----
|
|
258
|
+
Starting time is recorded for potential performance tracking.
|
|
259
|
+
|
|
260
|
+
"""
|
|
261
|
+
def __init__(self, total, compute_with_elements_number=False, core_number=1):
|
|
262
|
+
"""
|
|
263
|
+
Initialize a progress bar object to track and display the progress of an iteration.
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
total : int
|
|
268
|
+
The total number of iterations.
|
|
269
|
+
compute_with_elements_number : bool, optional
|
|
270
|
+
If True, create an element vector. Default is False.
|
|
271
|
+
core_number : int, optional
|
|
272
|
+
The number of cores to use. Default is 1.
|
|
273
|
+
|
|
274
|
+
Attributes
|
|
275
|
+
----------
|
|
276
|
+
starting_time : float
|
|
277
|
+
The time when the ProgressBar object is initialized.
|
|
278
|
+
total : int
|
|
279
|
+
The total number of iterations.
|
|
280
|
+
current_step : int
|
|
281
|
+
The current iteration step (initialized to 0).
|
|
282
|
+
element_vector : numpy.ndarray, optional
|
|
283
|
+
A vector of zeros with the same length as `total`, created if
|
|
284
|
+
`compute_with_elements_number` is True.
|
|
285
|
+
core_number : int
|
|
286
|
+
The number of cores.
|
|
287
|
+
|
|
288
|
+
Examples
|
|
289
|
+
--------
|
|
290
|
+
>>> p = PercentAndTimeTracker(10)
|
|
291
|
+
>>> print(p.total) # prints: 10
|
|
292
|
+
|
|
293
|
+
Notes
|
|
294
|
+
-----
|
|
295
|
+
Starting time is recorded for potential performance tracking.
|
|
296
|
+
|
|
297
|
+
"""
|
|
298
|
+
self.starting_time = default_timer()
|
|
299
|
+
self.total = total
|
|
300
|
+
self.current_step = 0
|
|
301
|
+
if compute_with_elements_number:
|
|
302
|
+
self.element_vector = np.zeros(total, dtype=np.int64)
|
|
303
|
+
self.core_number = core_number
|
|
304
|
+
|
|
305
|
+
def get_progress(self, step=None, element_number=None):
|
|
306
|
+
"""
|
|
307
|
+
Calculate and update the current progress, including elapsed time and estimated remaining time.
|
|
308
|
+
|
|
309
|
+
This function updates the internal state of the object to reflect progress
|
|
310
|
+
based on the current step and element number. It calculates elapsed time,
|
|
311
|
+
estimates total time, and computes the estimated time of arrival (ETA).
|
|
312
|
+
|
|
313
|
+
Parameters
|
|
314
|
+
----------
|
|
315
|
+
step : int or None, optional
|
|
316
|
+
The current step of the process. If ``None``, the internal counter is incremented.
|
|
317
|
+
element_number : int or None, optional
|
|
318
|
+
The current element number. If ``None``, no update is made to the element vector.
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
tuple
|
|
323
|
+
A tuple containing:
|
|
324
|
+
- `int`: The current progress percentage.
|
|
325
|
+
- `str`: A string with the ETA and remaining time.
|
|
326
|
+
|
|
327
|
+
Raises
|
|
328
|
+
------
|
|
329
|
+
ValueError
|
|
330
|
+
If ``step`` or ``element_number`` are invalid.
|
|
331
|
+
|
|
332
|
+
Notes
|
|
333
|
+
-----
|
|
334
|
+
The function uses linear regression to estimate future progress values when the current step is sufficiently large.
|
|
335
|
+
|
|
336
|
+
Examples
|
|
337
|
+
--------
|
|
338
|
+
>>> PercentAndTimeTracker(10, compute_with_elements_number=True).get_progress(9, 5)
|
|
339
|
+
(0, ', wait to get a more accurate ETA...')
|
|
340
|
+
"""
|
|
341
|
+
if step is not None:
|
|
342
|
+
self.current_step = step
|
|
343
|
+
if element_number is not None:
|
|
344
|
+
self.element_vector[self.current_step] = element_number
|
|
345
|
+
|
|
346
|
+
if self.current_step > 0:
|
|
347
|
+
elapsed_time = default_timer() - self.starting_time
|
|
348
|
+
if element_number is None or element_number == 0 or self.current_step < 15:
|
|
349
|
+
if self.current_step < self.core_number:
|
|
350
|
+
current_prop = self.core_number / self.total
|
|
351
|
+
else:
|
|
352
|
+
current_prop = (self.current_step + 1) / self.total
|
|
353
|
+
else:
|
|
354
|
+
x_mat = np.array([np.ones(self.current_step - 4), np.arange(5, self.current_step + 1)]).T
|
|
355
|
+
coefs = np.linalg.lstsq(x_mat, self.element_vector[5:self.current_step + 1], rcond=-1)[0]
|
|
356
|
+
self.element_vector = coefs[0] + (np.arange(self.total) * coefs[1])
|
|
357
|
+
self.element_vector[self.element_vector < 0] = 0
|
|
358
|
+
current_prop = self.element_vector[:self.current_step + 1].sum() / self.element_vector.sum()
|
|
359
|
+
|
|
360
|
+
total_time = elapsed_time / current_prop
|
|
361
|
+
current_prop = int(np.round(current_prop * 100))
|
|
362
|
+
remaining_time_s = total_time - elapsed_time
|
|
363
|
+
|
|
364
|
+
local_time = time.localtime()
|
|
365
|
+
local_m = int(time.strftime("%M", local_time))
|
|
366
|
+
local_h = int(time.strftime("%H", local_time))
|
|
367
|
+
remaining_time_h = remaining_time_s // 3600
|
|
368
|
+
reste_s = remaining_time_s % 3600
|
|
369
|
+
reste_m = reste_s // 60
|
|
370
|
+
# + str(int(np.floor(reste_s % 60))) + "S"
|
|
371
|
+
hours = int(np.floor(remaining_time_h))
|
|
372
|
+
minutes = int(np.floor(reste_m))
|
|
373
|
+
|
|
374
|
+
if (local_m + minutes) < 60:
|
|
375
|
+
eta_m = local_m + minutes
|
|
376
|
+
else:
|
|
377
|
+
eta_m = (local_m + minutes) % 60
|
|
378
|
+
local_h += 1
|
|
379
|
+
|
|
380
|
+
if (local_h + hours) < 24:
|
|
381
|
+
output = current_prop, f", ETA {local_h + hours}:{eta_m} ({hours}:{minutes} left)"
|
|
382
|
+
else:
|
|
383
|
+
days = (local_h + hours) // 24
|
|
384
|
+
eta_h = (local_h + hours) % 24
|
|
385
|
+
eta_d = time.strftime("%m", local_time) + "/" + str(int(time.strftime("%d", local_time)) + days)
|
|
386
|
+
output = current_prop, f", ETA {eta_d} {eta_h}:{eta_m} ({hours}:{minutes} left)"
|
|
387
|
+
# return current_prop, str(local_h + hours) + ":" + str(local_m + minutes) + "(" + str()
|
|
388
|
+
else:
|
|
389
|
+
output = int(np.round(100 / self.total)), ", wait..."
|
|
390
|
+
if step is None:
|
|
391
|
+
self.current_step += 1
|
|
392
|
+
if element_number is not None:
|
|
393
|
+
if self.current_step < 50:
|
|
394
|
+
output = int(0), ", wait to get a more accurate ETA..."
|
|
395
|
+
return output
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def insensitive_glob(pattern):
|
|
399
|
+
"""
|
|
400
|
+
Generates a glob pattern that matches both lowercase and uppercase letters.
|
|
401
|
+
|
|
402
|
+
Parameters
|
|
403
|
+
----------
|
|
404
|
+
pattern : str
|
|
405
|
+
The glob pattern to be made case-insensitive.
|
|
406
|
+
|
|
407
|
+
Returns
|
|
408
|
+
-------
|
|
409
|
+
str
|
|
410
|
+
A new glob pattern that will match both lowercase and uppercase letters.
|
|
411
|
+
|
|
412
|
+
Examples
|
|
413
|
+
--------
|
|
414
|
+
>>> insensitive_glob('*.TXT')
|
|
415
|
+
"""
|
|
416
|
+
def either(c):
|
|
417
|
+
return '[%s%s]' % (c.lower(), c.upper()) if c.isalpha() else c
|
|
418
|
+
return glob(''.join(map(either, pattern)))
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def smallest_memory_array(array_object, array_type='uint'):
|
|
422
|
+
"""
|
|
423
|
+
Convert the given array object to the smallest possible memory type.
|
|
424
|
+
|
|
425
|
+
This function determines the optimal data type for an array
|
|
426
|
+
based on its maximum value and converts it to that data type.
|
|
427
|
+
|
|
428
|
+
Parameters
|
|
429
|
+
----------
|
|
430
|
+
array_object : numpy.ndarray or list of lists
|
|
431
|
+
The input array object which can be either a NumPy array or a list of lists.
|
|
432
|
+
|
|
433
|
+
array_type : str, optional
|
|
434
|
+
The type of data to which the input array should be converted. Should be either 'uint' (default) or any other NumPy data type.
|
|
435
|
+
|
|
436
|
+
Returns
|
|
437
|
+
-------
|
|
438
|
+
numpy.ndarray
|
|
439
|
+
The converted array object with the smallest possible memory type.
|
|
440
|
+
|
|
441
|
+
Raises
|
|
442
|
+
------
|
|
443
|
+
TypeError
|
|
444
|
+
If the input `array_object` is not a NumPy array or list of lists.
|
|
445
|
+
|
|
446
|
+
ValueError
|
|
447
|
+
If the specified `array_type` is not supported by NumPy.
|
|
448
|
+
|
|
449
|
+
Notes
|
|
450
|
+
-----
|
|
451
|
+
This function uses NumPy's `iinfo` to determine the information about the integer data types and finds the smallest
|
|
452
|
+
data type that can store all values in the array without overflow.
|
|
453
|
+
|
|
454
|
+
Examples
|
|
455
|
+
--------
|
|
456
|
+
>>> arr = np.array([[1, 2], [3, 4]])
|
|
457
|
+
>>> result = smallest_memory_array(arr)
|
|
458
|
+
>>> print(result.dtype)
|
|
459
|
+
uint8
|
|
460
|
+
|
|
461
|
+
>>> import numpy as np
|
|
462
|
+
>>> arr = np.array([[1000, 2000], [3000, 4000]])
|
|
463
|
+
>>> result = smallest_memory_array(arr)
|
|
464
|
+
>>> print(result.dtype)
|
|
465
|
+
uint16
|
|
466
|
+
"""
|
|
467
|
+
if isinstance(array_object, np.ndarray):
|
|
468
|
+
value_max = array_object.max()
|
|
469
|
+
else:
|
|
470
|
+
if len(array_object[0]) > 0:
|
|
471
|
+
value_max = np.max((array_object[0].max(), array_object[1].max()))
|
|
472
|
+
else:
|
|
473
|
+
value_max = 0
|
|
474
|
+
|
|
475
|
+
if array_type == 'uint':
|
|
476
|
+
if value_max <= np.iinfo(np.uint8).max:
|
|
477
|
+
array_object = np.array(array_object, dtype=np.uint8)
|
|
478
|
+
elif value_max <= np.iinfo(np.uint16).max:
|
|
479
|
+
array_object = np.array(array_object, dtype=np.uint16)
|
|
480
|
+
elif value_max <= np.iinfo(np.uint32).max:
|
|
481
|
+
array_object = np.array(array_object, dtype=np.uint32)
|
|
482
|
+
else:
|
|
483
|
+
array_object = np.array(array_object, dtype=np.uint64)
|
|
484
|
+
return array_object
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def remove_coordinates(arr1, arr2):
|
|
488
|
+
"""
|
|
489
|
+
Remove coordinates from `arr1` that are present in `arr2`.
|
|
490
|
+
|
|
491
|
+
Given two arrays of coordinates, remove rows from the first array
|
|
492
|
+
that match any row in the second array.
|
|
493
|
+
|
|
494
|
+
Parameters
|
|
495
|
+
----------
|
|
496
|
+
arr1 : ndarray of shape (n, 2)
|
|
497
|
+
Array containing coordinates to filter.
|
|
498
|
+
arr2 : ndarray of shape (m, 2)
|
|
499
|
+
Array containing coordinates to match for removal.
|
|
500
|
+
|
|
501
|
+
Returns
|
|
502
|
+
-------
|
|
503
|
+
ndarray of shape (k, 2)
|
|
504
|
+
Array with coordinates from `arr1` that are not in `arr2`.
|
|
505
|
+
|
|
506
|
+
Examples
|
|
507
|
+
--------
|
|
508
|
+
>>> arr1 = np.array([[0, 0], [1, 2], [3, 4]])
|
|
509
|
+
>>> arr2 = np.array([[1, 2], [5, 6]])
|
|
510
|
+
>>> remove_coordinates(arr1, arr2)
|
|
511
|
+
array([[0, 0],
|
|
512
|
+
[3, 4]])
|
|
513
|
+
"""
|
|
514
|
+
# Convert to set of tuples
|
|
515
|
+
coords_to_remove = set(map(tuple, arr2))
|
|
516
|
+
return np.array([coord for coord in arr1 if tuple(coord) not in coords_to_remove])
|
|
Binary file
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: cellects
|
|
3
|
+
Version: 0.1.0.dev1
|
|
4
|
+
Summary: Cell Expansion Computer Tracking Software.
|
|
5
|
+
Author: Aurèle Boussard
|
|
6
|
+
Project-URL: Homepage, https://github.com/Aurele-B/Cellects
|
|
7
|
+
Project-URL: Issues, https://github.com/Aurele-B/Cellects/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: <3.13,>=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE.odt
|
|
14
|
+
Requires-Dist: coloredlogs
|
|
15
|
+
Requires-Dist: exif
|
|
16
|
+
Requires-Dist: ExifRead
|
|
17
|
+
Requires-Dist: numba
|
|
18
|
+
Requires-Dist: opencv-python
|
|
19
|
+
Requires-Dist: pandas
|
|
20
|
+
Requires-Dist: psutil
|
|
21
|
+
Requires-Dist: PySide6>=6.5
|
|
22
|
+
Requires-Dist: scipy
|
|
23
|
+
Requires-Dist: screeninfo
|
|
24
|
+
Requires-Dist: numpy>=1.26
|
|
25
|
+
Requires-Dist: scikit-image
|
|
26
|
+
Requires-Dist: tqdm
|
|
27
|
+
Requires-Dist: h5py
|
|
28
|
+
Requires-Dist: matplotlib
|
|
29
|
+
Requires-Dist: natsort
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest; extra == "test"
|
|
32
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
33
|
+
Provides-Extra: doc
|
|
34
|
+
Requires-Dist: mkdocs; extra == "doc"
|
|
35
|
+
Requires-Dist: mkdocs-material; extra == "doc"
|
|
36
|
+
Requires-Dist: pymdown-extensions; extra == "doc"
|
|
37
|
+
|
|
38
|
+
Cellects: Cell Expansion Computer Tracking Software
|
|
39
|
+
===================================================
|
|
40
|
+
|
|
41
|
+
Description
|
|
42
|
+
-----------
|
|
43
|
+
|
|
44
|
+
Cellects is a tracking software for organisms whose shape and size change over time.
|
|
45
|
+
Cellects’ main strengths are its broad scope of action,
|
|
46
|
+
automated computation of a variety of geometrical descriptors, easy installation and user-friendly interface.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
⚠️ **Note:** At this stage, Cellects is available **only from source**.
|
|
55
|
+
You will need **Miniconda3** and **git** installed on your system.
|
|
56
|
+
|
|
57
|
+
- Install [Miniconda3](https://docs.conda.io/en/latest/miniconda.html)
|
|
58
|
+
(choose the installer for your operating system).
|
|
59
|
+
- Install [git](https://git-scm.com/downloads)
|
|
60
|
+
(also available through package managers like `apt`, `brew`, or `choco`).
|
|
61
|
+
|
|
62
|
+
Once these prerequisites are installed, you can set up Cellects as follows:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Clone the repository
|
|
66
|
+
git clone https://github.com/Aurele-B/Cellects.git
|
|
67
|
+
cd Cellects
|
|
68
|
+
|
|
69
|
+
# Create and activate the environment
|
|
70
|
+
conda env create -f conda/env.yml
|
|
71
|
+
conda activate cellects-dev
|
|
72
|
+
|
|
73
|
+
# Install the package in editable mode
|
|
74
|
+
pip install -e .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Launch the application:
|
|
78
|
+
```bash
|
|
79
|
+
Cellects
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Developer Guide
|
|
85
|
+
|
|
86
|
+
### Run Tests
|
|
87
|
+
Cellects uses `pytest` + `pytest-cov`.
|
|
88
|
+
Install test dependencies:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e ".[test]"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Run the test suite (with coverage enabled by default via `pyproject.toml`):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pytest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
You can access the coverage report with `coverage html` and open `htmlcov/index.html` in your browser.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
open htmlcov/index.html # macOS
|
|
104
|
+
xdg-open htmlcov/index.html # Linux
|
|
105
|
+
start htmlcov\index.html # Windows (PowerShell)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Or explicitly:
|
|
109
|
+
```bash
|
|
110
|
+
pytest --cov=src/cellects --cov-report=term-missing
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Build Documentation
|
|
114
|
+
Install doc dependencies:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pip install -e ".[doc]"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Serve the docs locally:
|
|
121
|
+
```bash
|
|
122
|
+
mkdocs serve
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Open [http://127.0.0.1:8000](http://127.0.0.1:8000) in your browser.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Resources
|
|
130
|
+
- [User manual](https://github.com/Aurele-B/Cellects/blob/main/_old_doc/UserManual.md)
|
|
131
|
+
- [Usage example (video)](https://www.youtube.com/watch?v=N-k4p_aSPC0)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
cellects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
cellects/__main__.py,sha256=qiUkIOuyq3RuyHmYZM3drRbqe5DWP7IVyOGsEy425Lc,1449
|
|
3
|
+
cellects/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
cellects/config/all_vars_dict.py,sha256=JWS4Z9ppIXG7ugQPns7Et4Vb6r6n6DPcSYTr7uUYuwE,5980
|
|
5
|
+
cellects/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
cellects/core/cellects_paths.py,sha256=IV3O3p61OYa2Rk0FLUG-R8PsGKvLcsOuK2kzm9oVjbc,830
|
|
7
|
+
cellects/core/cellects_threads.py,sha256=bGyhasTIV8Uad9Z8AoyXYY9YzDl5HqLPp2kzVeDRKPc,88820
|
|
8
|
+
cellects/core/motion_analysis.py,sha256=AbPHVksE96mFXOA2ifDVTxL6S8cZAulnxXBvePcp21k,136709
|
|
9
|
+
cellects/core/one_image_analysis.py,sha256=SeT1Ps6XAyjALFfgl8hLQ28Q0hkWwMu_DKmVY84ytkc,68577
|
|
10
|
+
cellects/core/one_video_per_blob.py,sha256=evhWm56amxSBfZfKfmvBJK1wSWWIS19HPyXpxs8FsF0,42981
|
|
11
|
+
cellects/core/program_organizer.py,sha256=CcT0px29HNRx2VgwQOAoEbHqZPGuFyxmAPlieCAgb2E,79209
|
|
12
|
+
cellects/core/script_based_run.py,sha256=Y9FuJ2I5I3_T6vwAQ6w22vdGbkxmjLVAdDWPyoziVGU,6565
|
|
13
|
+
cellects/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
cellects/gui/advanced_parameters.py,sha256=pE2eGZ6tBuq40DalksDzHGGQSjBJcgVr1C2MOAVyGaM,78343
|
|
15
|
+
cellects/gui/cellects.py,sha256=qgHLvrAVR1YatSsuOBl5u7aXCUqVHEbhse7EFkTINOA,7574
|
|
16
|
+
cellects/gui/custom_widgets.py,sha256=2WcPsRgPCp4HEjA9x8wE8IX7RJ0A4XyBpN5rv-iqVL8,35503
|
|
17
|
+
cellects/gui/first_window.py,sha256=wYQaA-VQFWp_YDjbCXl0e_PgD59-y8kUXRdD2tDelbQ,24803
|
|
18
|
+
cellects/gui/if_several_folders_window.py,sha256=ixi6X4WDQ5CtunEPVFJWip8Sf5ba1Hk2t801lX5CNVk,12508
|
|
19
|
+
cellects/gui/image_analysis_window.py,sha256=A3N16SCbWXAvN-irHzpf-kWJ7C-CFirjb98kAH8pIYs,106073
|
|
20
|
+
cellects/gui/required_output.py,sha256=YJBSi_Vp5zet2pZVh71viGrlzE8EeMpX41opt3sD8QM,11992
|
|
21
|
+
cellects/gui/video_analysis_window.py,sha256=xl2qtJqUp47GvPh7f4tQoaiK8nUO9Q2Rk6qGSiFBLp8,38033
|
|
22
|
+
cellects/icons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
cellects/icons/cellects_icon.icns,sha256=3GM5rpVKUoy-gAYEee5gGPh9lQx9KZqh9iRqYCT83Aw,393392
|
|
24
|
+
cellects/icons/cellects_icon.ico,sha256=Eqlci8zZ0zdsRh2kSQAu4aHAPbR2NEzSbJPgaRQNenI,208076
|
|
25
|
+
cellects/image_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
cellects/image_analysis/cell_leaving_detection.py,sha256=f0wWTACMQASSdXpDtJGe7STJTj6xs1UO1JJzI2FgDBk,2951
|
|
27
|
+
cellects/image_analysis/cluster_flux_study.py,sha256=lHKo2OFN_FvmyX9kP7ubRadTVVxuXDeuRWQuydvyAGQ,5496
|
|
28
|
+
cellects/image_analysis/extract_exif.py,sha256=BcZnNk528hmxdaM8wLzcRZlVz0_VhLySdqCbPzWNyyI,3447
|
|
29
|
+
cellects/image_analysis/fractal_analysis.py,sha256=xC9CZVFBD43bXJFEATeI2Vsi82mzoZbKC_XFA8FD-CI,6785
|
|
30
|
+
cellects/image_analysis/fractal_functions.py,sha256=1_6HWHDhri9w4sfPPBIx45Dz2bBs8491xFVtSa3Pkzw,4393
|
|
31
|
+
cellects/image_analysis/image_segmentation.py,sha256=y_cFpdTyi1fDOmlVvtJphkIgZi3a9Txda3T0SnaTZmE,12021
|
|
32
|
+
cellects/image_analysis/morphological_operations.py,sha256=-jt7vwrj2r-mvBCoDuDIyZmjy0qY0A7MprpZtb8sbvo,42908
|
|
33
|
+
cellects/image_analysis/network_functions.py,sha256=5IRF2qH4Lqh3u4mQfdCpXBmN9Q6zwVu1siOWsgBkkn8,65233
|
|
34
|
+
cellects/image_analysis/one_image_analysis_threads.py,sha256=AjAEbJmtldlf23UxWs5my7xz2NIYy3wm25o13pnnYq8,17253
|
|
35
|
+
cellects/image_analysis/progressively_add_distant_shapes.py,sha256=fYj1pfG13-0aWagfLzki20zQJbP706YDXxIK8P572EI,13723
|
|
36
|
+
cellects/image_analysis/shape_descriptors.py,sha256=jOQLWWLbk4KLd00MpZIL91ROxoV6h2cviLptkh6nw_8,35138
|
|
37
|
+
cellects/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
cellects/utils/formulas.py,sha256=JUY7iCLmUUbCiJ6rTqosSmnHu1FyreQ5ktrvfN1U5-g,25744
|
|
39
|
+
cellects/utils/load_display_save.py,sha256=igaNsl3XmVTlaRL5Glq3E8XYQEuB83ePlSynjO0hrlA,34421
|
|
40
|
+
cellects/utils/utilitarian.py,sha256=VKTpAGd2IEa_AJfVfIIS_ysNn_eROncmtQjYB7g7Eic,17318
|
|
41
|
+
cellects-0.1.0.dev1.dist-info/LICENSE.odt,sha256=tvKfCylOEuclQm5zOnNWBx0Fp4n7M5yqSmTr6SAekbY,37132
|
|
42
|
+
cellects-0.1.0.dev1.dist-info/METADATA,sha256=-x_CDE0x7EnchG3hx_iwG4Sr2jgZQ4f0M9O7QvysNGE,3316
|
|
43
|
+
cellects-0.1.0.dev1.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
|
44
|
+
cellects-0.1.0.dev1.dist-info/entry_points.txt,sha256=JT6rEvKpUuKyDPvfOYma-IMQNvfnKMstFMAoVJhXIGc,60
|
|
45
|
+
cellects-0.1.0.dev1.dist-info/top_level.txt,sha256=8VlvCH4ka3bqugIpQnOVjc3UV9Vavfx5SXNyUV9_lGw,9
|
|
46
|
+
cellects-0.1.0.dev1.dist-info/RECORD,,
|