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.
Files changed (44) 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 +155 -0
  5. cellects/core/__init__.py +0 -0
  6. cellects/core/cellects_paths.py +31 -0
  7. cellects/core/cellects_threads.py +1451 -0
  8. cellects/core/motion_analysis.py +2010 -0
  9. cellects/core/one_image_analysis.py +1061 -0
  10. cellects/core/one_video_per_blob.py +540 -0
  11. cellects/core/program_organizer.py +1316 -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 +790 -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 +2066 -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/image_segmentation.py +706 -0
  29. cellects/image_analysis/morphological_operations.py +1635 -0
  30. cellects/image_analysis/network_functions.py +1757 -0
  31. cellects/image_analysis/one_image_analysis_threads.py +289 -0
  32. cellects/image_analysis/progressively_add_distant_shapes.py +508 -0
  33. cellects/image_analysis/shape_descriptors.py +1016 -0
  34. cellects/utils/__init__.py +0 -0
  35. cellects/utils/decorators.py +14 -0
  36. cellects/utils/formulas.py +637 -0
  37. cellects/utils/load_display_save.py +1054 -0
  38. cellects/utils/utilitarian.py +490 -0
  39. cellects-0.1.2.dist-info/LICENSE.odt +0 -0
  40. cellects-0.1.2.dist-info/METADATA +132 -0
  41. cellects-0.1.2.dist-info/RECORD +44 -0
  42. cellects-0.1.2.dist-info/WHEEL +5 -0
  43. cellects-0.1.2.dist-info/entry_points.txt +2 -0
  44. 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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (76.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ Cellects = cellects.__main__:run_cellects
@@ -0,0 +1 @@
1
+ cellects