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.
Files changed (46) hide show
  1. cellects/__init__.py +0 -0
  2. cellects/__main__.py +49 -0
  3. cellects/config/__init__.py +0 -0
  4. cellects/config/all_vars_dict.py +154 -0
  5. cellects/core/__init__.py +0 -0
  6. cellects/core/cellects_paths.py +30 -0
  7. cellects/core/cellects_threads.py +1464 -0
  8. cellects/core/motion_analysis.py +1931 -0
  9. cellects/core/one_image_analysis.py +1065 -0
  10. cellects/core/one_video_per_blob.py +679 -0
  11. cellects/core/program_organizer.py +1347 -0
  12. cellects/core/script_based_run.py +154 -0
  13. cellects/gui/__init__.py +0 -0
  14. cellects/gui/advanced_parameters.py +1258 -0
  15. cellects/gui/cellects.py +189 -0
  16. cellects/gui/custom_widgets.py +789 -0
  17. cellects/gui/first_window.py +449 -0
  18. cellects/gui/if_several_folders_window.py +239 -0
  19. cellects/gui/image_analysis_window.py +1909 -0
  20. cellects/gui/required_output.py +232 -0
  21. cellects/gui/video_analysis_window.py +656 -0
  22. cellects/icons/__init__.py +0 -0
  23. cellects/icons/cellects_icon.icns +0 -0
  24. cellects/icons/cellects_icon.ico +0 -0
  25. cellects/image_analysis/__init__.py +0 -0
  26. cellects/image_analysis/cell_leaving_detection.py +54 -0
  27. cellects/image_analysis/cluster_flux_study.py +102 -0
  28. cellects/image_analysis/extract_exif.py +61 -0
  29. cellects/image_analysis/fractal_analysis.py +184 -0
  30. cellects/image_analysis/fractal_functions.py +108 -0
  31. cellects/image_analysis/image_segmentation.py +272 -0
  32. cellects/image_analysis/morphological_operations.py +867 -0
  33. cellects/image_analysis/network_functions.py +1244 -0
  34. cellects/image_analysis/one_image_analysis_threads.py +289 -0
  35. cellects/image_analysis/progressively_add_distant_shapes.py +246 -0
  36. cellects/image_analysis/shape_descriptors.py +981 -0
  37. cellects/utils/__init__.py +0 -0
  38. cellects/utils/formulas.py +881 -0
  39. cellects/utils/load_display_save.py +1016 -0
  40. cellects/utils/utilitarian.py +516 -0
  41. cellects-0.1.0.dev1.dist-info/LICENSE.odt +0 -0
  42. cellects-0.1.0.dev1.dist-info/METADATA +131 -0
  43. cellects-0.1.0.dev1.dist-info/RECORD +46 -0
  44. cellects-0.1.0.dev1.dist-info/WHEEL +5 -0
  45. cellects-0.1.0.dev1.dist-info/entry_points.txt +2 -0
  46. 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])
@@ -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,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (76.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+