celldetective 1.4.2__py3-none-any.whl → 1.5.0b0__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.
- celldetective/__init__.py +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +304 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +197 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
- celldetective-1.5.0b0.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- /celldetective/{gui/processes → processes}/downloader.py +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
from typing import Union, List
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def step_function(t: Union[np.ndarray, List], t_shift: float, dt: float) -> np.ndarray:
|
|
6
|
+
"""
|
|
7
|
+
Computes a step function using the logistic sigmoid function.
|
|
8
|
+
|
|
9
|
+
This function calculates the value of a sigmoid function, which is often used to model
|
|
10
|
+
a step change or transition. The sigmoid function is defined as:
|
|
11
|
+
|
|
12
|
+
.. math::
|
|
13
|
+
f(t) = \\frac{1}{1 + \\exp{\\left( -\\frac{t - t_{shift}}{dt} \\right)}}
|
|
14
|
+
|
|
15
|
+
where `t` is the input variable, `t_shift` is the point of the transition, and `dt` controls
|
|
16
|
+
the steepness of the transition.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
t : array_like
|
|
21
|
+
The input values for which the step function will be computed.
|
|
22
|
+
t_shift : float
|
|
23
|
+
The point in the `t` domain where the transition occurs.
|
|
24
|
+
dt : float
|
|
25
|
+
The parameter that controls the steepness of the transition. Smaller values make the
|
|
26
|
+
transition steeper, while larger values make it smoother.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
array_like
|
|
31
|
+
The computed values of the step function for each value in `t`.
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
>>> import numpy as np
|
|
36
|
+
>>> t = np.array([0, 1, 2, 3, 4, 5])
|
|
37
|
+
>>> t_shift = 2
|
|
38
|
+
>>> dt = 1
|
|
39
|
+
>>> step_function(t, t_shift, dt)
|
|
40
|
+
array([0.26894142, 0.37754067, 0.5 , 0.62245933, 0.73105858, 0.81757448])
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
return 1 / (1 + np.exp(-(t - t_shift) / dt))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def derivative(x, timeline, window, mode="bi"):
|
|
47
|
+
"""
|
|
48
|
+
Compute the derivative of a given array of values with respect to time using a specified numerical differentiation method.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
x : array_like
|
|
53
|
+
The input array of values.
|
|
54
|
+
timeline : array_like
|
|
55
|
+
The array representing the time points corresponding to the input values.
|
|
56
|
+
window : int
|
|
57
|
+
The size of the window used for numerical differentiation. Must be a positive odd integer.
|
|
58
|
+
mode : {'bi', 'forward', 'backward'}, optional
|
|
59
|
+
The numerical differentiation method to be used:
|
|
60
|
+
- 'bi' (default): Bidirectional differentiation using a symmetric window.
|
|
61
|
+
- 'forward': Forward differentiation using a one-sided window.
|
|
62
|
+
- 'backward': Backward differentiation using a one-sided window.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
dxdt : ndarray
|
|
67
|
+
The computed derivative values of the input array with respect to time.
|
|
68
|
+
|
|
69
|
+
Raises
|
|
70
|
+
------
|
|
71
|
+
AssertionError
|
|
72
|
+
If the window size is not an odd integer and mode is 'bi'.
|
|
73
|
+
|
|
74
|
+
Notes
|
|
75
|
+
-----
|
|
76
|
+
- For 'bi' mode, the window size must be an odd number.
|
|
77
|
+
- For 'forward' mode, the derivative at the edge points may not be accurate due to the one-sided window.
|
|
78
|
+
- For 'backward' mode, the derivative at the first few points may not be accurate due to the one-sided window.
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
>>> import numpy as np
|
|
83
|
+
>>> x = np.array([1, 2, 4, 7, 11])
|
|
84
|
+
>>> timeline = np.array([0, 1, 2, 3, 4])
|
|
85
|
+
>>> window = 3
|
|
86
|
+
>>> derivative(x, timeline, window, mode='bi')
|
|
87
|
+
array([3., 3., 3.])
|
|
88
|
+
|
|
89
|
+
>>> derivative(x, timeline, window, mode='forward')
|
|
90
|
+
array([1., 2., 3.])
|
|
91
|
+
|
|
92
|
+
>>> derivative(x, timeline, window, mode='backward')
|
|
93
|
+
array([3., 3., 3., 3.])
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
# modes = bi, forward, backward
|
|
97
|
+
dxdt = np.zeros(len(x))
|
|
98
|
+
dxdt[:] = np.nan
|
|
99
|
+
|
|
100
|
+
if mode == "bi":
|
|
101
|
+
assert window % 2 == 1, "Please set an odd window for the bidirectional mode"
|
|
102
|
+
lower_bound = window // 2
|
|
103
|
+
upper_bound = len(x) - window // 2
|
|
104
|
+
elif mode == "forward":
|
|
105
|
+
lower_bound = 0
|
|
106
|
+
upper_bound = len(x) - window
|
|
107
|
+
elif mode == "backward":
|
|
108
|
+
lower_bound = window
|
|
109
|
+
upper_bound = len(x)
|
|
110
|
+
|
|
111
|
+
for t in range(lower_bound, upper_bound):
|
|
112
|
+
if mode == "bi":
|
|
113
|
+
dxdt[t] = (x[t + window // 2] - x[t - window // 2]) / (
|
|
114
|
+
timeline[t + window // 2] - timeline[t - window // 2]
|
|
115
|
+
)
|
|
116
|
+
elif mode == "forward":
|
|
117
|
+
dxdt[t] = (x[t + window] - x[t]) / (timeline[t + window] - timeline[t])
|
|
118
|
+
elif mode == "backward":
|
|
119
|
+
dxdt[t] = (x[t] - x[t - window]) / (timeline[t] - timeline[t - window])
|
|
120
|
+
return dxdt
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def differentiate_per_track(tracks, measurement, window_size=3, mode="bi"):
|
|
124
|
+
|
|
125
|
+
groupby_cols = ["TRACK_ID"]
|
|
126
|
+
if "position" in list(tracks.columns):
|
|
127
|
+
groupby_cols = ["position"] + groupby_cols
|
|
128
|
+
|
|
129
|
+
tracks = tracks.sort_values(by=groupby_cols + ["FRAME"], ignore_index=True)
|
|
130
|
+
tracks = tracks.reset_index(drop=True)
|
|
131
|
+
for tid, group in tracks.groupby(groupby_cols):
|
|
132
|
+
indices = group.index
|
|
133
|
+
timeline = group["FRAME"].values
|
|
134
|
+
signal = group[measurement].values
|
|
135
|
+
dsignal = derivative(signal, timeline, window_size, mode=mode)
|
|
136
|
+
tracks.loc[indices, "d/dt." + measurement] = dsignal
|
|
137
|
+
return tracks
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def velocity_per_track(tracks, window_size=3, mode="bi"):
|
|
141
|
+
|
|
142
|
+
groupby_cols = ["TRACK_ID"]
|
|
143
|
+
if "position" in list(tracks.columns):
|
|
144
|
+
groupby_cols = ["position"] + groupby_cols
|
|
145
|
+
|
|
146
|
+
tracks = tracks.sort_values(by=groupby_cols + ["FRAME"], ignore_index=True)
|
|
147
|
+
tracks = tracks.reset_index(drop=True)
|
|
148
|
+
for tid, group in tracks.groupby(groupby_cols):
|
|
149
|
+
indices = group.index
|
|
150
|
+
timeline = group["FRAME"].values
|
|
151
|
+
x = group["POSITION_X"].values
|
|
152
|
+
y = group["POSITION_Y"].values
|
|
153
|
+
v = velocity(x, y, timeline, window=window_size, mode=mode)
|
|
154
|
+
v_abs = magnitude_velocity(v)
|
|
155
|
+
tracks.loc[indices, "velocity"] = v_abs
|
|
156
|
+
return tracks
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def velocity(x, y, timeline, window, mode="bi"):
|
|
160
|
+
"""
|
|
161
|
+
Compute the velocity vector of a given 2D trajectory represented by arrays of x and y coordinates
|
|
162
|
+
with respect to time using a specified numerical differentiation method.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
x : array_like
|
|
167
|
+
The array of x-coordinates of the trajectory.
|
|
168
|
+
y : array_like
|
|
169
|
+
The array of y-coordinates of the trajectory.
|
|
170
|
+
timeline : array_like
|
|
171
|
+
The array representing the time points corresponding to the x and y coordinates.
|
|
172
|
+
window : int
|
|
173
|
+
The size of the window used for numerical differentiation. Must be a positive odd integer.
|
|
174
|
+
mode : {'bi', 'forward', 'backward'}, optional
|
|
175
|
+
The numerical differentiation method to be used:
|
|
176
|
+
- 'bi' (default): Bidirectional differentiation using a symmetric window.
|
|
177
|
+
- 'forward': Forward differentiation using a one-sided window.
|
|
178
|
+
- 'backward': Backward differentiation using a one-sided window.
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
v : ndarray
|
|
183
|
+
The computed velocity vector of the 2D trajectory with respect to time.
|
|
184
|
+
The first column represents the x-component of velocity, and the second column represents the y-component.
|
|
185
|
+
|
|
186
|
+
Raises
|
|
187
|
+
------
|
|
188
|
+
AssertionError
|
|
189
|
+
If the window size is not an odd integer and mode is 'bi'.
|
|
190
|
+
|
|
191
|
+
Notes
|
|
192
|
+
-----
|
|
193
|
+
- For 'bi' mode, the window size must be an odd number.
|
|
194
|
+
- For 'forward' mode, the velocity at the edge points may not be accurate due to the one-sided window.
|
|
195
|
+
- For 'backward' mode, the velocity at the first few points may not be accurate due to the one-sided window.
|
|
196
|
+
|
|
197
|
+
Examples
|
|
198
|
+
--------
|
|
199
|
+
>>> import numpy as np
|
|
200
|
+
>>> x = np.array([1, 2, 4, 7, 11])
|
|
201
|
+
>>> y = np.array([0, 3, 5, 8, 10])
|
|
202
|
+
>>> timeline = np.array([0, 1, 2, 3, 4])
|
|
203
|
+
>>> window = 3
|
|
204
|
+
>>> velocity(x, y, timeline, window, mode='bi')
|
|
205
|
+
array([[3., 3.],
|
|
206
|
+
[3., 3.]])
|
|
207
|
+
|
|
208
|
+
>>> velocity(x, y, timeline, window, mode='forward')
|
|
209
|
+
array([[2., 2.],
|
|
210
|
+
[3., 3.]])
|
|
211
|
+
|
|
212
|
+
>>> velocity(x, y, timeline, window, mode='backward')
|
|
213
|
+
array([[3., 3.],
|
|
214
|
+
[3., 3.]])
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
v = np.zeros((len(x), 2))
|
|
218
|
+
v[:, :] = np.nan
|
|
219
|
+
|
|
220
|
+
v[:, 0] = derivative(x, timeline, window, mode=mode)
|
|
221
|
+
v[:, 1] = derivative(y, timeline, window, mode=mode)
|
|
222
|
+
|
|
223
|
+
return v
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def magnitude_velocity(v_matrix):
|
|
227
|
+
"""
|
|
228
|
+
Compute the magnitude of velocity vectors given a matrix representing 2D velocity vectors.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
v_matrix : array_like
|
|
233
|
+
The matrix where each row represents a 2D velocity vector with the first column
|
|
234
|
+
being the x-component and the second column being the y-component.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
magnitude : ndarray
|
|
239
|
+
The computed magnitudes of the input velocity vectors.
|
|
240
|
+
|
|
241
|
+
Notes
|
|
242
|
+
-----
|
|
243
|
+
- If a velocity vector has NaN components, the corresponding magnitude will be NaN.
|
|
244
|
+
- The function handles NaN values in the input matrix gracefully.
|
|
245
|
+
|
|
246
|
+
Examples
|
|
247
|
+
--------
|
|
248
|
+
>>> import numpy as np
|
|
249
|
+
>>> v_matrix = np.array([[3, 4],
|
|
250
|
+
... [2, 2],
|
|
251
|
+
... [3, 3]])
|
|
252
|
+
>>> magnitude_velocity(v_matrix)
|
|
253
|
+
array([5., 2.82842712, 4.24264069])
|
|
254
|
+
|
|
255
|
+
>>> v_matrix_with_nan = np.array([[3, 4],
|
|
256
|
+
... [np.nan, 2],
|
|
257
|
+
... [3, np.nan]])
|
|
258
|
+
>>> magnitude_velocity(v_matrix_with_nan)
|
|
259
|
+
array([5., nan, nan])
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
magnitude = np.zeros(len(v_matrix))
|
|
263
|
+
magnitude[:] = np.nan
|
|
264
|
+
for i in range(len(v_matrix)):
|
|
265
|
+
if v_matrix[i, 0] == v_matrix[i, 0]:
|
|
266
|
+
magnitude[i] = np.sqrt(v_matrix[i, 0] ** 2 + v_matrix[i, 1] ** 2)
|
|
267
|
+
return magnitude
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def orientation(v_matrix):
|
|
271
|
+
"""
|
|
272
|
+
Compute the orientation angles (in radians) of 2D velocity vectors given a matrix representing velocity vectors.
|
|
273
|
+
|
|
274
|
+
Parameters
|
|
275
|
+
----------
|
|
276
|
+
v_matrix : array_like
|
|
277
|
+
The matrix where each row represents a 2D velocity vector with the first column
|
|
278
|
+
being the x-component and the second column being the y-component.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
orientation_array : ndarray
|
|
283
|
+
The computed orientation angles of the input velocity vectors in radians.
|
|
284
|
+
If a velocity vector has NaN components, the corresponding orientation angle will be NaN.
|
|
285
|
+
|
|
286
|
+
Examples
|
|
287
|
+
--------
|
|
288
|
+
>>> import numpy as np
|
|
289
|
+
>>> v_matrix = np.array([[3, 4],
|
|
290
|
+
... [2, 2],
|
|
291
|
+
... [-3, -3]])
|
|
292
|
+
>>> orientation(v_matrix)
|
|
293
|
+
array([0.92729522, 0.78539816, -2.35619449])
|
|
294
|
+
|
|
295
|
+
>>> v_matrix_with_nan = np.array([[3, 4],
|
|
296
|
+
... [np.nan, 2],
|
|
297
|
+
... [3, np.nan]])
|
|
298
|
+
>>> orientation(v_matrix_with_nan)
|
|
299
|
+
array([0.92729522, nan, nan])
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
orientation_array = np.zeros(len(v_matrix))
|
|
303
|
+
for t in range(len(orientation_array)):
|
|
304
|
+
if v_matrix[t, 0] == v_matrix[t, 0]:
|
|
305
|
+
orientation_array[t] = np.arctan2(v_matrix[t, 0], v_matrix[t, 1])
|
|
306
|
+
return orientation_array
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def safe_log(array):
|
|
310
|
+
"""
|
|
311
|
+
Safely computes the base-10 logarithm for numeric inputs, handling invalid or non-positive values.
|
|
312
|
+
|
|
313
|
+
Parameters
|
|
314
|
+
----------
|
|
315
|
+
array : int, float, list, or numpy.ndarray
|
|
316
|
+
The input value or array for which to compute the logarithm.
|
|
317
|
+
Can be a single number (int or float), a list, or a numpy array.
|
|
318
|
+
|
|
319
|
+
Returns
|
|
320
|
+
-------
|
|
321
|
+
float or numpy.ndarray
|
|
322
|
+
- If the input is a single numeric value, returns the base-10 logarithm as a float, or `np.nan` if the value is non-positive.
|
|
323
|
+
- If the input is a list or numpy array, returns a numpy array with the base-10 logarithm of each element.
|
|
324
|
+
Invalid or non-positive values are replaced with `np.nan`.
|
|
325
|
+
|
|
326
|
+
Notes
|
|
327
|
+
-----
|
|
328
|
+
- Non-positive values (`<= 0`) are considered invalid and will result in `np.nan`.
|
|
329
|
+
- NaN values in the input array are preserved in the output.
|
|
330
|
+
- If the input is a list, it is converted to a numpy array for processing.
|
|
331
|
+
|
|
332
|
+
Examples
|
|
333
|
+
--------
|
|
334
|
+
>>> safe_log(10)
|
|
335
|
+
1.0
|
|
336
|
+
|
|
337
|
+
>>> safe_log(-5)
|
|
338
|
+
nan
|
|
339
|
+
|
|
340
|
+
>>> safe_log([10, 0, -5, 100])
|
|
341
|
+
array([1.0, nan, nan, 2.0])
|
|
342
|
+
|
|
343
|
+
>>> import numpy as np
|
|
344
|
+
>>> safe_log(np.array([1, 10, 100]))
|
|
345
|
+
array([0.0, 1.0, 2.0])
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
array = np.asarray(array, dtype=float)
|
|
349
|
+
result = np.where(array > 0, np.log10(array), np.nan)
|
|
350
|
+
|
|
351
|
+
return result.item() if np.isscalar(array) else result
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from glob import glob
|
|
3
|
+
from shutil import rmtree
|
|
4
|
+
|
|
5
|
+
from natsort import natsorted
|
|
6
|
+
|
|
7
|
+
from celldetective.utils.downloaders import get_zenodo_files
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_tracking_configs_list(return_path=False):
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
Retrieve a list of available tracking configurations.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
return_path : bool, optional
|
|
18
|
+
If True, also returns the path to the models. Default is False.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
list or tuple
|
|
23
|
+
If return_path is False, returns a list of available tracking configurations.
|
|
24
|
+
If return_path is True, returns a tuple containing the list of models and the path to the models.
|
|
25
|
+
|
|
26
|
+
Notes
|
|
27
|
+
-----
|
|
28
|
+
This function retrieves the list of available tracking configurations by searching for model directories
|
|
29
|
+
in the predefined model path. The model path is derived from the parent directory of the current script
|
|
30
|
+
location and the path to the model directory. By default, it returns only the names of the models.
|
|
31
|
+
If return_path is set to True, it also returns the path to the models.
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
>>> models = get_tracking_configs_list()
|
|
36
|
+
# Retrieve a list of available tracking configurations.
|
|
37
|
+
|
|
38
|
+
>>> models, path = get_tracking_configs_list(return_path=True)
|
|
39
|
+
# Retrieve a list of available tracking configurations.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
modelpath = os.sep.join(
|
|
44
|
+
[
|
|
45
|
+
os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],
|
|
46
|
+
# "celldetective",
|
|
47
|
+
"models",
|
|
48
|
+
"tracking_configs",
|
|
49
|
+
os.sep,
|
|
50
|
+
]
|
|
51
|
+
)
|
|
52
|
+
available_models = glob(modelpath + "*.json")
|
|
53
|
+
available_models = [m.replace("\\", "/").split("/")[-1] for m in available_models]
|
|
54
|
+
available_models = [m.replace("\\", "/").split(".")[0] for m in available_models]
|
|
55
|
+
|
|
56
|
+
if not return_path:
|
|
57
|
+
return available_models
|
|
58
|
+
else:
|
|
59
|
+
return available_models, modelpath
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_signal_models_list(return_path=False):
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
Retrieve a list of available signal detection models.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
return_path : bool, optional
|
|
70
|
+
If True, also returns the path to the models. Default is False.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
list or tuple
|
|
75
|
+
If return_path is False, returns a list of available signal detection models.
|
|
76
|
+
If return_path is True, returns a tuple containing the list of models and the path to the models.
|
|
77
|
+
|
|
78
|
+
Notes
|
|
79
|
+
-----
|
|
80
|
+
This function retrieves the list of available signal detection models by searching for model directories
|
|
81
|
+
in the predefined model path. The model path is derived from the parent directory of the current script
|
|
82
|
+
location and the path to the model directory. By default, it returns only the names of the models.
|
|
83
|
+
If return_path is set to True, it also returns the path to the models.
|
|
84
|
+
|
|
85
|
+
Examples
|
|
86
|
+
--------
|
|
87
|
+
>>> models = get_signal_models_list()
|
|
88
|
+
# Retrieve a list of available signal detection models.
|
|
89
|
+
|
|
90
|
+
>>> models, path = get_signal_models_list(return_path=True)
|
|
91
|
+
# Retrieve a list of available signal detection models and the path to the models.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
modelpath = os.sep.join(
|
|
96
|
+
[
|
|
97
|
+
os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],
|
|
98
|
+
# "celldetective",
|
|
99
|
+
"models",
|
|
100
|
+
"signal_detection",
|
|
101
|
+
os.sep,
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
repository_models = get_zenodo_files(
|
|
105
|
+
cat=os.sep.join(["models", "signal_detection"])
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
available_models = glob(modelpath + f"*{os.sep}")
|
|
109
|
+
available_models = [m.replace("\\", "/").split("/")[-2] for m in available_models]
|
|
110
|
+
available_models = [
|
|
111
|
+
m
|
|
112
|
+
for m in available_models
|
|
113
|
+
if os.path.exists(os.path.join(modelpath, m, "config_input.json"))
|
|
114
|
+
]
|
|
115
|
+
for rm in repository_models:
|
|
116
|
+
if rm not in available_models:
|
|
117
|
+
available_models.append(rm)
|
|
118
|
+
|
|
119
|
+
if not return_path:
|
|
120
|
+
return available_models
|
|
121
|
+
else:
|
|
122
|
+
return available_models, modelpath
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_pair_signal_models_list(return_path=False):
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
Retrieve a list of available signal detection models.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
return_path : bool, optional
|
|
133
|
+
If True, also returns the path to the models. Default is False.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
list or tuple
|
|
138
|
+
If return_path is False, returns a list of available signal detection models.
|
|
139
|
+
If return_path is True, returns a tuple containing the list of models and the path to the models.
|
|
140
|
+
|
|
141
|
+
Notes
|
|
142
|
+
-----
|
|
143
|
+
This function retrieves the list of available signal detection models by searching for model directories
|
|
144
|
+
in the predefined model path. The model path is derived from the parent directory of the current script
|
|
145
|
+
location and the path to the model directory. By default, it returns only the names of the models.
|
|
146
|
+
If return_path is set to True, it also returns the path to the models.
|
|
147
|
+
|
|
148
|
+
Examples
|
|
149
|
+
--------
|
|
150
|
+
>>> models = get_signal_models_list()
|
|
151
|
+
# Retrieve a list of available signal detection models.
|
|
152
|
+
|
|
153
|
+
>>> models, path = get_signal_models_list(return_path=True)
|
|
154
|
+
# Retrieve a list of available signal detection models and the path to the models.
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
modelpath = os.sep.join(
|
|
159
|
+
[
|
|
160
|
+
os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],
|
|
161
|
+
# "celldetective",
|
|
162
|
+
"models",
|
|
163
|
+
"pair_signal_detection",
|
|
164
|
+
os.sep,
|
|
165
|
+
]
|
|
166
|
+
)
|
|
167
|
+
# repository_models = get_zenodo_files(cat=os.sep.join(["models", "pair_signal_detection"]))
|
|
168
|
+
|
|
169
|
+
available_models = glob(modelpath + f"*{os.sep}")
|
|
170
|
+
available_models = [m.replace("\\", "/").split("/")[-2] for m in available_models]
|
|
171
|
+
# for rm in repository_models:
|
|
172
|
+
# if rm not in available_models:
|
|
173
|
+
# available_models.append(rm)
|
|
174
|
+
|
|
175
|
+
if not return_path:
|
|
176
|
+
return available_models
|
|
177
|
+
else:
|
|
178
|
+
return available_models, modelpath
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_segmentation_models_list(mode="targets", return_path=False):
|
|
182
|
+
|
|
183
|
+
modelpath = os.sep.join(
|
|
184
|
+
[
|
|
185
|
+
os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],
|
|
186
|
+
# "celldetective",
|
|
187
|
+
"models",
|
|
188
|
+
f"segmentation_{mode}",
|
|
189
|
+
os.sep,
|
|
190
|
+
]
|
|
191
|
+
)
|
|
192
|
+
if not os.path.exists(modelpath):
|
|
193
|
+
os.mkdir(modelpath)
|
|
194
|
+
repository_models = []
|
|
195
|
+
else:
|
|
196
|
+
repository_models = get_zenodo_files(
|
|
197
|
+
cat=os.sep.join(["models", f"segmentation_{mode}"])
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
available_models = natsorted(glob(modelpath + "*/"))
|
|
201
|
+
available_models = [m.replace("\\", "/").split("/")[-2] for m in available_models]
|
|
202
|
+
|
|
203
|
+
# Auto model cleanup
|
|
204
|
+
to_remove = []
|
|
205
|
+
for model in available_models:
|
|
206
|
+
path = modelpath + model
|
|
207
|
+
files = glob(path + os.sep + "*")
|
|
208
|
+
if path + os.sep + "config_input.json" not in files:
|
|
209
|
+
rmtree(path)
|
|
210
|
+
to_remove.append(model)
|
|
211
|
+
for m in to_remove:
|
|
212
|
+
available_models.remove(m)
|
|
213
|
+
|
|
214
|
+
for rm in repository_models:
|
|
215
|
+
if rm not in available_models:
|
|
216
|
+
available_models.append(rm)
|
|
217
|
+
|
|
218
|
+
if not return_path:
|
|
219
|
+
return available_models
|
|
220
|
+
else:
|
|
221
|
+
return available_models, modelpath
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def get_segmentation_datasets_list(return_path=False):
|
|
225
|
+
"""
|
|
226
|
+
Retrieves a list of available segmentation datasets from both the local 'celldetective/datasets/segmentation_annotations'
|
|
227
|
+
directory and a Zenodo repository, optionally returning the path to the local datasets directory.
|
|
228
|
+
|
|
229
|
+
This function compiles a list of available segmentation datasets by first identifying datasets stored locally
|
|
230
|
+
within a specified path related to the script's directory. It then extends this list with datasets available
|
|
231
|
+
in a Zenodo repository, ensuring no duplicates are added. The function can return just the list of dataset
|
|
232
|
+
names or, if specified, also return the path to the local datasets directory.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
return_path : bool, optional
|
|
237
|
+
If True, the function returns a tuple containing the list of available dataset names and the path to the
|
|
238
|
+
local datasets directory. If False, only the list of dataset names is returned (default is False).
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
list or (list, str)
|
|
243
|
+
If return_path is False, returns a list of strings, each string being the name of an available dataset.
|
|
244
|
+
If return_path is True, returns a tuple where the first element is this list and the second element is a
|
|
245
|
+
string representing the path to the local datasets directory.
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
datasets_path = os.sep.join(
|
|
250
|
+
[
|
|
251
|
+
os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],
|
|
252
|
+
# "celldetective",
|
|
253
|
+
"datasets",
|
|
254
|
+
"segmentation_annotations",
|
|
255
|
+
os.sep,
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
repository_datasets = get_zenodo_files(
|
|
259
|
+
cat=os.sep.join(["datasets", "segmentation_annotations"])
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
available_datasets = natsorted(glob(datasets_path + "*/"))
|
|
263
|
+
available_datasets = [
|
|
264
|
+
m.replace("\\", "/").split("/")[-2] for m in available_datasets
|
|
265
|
+
]
|
|
266
|
+
for rm in repository_datasets:
|
|
267
|
+
if rm not in available_datasets:
|
|
268
|
+
available_datasets.append(rm)
|
|
269
|
+
|
|
270
|
+
if not return_path:
|
|
271
|
+
return available_datasets
|
|
272
|
+
else:
|
|
273
|
+
return available_datasets, datasets_path
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def get_signal_datasets_list(return_path=False):
|
|
277
|
+
"""
|
|
278
|
+
Retrieves a list of available signal datasets from both the local 'celldetective/datasets/signal_annotations' directory
|
|
279
|
+
and a Zenodo repository, optionally returning the path to the local datasets directory.
|
|
280
|
+
|
|
281
|
+
This function compiles a list of available signal datasets by first identifying datasets stored locally within a specified
|
|
282
|
+
path related to the script's directory. It then extends this list with datasets available in a Zenodo repository, ensuring
|
|
283
|
+
no duplicates are added. The function can return just the list of dataset names or, if specified, also return the path to
|
|
284
|
+
the local datasets directory.
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
return_path : bool, optional
|
|
289
|
+
If True, the function returns a tuple containing the list of available dataset names and the path to the local datasets
|
|
290
|
+
directory. If False, only the list of dataset names is returned (default is False).
|
|
291
|
+
|
|
292
|
+
Returns
|
|
293
|
+
-------
|
|
294
|
+
list or (list, str)
|
|
295
|
+
If return_path is False, returns a list of strings, each string being the name of an available dataset. If return_path
|
|
296
|
+
is True, returns a tuple where the first element is this list and the second element is a string representing the path
|
|
297
|
+
to the local datasets directory.
|
|
298
|
+
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
datasets_path = os.sep.join(
|
|
302
|
+
[
|
|
303
|
+
os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],
|
|
304
|
+
# "celldetective",
|
|
305
|
+
"datasets",
|
|
306
|
+
"signal_annotations",
|
|
307
|
+
os.sep,
|
|
308
|
+
]
|
|
309
|
+
)
|
|
310
|
+
repository_datasets = get_zenodo_files(
|
|
311
|
+
cat=os.sep.join(["datasets", "signal_annotations"])
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
available_datasets = natsorted(glob(datasets_path + "*/"))
|
|
315
|
+
available_datasets = [
|
|
316
|
+
m.replace("\\", "/").split("/")[-2] for m in available_datasets
|
|
317
|
+
]
|
|
318
|
+
for rm in repository_datasets:
|
|
319
|
+
if rm not in available_datasets:
|
|
320
|
+
available_datasets.append(rm)
|
|
321
|
+
|
|
322
|
+
if not return_path:
|
|
323
|
+
return available_datasets
|
|
324
|
+
else:
|
|
325
|
+
return available_datasets, datasets_path
|