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,490 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Utility module with array operations, path manipulation, and progress tracking.
|
|
4
|
+
|
|
5
|
+
This module provides performance-optimized utilities for numerical comparisons using Numba,
|
|
6
|
+
path string truncation, dictionary filtering, and iteration progress monitoring. It is designed
|
|
7
|
+
for applications requiring efficient data processing pipelines with both low-level optimization
|
|
8
|
+
and human-readable output formatting.
|
|
9
|
+
|
|
10
|
+
Classes
|
|
11
|
+
-------
|
|
12
|
+
PercentAndTimeTracker : Track iteration progress with time estimates and percentage completion
|
|
13
|
+
|
|
14
|
+
Functions
|
|
15
|
+
---------
|
|
16
|
+
greater_along_first_axis : Compare arrays element-wise along first axis (>) using Numba
|
|
17
|
+
less_along_first_axis : Compare arrays element-wise along first axis (<) using Numba
|
|
18
|
+
translate_dict : Convert standard dict to typed dict, filtering non-string values
|
|
19
|
+
reduce_path_len : Truncate long path strings with ellipsis insertion
|
|
20
|
+
find_nearest : Find array element closest to target value
|
|
21
|
+
|
|
22
|
+
Notes
|
|
23
|
+
-----
|
|
24
|
+
Numba-optimized functions (greater_along_first_axis and less_along_first_axis) require
|
|
25
|
+
input arrays of identical shape. String manipulation utilities include automatic type conversion.
|
|
26
|
+
The progress tracker records initialization time for potential performance analysis.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import numpy as np
|
|
30
|
+
from numpy.typing import NDArray
|
|
31
|
+
from typing import Tuple
|
|
32
|
+
from timeit import default_timer
|
|
33
|
+
import time
|
|
34
|
+
from numba.typed import Dict
|
|
35
|
+
from cellects.utils.decorators import njit
|
|
36
|
+
from glob import glob
|
|
37
|
+
vectorized_len = np.vectorize(len)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@njit()
|
|
41
|
+
def greater_along_first_axis(array_in_1: NDArray, array_in_2: NDArray) -> NDArray[np.uint8]:
|
|
42
|
+
"""
|
|
43
|
+
Compare two arrays along the first axis and store the result in a third array.
|
|
44
|
+
|
|
45
|
+
This function performs a comparison between two input arrays
|
|
46
|
+
along their first axis and stores the result in a third array. The comparison is
|
|
47
|
+
made to determine which elements of each row of the first array are greater than
|
|
48
|
+
the elements(s) corresponding to that row in the second array.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
array_in_1 : ndarray
|
|
53
|
+
First input array.
|
|
54
|
+
array_in_2 : ndarray
|
|
55
|
+
Second input array.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
out : ndarray of uint8
|
|
60
|
+
Boolean ndarray with same shape as input arrays,
|
|
61
|
+
containing the result of element-wise comparison.
|
|
62
|
+
|
|
63
|
+
Examples
|
|
64
|
+
--------
|
|
65
|
+
>>> array_in_1 = np.array([[2, 4], [5, 8]])
|
|
66
|
+
>>> array_in_2 = np.array([3, 6])
|
|
67
|
+
>>> array_out = greater_along_first_axis(array_in_1, array_in_2)
|
|
68
|
+
>>> print(array_out)
|
|
69
|
+
[[0 1]
|
|
70
|
+
[0 1]]
|
|
71
|
+
"""
|
|
72
|
+
array_out = np.zeros(array_in_1.shape, dtype=np.uint8)
|
|
73
|
+
for i, value in enumerate(array_in_2):
|
|
74
|
+
array_out[i, ...] = array_in_1[i, ...] > value
|
|
75
|
+
return array_out
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@njit()
|
|
79
|
+
def less_along_first_axis(array_in_1: NDArray, array_in_2: NDArray) -> NDArray[np.uint8]:
|
|
80
|
+
"""
|
|
81
|
+
Compare two arrays along the first axis and store the result in a third array.
|
|
82
|
+
|
|
83
|
+
This function performs a comparison between two input arrays
|
|
84
|
+
along their first axis and stores the result in a third array. The comparison is
|
|
85
|
+
made to determine which elements of each row of the first array are lesser than
|
|
86
|
+
the elements(s) corresponding to that row in the second array.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
array_in_1 : ndarray
|
|
91
|
+
The first input array.
|
|
92
|
+
array_in_2 : ndarray
|
|
93
|
+
The second input array.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
ndarray of uint8
|
|
98
|
+
A boolean array where each element is `True` if the corresponding
|
|
99
|
+
element in `array_in_1` is lesser than the corresponding element
|
|
100
|
+
in `array_in_2`, and `False` otherwise.
|
|
101
|
+
|
|
102
|
+
Examples
|
|
103
|
+
--------
|
|
104
|
+
>>> array_in_1 = np.array([[2, 4], [5, 8]])
|
|
105
|
+
>>> array_in_2 = np.array([3, 6])
|
|
106
|
+
>>> array_out = less_along_first_axis(array_in_1, array_in_2)
|
|
107
|
+
>>> print(array_out)
|
|
108
|
+
[[1 0]
|
|
109
|
+
[1 0]]
|
|
110
|
+
"""
|
|
111
|
+
array_out = np.zeros(array_in_1.shape, dtype=np.uint8)
|
|
112
|
+
for i, value in enumerate(array_in_2):
|
|
113
|
+
array_out[i, ...] = array_in_1[i, ...] < value
|
|
114
|
+
return array_out
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def translate_dict(old_dict: dict) -> Dict:
|
|
118
|
+
"""
|
|
119
|
+
Translate a dictionary to a typed dictionary and filter out non-string values.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
old_dict : dict
|
|
124
|
+
The input dictionary that may contain non-string values
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
numba_dict : Dict
|
|
129
|
+
A typed dictionary containing only the items from `old_dict` where the value is not a string
|
|
130
|
+
|
|
131
|
+
Examples
|
|
132
|
+
--------
|
|
133
|
+
>>> result = translate_dict({'a': 1., 'b': 'string', 'c': 2.0})
|
|
134
|
+
>>> print(result)
|
|
135
|
+
{a: 1.0, c: 2.0}
|
|
136
|
+
"""
|
|
137
|
+
numba_dict = Dict()
|
|
138
|
+
for k, v in old_dict.items():
|
|
139
|
+
if not isinstance(v, str):
|
|
140
|
+
numba_dict[k] = v
|
|
141
|
+
return numba_dict
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def reduce_path_len(pathway: str, to_start: int, from_end: int) -> str:
|
|
145
|
+
"""
|
|
146
|
+
Reduce the length of a given pathway string by truncating it from both ends.
|
|
147
|
+
|
|
148
|
+
The function is used to shorten the `pathway` string if its length exceeds
|
|
149
|
+
a calculated maximum size. If it does, the function truncates it from both ends,
|
|
150
|
+
inserting an ellipsis ("...") in between.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
pathway : str
|
|
155
|
+
The pathway string to be reduced. If an integer is provided,
|
|
156
|
+
it will be converted into a string.
|
|
157
|
+
to_start : int
|
|
158
|
+
Number of characters from the start to keep in the pathway string.
|
|
159
|
+
from_end : int
|
|
160
|
+
Number of characters from the end to keep in the pathway string.
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
str
|
|
165
|
+
The reduced version of the `pathway` string. If truncation is not necessary,
|
|
166
|
+
returns the original pathway string.
|
|
167
|
+
|
|
168
|
+
Examples
|
|
169
|
+
--------
|
|
170
|
+
>>> reduce_path_len("example/complicated/path/to/resource", 8, 12)
|
|
171
|
+
'example/.../to/resource'
|
|
172
|
+
"""
|
|
173
|
+
if not isinstance(pathway, str):
|
|
174
|
+
pathway = str(pathway)
|
|
175
|
+
max_size = to_start + from_end + 3
|
|
176
|
+
if len(pathway) > max_size:
|
|
177
|
+
pathway = pathway[:to_start] + "..." + pathway[-from_end:]
|
|
178
|
+
return pathway
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def find_nearest(array: NDArray, value):
|
|
182
|
+
"""
|
|
183
|
+
Find the element in an array that is closest to a given value.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
array : array_like
|
|
188
|
+
Input array. Can be any array-like data structure.
|
|
189
|
+
value : int or float
|
|
190
|
+
The value to find the closest element to.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
:obj:`array` type
|
|
195
|
+
The element in `array` that is closest to `value`.
|
|
196
|
+
|
|
197
|
+
Examples
|
|
198
|
+
--------
|
|
199
|
+
>>> find_nearest([1, 2, 3, 4], 2.5)
|
|
200
|
+
2
|
|
201
|
+
"""
|
|
202
|
+
array = np.asarray(array)
|
|
203
|
+
idx = (np.abs(array - value)).argmin()
|
|
204
|
+
return array[idx]
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class PercentAndTimeTracker:
|
|
208
|
+
"""
|
|
209
|
+
Initialize a progress bar object to track and display the progress of an iteration.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
total : int
|
|
214
|
+
The total number of iterations.
|
|
215
|
+
compute_with_elements_number : bool, optional
|
|
216
|
+
If True, create an element vector. Default is False.
|
|
217
|
+
core_number : int, optional
|
|
218
|
+
The number of cores to use. Default is 1.
|
|
219
|
+
|
|
220
|
+
Attributes
|
|
221
|
+
----------
|
|
222
|
+
starting_time : float
|
|
223
|
+
The time when the ProgressBar object is initialized.
|
|
224
|
+
total : int
|
|
225
|
+
The total number of iterations.
|
|
226
|
+
current_step : int
|
|
227
|
+
The current iteration step (initialized to 0).
|
|
228
|
+
element_vector : numpy.ndarray, optional
|
|
229
|
+
A vector of zeros with the same length as `total`, created if
|
|
230
|
+
`compute_with_elements_number` is True.
|
|
231
|
+
core_number : int
|
|
232
|
+
The number of cores.
|
|
233
|
+
|
|
234
|
+
Examples
|
|
235
|
+
--------
|
|
236
|
+
>>> p = PercentAndTimeTracker(10)
|
|
237
|
+
>>> print(p.total) # prints: 10
|
|
238
|
+
>>> p = PercentAndTimeTracker(10, compute_with_elements_number=True)
|
|
239
|
+
>>> print(p.element_vector) # prints: [0 0 0 0 0 0 0 0 0 0]
|
|
240
|
+
|
|
241
|
+
Notes
|
|
242
|
+
-----
|
|
243
|
+
Starting time is recorded for potential performance tracking.
|
|
244
|
+
|
|
245
|
+
"""
|
|
246
|
+
def __init__(self, total: int, compute_with_elements_number: bool=False, core_number:int =1):
|
|
247
|
+
"""Initialize an instance of the class.
|
|
248
|
+
|
|
249
|
+
This constructor sets up the initial attributes including
|
|
250
|
+
a starting time, total value, current step, and an optional
|
|
251
|
+
element vector if ``compute_with_elements_number`` is set to True.
|
|
252
|
+
The core number can be specified, defaulting to 1.
|
|
253
|
+
|
|
254
|
+
Parameters
|
|
255
|
+
----------
|
|
256
|
+
total : int
|
|
257
|
+
The total number of elements or steps.
|
|
258
|
+
compute_with_elements_number : bool, optional
|
|
259
|
+
If True, initialize an element vector of zeros. Defaults to False.
|
|
260
|
+
core_number : int, optional
|
|
261
|
+
The number of cores to use. Defaults to 1.
|
|
262
|
+
|
|
263
|
+
Attributes
|
|
264
|
+
----------
|
|
265
|
+
starting_time : float
|
|
266
|
+
The time of instantiation.
|
|
267
|
+
total : int
|
|
268
|
+
The total number of elements or steps.
|
|
269
|
+
current_step : int
|
|
270
|
+
The current step in the process.
|
|
271
|
+
element_vector : ndarray of int64, optional
|
|
272
|
+
A vector initialized with zeros. Exists if ``compute_with_elements_number`` is True.
|
|
273
|
+
core_number : int
|
|
274
|
+
The number of cores to use.
|
|
275
|
+
"""
|
|
276
|
+
self.starting_time = default_timer()
|
|
277
|
+
self.total = total
|
|
278
|
+
self.current_step = 0
|
|
279
|
+
if compute_with_elements_number:
|
|
280
|
+
self.element_vector = np.zeros(total, dtype=np.int64)
|
|
281
|
+
self.core_number = core_number
|
|
282
|
+
|
|
283
|
+
def get_progress(self, step=None, element_number=None):
|
|
284
|
+
"""
|
|
285
|
+
Calculate and update the current progress, including elapsed time and estimated remaining time.
|
|
286
|
+
|
|
287
|
+
This function updates the internal state of the object to reflect progress
|
|
288
|
+
based on the current step and element number. It calculates elapsed time,
|
|
289
|
+
estimates total time, and computes the estimated time of arrival (ETA).
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
step : int or None, optional
|
|
294
|
+
The current step of the process. If ``None``, the internal counter is incremented.
|
|
295
|
+
element_number : int or None, optional
|
|
296
|
+
The current element number. If ``None``, no update is made to the element vector.
|
|
297
|
+
|
|
298
|
+
Returns
|
|
299
|
+
-------
|
|
300
|
+
tuple
|
|
301
|
+
A tuple containing:
|
|
302
|
+
- `int`: The current progress percentage.
|
|
303
|
+
- `str`: A string with the ETA and remaining time.
|
|
304
|
+
|
|
305
|
+
Raises
|
|
306
|
+
------
|
|
307
|
+
ValueError
|
|
308
|
+
If ``step`` or ``element_number`` are invalid.
|
|
309
|
+
|
|
310
|
+
Notes
|
|
311
|
+
-----
|
|
312
|
+
The function uses linear regression to estimate future progress values when the current step is sufficiently large.
|
|
313
|
+
|
|
314
|
+
Examples
|
|
315
|
+
--------
|
|
316
|
+
>>> PercentAndTimeTracker(10, compute_with_elements_number=True).get_progress(9, 5)
|
|
317
|
+
(0, ', wait to get a more accurate ETA...')
|
|
318
|
+
"""
|
|
319
|
+
if step is not None:
|
|
320
|
+
self.current_step = step
|
|
321
|
+
if element_number is not None:
|
|
322
|
+
self.element_vector[self.current_step] = element_number
|
|
323
|
+
|
|
324
|
+
if self.current_step > 0:
|
|
325
|
+
elapsed_time = default_timer() - self.starting_time
|
|
326
|
+
if element_number is None or element_number == 0 or self.current_step < 15:
|
|
327
|
+
if self.current_step < self.core_number:
|
|
328
|
+
current_prop = self.core_number / self.total
|
|
329
|
+
else:
|
|
330
|
+
current_prop = (self.current_step + 1) / self.total
|
|
331
|
+
else:
|
|
332
|
+
x_mat = np.array([np.ones(self.current_step - 4), np.arange(5, self.current_step + 1)]).T
|
|
333
|
+
coefs = np.linalg.lstsq(x_mat, self.element_vector[5:self.current_step + 1], rcond=-1)[0]
|
|
334
|
+
self.element_vector = coefs[0] + (np.arange(self.total) * coefs[1])
|
|
335
|
+
self.element_vector[self.element_vector < 0] = 0
|
|
336
|
+
current_prop = self.element_vector[:self.current_step + 1].sum() / self.element_vector.sum()
|
|
337
|
+
|
|
338
|
+
total_time = elapsed_time / current_prop
|
|
339
|
+
current_prop = int(np.round(current_prop * 100))
|
|
340
|
+
remaining_time_s = total_time - elapsed_time
|
|
341
|
+
|
|
342
|
+
local_time = time.localtime()
|
|
343
|
+
local_m = int(time.strftime("%M", local_time))
|
|
344
|
+
local_h = int(time.strftime("%H", local_time))
|
|
345
|
+
remaining_time_h = remaining_time_s // 3600
|
|
346
|
+
reste_s = remaining_time_s % 3600
|
|
347
|
+
reste_m = reste_s // 60
|
|
348
|
+
# + str(int(np.floor(reste_s % 60))) + "S"
|
|
349
|
+
hours = int(np.floor(remaining_time_h))
|
|
350
|
+
minutes = int(np.floor(reste_m))
|
|
351
|
+
|
|
352
|
+
if (local_m + minutes) < 60:
|
|
353
|
+
eta_m = local_m + minutes
|
|
354
|
+
else:
|
|
355
|
+
eta_m = (local_m + minutes) % 60
|
|
356
|
+
local_h += 1
|
|
357
|
+
|
|
358
|
+
if (local_h + hours) < 24:
|
|
359
|
+
output = current_prop, f", ETA {local_h + hours}:{eta_m} ({hours}:{minutes} left)"
|
|
360
|
+
else:
|
|
361
|
+
days = (local_h + hours) // 24
|
|
362
|
+
eta_h = (local_h + hours) % 24
|
|
363
|
+
eta_d = time.strftime("%m", local_time) + "/" + str(int(time.strftime("%d", local_time)) + days)
|
|
364
|
+
output = current_prop, f", ETA {eta_d} {eta_h}:{eta_m} ({hours}:{minutes} left)"
|
|
365
|
+
# return current_prop, str(local_h + hours) + ":" + str(local_m + minutes) + "(" + str()
|
|
366
|
+
else:
|
|
367
|
+
output = int(np.round(100 / self.total)), ", wait..."
|
|
368
|
+
if step is None:
|
|
369
|
+
self.current_step += 1
|
|
370
|
+
if element_number is not None:
|
|
371
|
+
if self.current_step < 50:
|
|
372
|
+
output = int(0), ", wait to get a more accurate ETA..."
|
|
373
|
+
return output
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def insensitive_glob(pattern: str):
|
|
377
|
+
"""
|
|
378
|
+
Generates a glob pattern that matches both lowercase and uppercase letters.
|
|
379
|
+
|
|
380
|
+
Parameters
|
|
381
|
+
----------
|
|
382
|
+
pattern : str
|
|
383
|
+
The glob pattern to be made case-insensitive.
|
|
384
|
+
|
|
385
|
+
Returns
|
|
386
|
+
-------
|
|
387
|
+
str
|
|
388
|
+
A new glob pattern that will match both lowercase and uppercase letters.
|
|
389
|
+
|
|
390
|
+
Examples
|
|
391
|
+
--------
|
|
392
|
+
>>> insensitive_glob('*.TXT')
|
|
393
|
+
"""
|
|
394
|
+
def either(c):
|
|
395
|
+
return '[%s%s]' % (c.lower(), c.upper()) if c.isalpha() else c
|
|
396
|
+
return glob(''.join(map(either, pattern)))
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def smallest_memory_array(array_object, array_type='uint') -> NDArray:
|
|
400
|
+
"""
|
|
401
|
+
Convert input data to the smallest possible NumPy array type that can hold it.
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
array_object : ndarray or list of lists
|
|
406
|
+
The input data to be converted.
|
|
407
|
+
array_type : str, optional, default is 'uint'
|
|
408
|
+
The type of NumPy data type to use ('uint').
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
ndarray
|
|
413
|
+
A NumPy array of the smallest data type that can hold all values in `array_object`.
|
|
414
|
+
|
|
415
|
+
Examples
|
|
416
|
+
--------
|
|
417
|
+
>>> import numpy as np
|
|
418
|
+
>>> array = [[1, 2], [3, 4]]
|
|
419
|
+
>>> smallest_memory_array(array)
|
|
420
|
+
array([[1, 2],
|
|
421
|
+
[3, 4]], dtype=np.uint8)
|
|
422
|
+
|
|
423
|
+
>>> array = [[1000, 2000], [3000, 4000]]
|
|
424
|
+
>>> smallest_memory_array(array)
|
|
425
|
+
array([[1000, 2000],
|
|
426
|
+
[3000, 4000]], dtype=uint16)
|
|
427
|
+
|
|
428
|
+
>>> array = [[2**31, 2**32], [2**33, 2**34]]
|
|
429
|
+
>>> smallest_memory_array(array)
|
|
430
|
+
array([[ 2147483648, 4294967296],
|
|
431
|
+
[ 8589934592, 17179869184]], dtype=uint64)
|
|
432
|
+
"""
|
|
433
|
+
if isinstance(array_object, list):
|
|
434
|
+
array_object = np.array(array_object)
|
|
435
|
+
if isinstance(array_object, np.ndarray):
|
|
436
|
+
value_max = array_object.max()
|
|
437
|
+
else:
|
|
438
|
+
|
|
439
|
+
if len(array_object[0]) > 0:
|
|
440
|
+
value_max = np.max((array_object[0].max(), array_object[1].max()))
|
|
441
|
+
else:
|
|
442
|
+
value_max = 0
|
|
443
|
+
|
|
444
|
+
if array_type == 'uint':
|
|
445
|
+
if value_max <= np.iinfo(np.uint8).max:
|
|
446
|
+
array_object = np.array(array_object, dtype=np.uint8)
|
|
447
|
+
elif value_max <= np.iinfo(np.uint16).max:
|
|
448
|
+
array_object = np.array(array_object, dtype=np.uint16)
|
|
449
|
+
elif value_max <= np.iinfo(np.uint32).max:
|
|
450
|
+
array_object = np.array(array_object, dtype=np.uint32)
|
|
451
|
+
else:
|
|
452
|
+
array_object = np.array(array_object, dtype=np.uint64)
|
|
453
|
+
return array_object
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def remove_coordinates(arr1: NDArray, arr2: NDArray) -> NDArray:
|
|
457
|
+
"""
|
|
458
|
+
Remove coordinates from `arr1` that are present in `arr2`.
|
|
459
|
+
|
|
460
|
+
Given two arrays of coordinates, remove rows from the first array
|
|
461
|
+
that match any row in the second array.
|
|
462
|
+
|
|
463
|
+
Parameters
|
|
464
|
+
----------
|
|
465
|
+
arr1 : ndarray of shape (n, 2)
|
|
466
|
+
Array containing coordinates to filter.
|
|
467
|
+
arr2 : ndarray of shape (m, 2)
|
|
468
|
+
Array containing coordinates to match for removal.
|
|
469
|
+
|
|
470
|
+
Returns
|
|
471
|
+
-------
|
|
472
|
+
ndarray of shape (k, 2)
|
|
473
|
+
Array with coordinates from `arr1` that are not in `arr2`.
|
|
474
|
+
|
|
475
|
+
Examples
|
|
476
|
+
--------
|
|
477
|
+
>>> arr1 = np.arange(200).reshape(100, 2)
|
|
478
|
+
>>> arr2 = np.array([[196, 197], [198, 199]])
|
|
479
|
+
>>> remove_coordinates(arr1, arr2)
|
|
480
|
+
array([[0, 0],
|
|
481
|
+
[3, 4]])
|
|
482
|
+
"""
|
|
483
|
+
if arr1.shape[1] != 2 or arr2.shape[1] != 2:
|
|
484
|
+
raise ValueError("Both arrays must have shape (n, 2)")
|
|
485
|
+
mask = ~np.isin(arr1, arr2).all(axis=1)
|
|
486
|
+
return arr1[mask]
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
Binary file
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: cellects
|
|
3
|
+
Version: 0.1.2
|
|
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-env; extra == "test"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
34
|
+
Provides-Extra: doc
|
|
35
|
+
Requires-Dist: mkdocs; extra == "doc"
|
|
36
|
+
Requires-Dist: mkdocs-material; extra == "doc"
|
|
37
|
+
Requires-Dist: pymdown-extensions; extra == "doc"
|
|
38
|
+
|
|
39
|
+
Cellects: Cell Expansion Computer Tracking Software
|
|
40
|
+
===================================================
|
|
41
|
+
|
|
42
|
+
Description
|
|
43
|
+
-----------
|
|
44
|
+
|
|
45
|
+
Cellects is a tracking software for organisms whose shape and size change over time.
|
|
46
|
+
Cellects’ main strengths are its broad scope of action,
|
|
47
|
+
automated computation of a variety of geometrical descriptors, easy installation and user-friendly interface.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
⚠️ **Note:** At this stage, Cellects is available **only from source**.
|
|
56
|
+
You will need **Miniconda3** and **git** installed on your system.
|
|
57
|
+
|
|
58
|
+
- Install [Miniconda3](https://docs.conda.io/en/latest/miniconda.html)
|
|
59
|
+
(choose the installer for your operating system).
|
|
60
|
+
- Install [git](https://git-scm.com/downloads)
|
|
61
|
+
(also available through package managers like `apt`, `brew`, or `choco`).
|
|
62
|
+
|
|
63
|
+
Once these prerequisites are installed, you can set up Cellects as follows:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Clone the repository
|
|
67
|
+
git clone https://github.com/Aurele-B/Cellects.git
|
|
68
|
+
cd Cellects
|
|
69
|
+
|
|
70
|
+
# Create and activate the environment
|
|
71
|
+
conda env create -f conda/env.yml
|
|
72
|
+
conda activate cellects-dev
|
|
73
|
+
|
|
74
|
+
# Install the package in editable mode
|
|
75
|
+
pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Launch the application:
|
|
79
|
+
```bash
|
|
80
|
+
Cellects
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Developer Guide
|
|
86
|
+
|
|
87
|
+
### Run Tests
|
|
88
|
+
Cellects uses `pytest` + `pytest-cov`.
|
|
89
|
+
Install test dependencies:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install -e ".[test]"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Run the test suite (with coverage enabled by default via `pyproject.toml`):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pytest
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You can access the coverage report with `coverage html` and open `htmlcov/index.html` in your browser.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
open htmlcov/index.html # macOS
|
|
105
|
+
xdg-open htmlcov/index.html # Linux
|
|
106
|
+
start htmlcov\index.html # Windows (PowerShell)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or explicitly:
|
|
110
|
+
```bash
|
|
111
|
+
pytest --cov=src/cellects --cov-report=term-missing
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Build Documentation
|
|
115
|
+
Install doc dependencies:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install -e ".[doc]"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Serve the docs locally:
|
|
122
|
+
```bash
|
|
123
|
+
mkdocs serve
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Open [http://127.0.0.1:8000](http://127.0.0.1:8000) in your browser.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Resources
|
|
131
|
+
- [User manual](https://github.com/Aurele-B/Cellects/blob/main/_old_doc/UserManual.md)
|
|
132
|
+
- [Usage example (video)](https://www.youtube.com/watch?v=N-k4p_aSPC0)
|
|
@@ -0,0 +1,44 @@
|
|
|
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=jcmYUeTiVtEkvIbZAYIASP5oCNiM909YQhaRHCY9y-U,6103
|
|
5
|
+
cellects/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
cellects/core/cellects_paths.py,sha256=vwFEYXVAD16w0euoTuJ8Ca0tUyelsoiDNmWqryU5k-k,880
|
|
7
|
+
cellects/core/cellects_threads.py,sha256=St1bAZTWdsrAYu1DBUyOwWSUJv5sellfaNVF3W14LfQ,88336
|
|
8
|
+
cellects/core/motion_analysis.py,sha256=7_fohdeRBFRbJot3g14E6gh58TP9muE4O5fuePqr84A,141997
|
|
9
|
+
cellects/core/one_image_analysis.py,sha256=hU9pa0coVA5s_CTiKgn4najlb8J3LQgEIocXCMi_4nM,68583
|
|
10
|
+
cellects/core/one_video_per_blob.py,sha256=8YKttLdf0gNO3yASdhvAdo-GEh9btDkXrzku7f-t4h0,35536
|
|
11
|
+
cellects/core/program_organizer.py,sha256=gRA3RLdj5i9wPzbqooHd6NoLkPYon5o57HaA7u1d0zQ,77988
|
|
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=tqOYJqBL_3vcBOYkCuHWVa3MBzcEiQiphEcS0QzLCJA,35563
|
|
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=7RX9s-jJkvx6qjnFZE81xKCOnhEeY1LhCIYqVlH5r24,116376
|
|
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/image_segmentation.py,sha256=g1UXEfb7UFAv0UzZOxFiMJ0rZb0wrnQ4dC69PSJk_NI,28184
|
|
29
|
+
cellects/image_analysis/morphological_operations.py,sha256=a4_SWxk9lik6RZ-DmPrjnkqaV_LUt3h8ar4IQ_F_GTQ,67908
|
|
30
|
+
cellects/image_analysis/network_functions.py,sha256=ahxbWWcq7AxXxelaoEALGqdTbWmDWumNWoUALF2MxoU,88545
|
|
31
|
+
cellects/image_analysis/one_image_analysis_threads.py,sha256=AjAEbJmtldlf23UxWs5my7xz2NIYy3wm25o13pnnYq8,17253
|
|
32
|
+
cellects/image_analysis/progressively_add_distant_shapes.py,sha256=GuTfU57A6JweJHa9dpv7dTVoLX3n2fY6EL9Ul_ukBqU,25799
|
|
33
|
+
cellects/image_analysis/shape_descriptors.py,sha256=8E36-A6hdgJLnj0jVj9_flFDCe2-DBCgpvjNNycyF2I,36546
|
|
34
|
+
cellects/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
+
cellects/utils/decorators.py,sha256=kjZWSK71l5-LrrH7BZHb0kdFaAikC_qZu14_KjIUCms,361
|
|
36
|
+
cellects/utils/formulas.py,sha256=Blw_dgYXb3sbxadONvHUUPoC6qlEVC1NJPMnEimNIxo,20397
|
|
37
|
+
cellects/utils/load_display_save.py,sha256=a_D9uhXPfne-glgFagc0_C_VqKybR9GY8aebc7uLgyg,38299
|
|
38
|
+
cellects/utils/utilitarian.py,sha256=hdCN6PP0z25N-JCElEcGdTzIjyKVG_TYl3BiFmZ2a2k,17248
|
|
39
|
+
cellects-0.1.2.dist-info/LICENSE.odt,sha256=tvKfCylOEuclQm5zOnNWBx0Fp4n7M5yqSmTr6SAekbY,37132
|
|
40
|
+
cellects-0.1.2.dist-info/METADATA,sha256=gYz_r3dVpqwwYTMKtV_SpMov9bWgFgQy7PyPOZJINlw,3354
|
|
41
|
+
cellects-0.1.2.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
|
42
|
+
cellects-0.1.2.dist-info/entry_points.txt,sha256=JT6rEvKpUuKyDPvfOYma-IMQNvfnKMstFMAoVJhXIGc,60
|
|
43
|
+
cellects-0.1.2.dist-info/top_level.txt,sha256=8VlvCH4ka3bqugIpQnOVjc3UV9Vavfx5SXNyUV9_lGw,9
|
|
44
|
+
cellects-0.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cellects
|