celldetective 1.1.0__py3-none-any.whl → 1.1.1.post1__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/__main__.py +5 -19
- celldetective/extra_properties.py +63 -53
- celldetective/filters.py +39 -11
- celldetective/gui/classifier_widget.py +56 -7
- celldetective/gui/control_panel.py +5 -0
- celldetective/gui/layouts.py +3 -2
- celldetective/gui/measurement_options.py +13 -109
- celldetective/gui/plot_signals_ui.py +1 -0
- celldetective/gui/process_block.py +1 -1
- celldetective/gui/survival_ui.py +7 -1
- celldetective/gui/tableUI.py +294 -28
- celldetective/gui/thresholds_gui.py +51 -10
- celldetective/gui/viewers.py +169 -22
- celldetective/io.py +41 -17
- celldetective/measure.py +13 -238
- celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +29 -0
- celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
- celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +37 -0
- celldetective/neighborhood.py +4 -1
- celldetective/preprocessing.py +483 -143
- celldetective/scripts/segment_cells.py +26 -7
- celldetective/scripts/train_segmentation_model.py +35 -34
- celldetective/segmentation.py +29 -20
- celldetective/signals.py +13 -231
- celldetective/tracking.py +2 -1
- celldetective/utils.py +440 -26
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.post1.dist-info}/METADATA +1 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.post1.dist-info}/RECORD +34 -30
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.post1.dist-info}/WHEEL +1 -1
- tests/test_preprocessing.py +37 -0
- tests/test_utils.py +48 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.post1.dist-info}/LICENSE +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.post1.dist-info}/top_level.txt +0 -0
celldetective/utils.py
CHANGED
|
@@ -22,6 +22,413 @@ import zipfile
|
|
|
22
22
|
from tqdm import tqdm
|
|
23
23
|
import shutil
|
|
24
24
|
import tempfile
|
|
25
|
+
from scipy.interpolate import griddata
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def derivative(x, timeline, window, mode='bi'):
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
Compute the derivative of a given array of values with respect to time using a specified numerical differentiation method.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
x : array_like
|
|
36
|
+
The input array of values.
|
|
37
|
+
timeline : array_like
|
|
38
|
+
The array representing the time points corresponding to the input values.
|
|
39
|
+
window : int
|
|
40
|
+
The size of the window used for numerical differentiation. Must be a positive odd integer.
|
|
41
|
+
mode : {'bi', 'forward', 'backward'}, optional
|
|
42
|
+
The numerical differentiation method to be used:
|
|
43
|
+
- 'bi' (default): Bidirectional differentiation using a symmetric window.
|
|
44
|
+
- 'forward': Forward differentiation using a one-sided window.
|
|
45
|
+
- 'backward': Backward differentiation using a one-sided window.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
dxdt : ndarray
|
|
50
|
+
The computed derivative values of the input array with respect to time.
|
|
51
|
+
|
|
52
|
+
Raises
|
|
53
|
+
------
|
|
54
|
+
AssertionError
|
|
55
|
+
If the window size is not an odd integer and mode is 'bi'.
|
|
56
|
+
|
|
57
|
+
Notes
|
|
58
|
+
-----
|
|
59
|
+
- For 'bi' mode, the window size must be an odd number.
|
|
60
|
+
- For 'forward' mode, the derivative at the edge points may not be accurate due to the one-sided window.
|
|
61
|
+
- For 'backward' mode, the derivative at the first few points may not be accurate due to the one-sided window.
|
|
62
|
+
|
|
63
|
+
Examples
|
|
64
|
+
--------
|
|
65
|
+
>>> import numpy as np
|
|
66
|
+
>>> x = np.array([1, 2, 4, 7, 11])
|
|
67
|
+
>>> timeline = np.array([0, 1, 2, 3, 4])
|
|
68
|
+
>>> window = 3
|
|
69
|
+
>>> derivative(x, timeline, window, mode='bi')
|
|
70
|
+
array([3., 3., 3.])
|
|
71
|
+
|
|
72
|
+
>>> derivative(x, timeline, window, mode='forward')
|
|
73
|
+
array([1., 2., 3.])
|
|
74
|
+
|
|
75
|
+
>>> derivative(x, timeline, window, mode='backward')
|
|
76
|
+
array([3., 3., 3., 3.])
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# modes = bi, forward, backward
|
|
80
|
+
dxdt = np.zeros(len(x))
|
|
81
|
+
dxdt[:] = np.nan
|
|
82
|
+
|
|
83
|
+
if mode=='bi':
|
|
84
|
+
assert window%2==1,'Please set an odd window for the bidirectional mode'
|
|
85
|
+
lower_bound = window//2
|
|
86
|
+
upper_bound = len(x) - window//2 - 1
|
|
87
|
+
elif mode=='forward':
|
|
88
|
+
lower_bound = 0
|
|
89
|
+
upper_bound = len(x) - window
|
|
90
|
+
elif mode=='backward':
|
|
91
|
+
lower_bound = window
|
|
92
|
+
upper_bound = len(x)
|
|
93
|
+
|
|
94
|
+
for t in range(lower_bound,upper_bound):
|
|
95
|
+
if mode=='bi':
|
|
96
|
+
dxdt[t] = (x[t+window//2+1] - x[t-window//2]) / (timeline[t+window//2+1] - timeline[t-window//2])
|
|
97
|
+
elif mode=='forward':
|
|
98
|
+
dxdt[t] = (x[t+window] - x[t]) / (timeline[t+window] - timeline[t])
|
|
99
|
+
elif mode=='backward':
|
|
100
|
+
dxdt[t] = (x[t] - x[t-window]) / (timeline[t] - timeline[t-window])
|
|
101
|
+
return dxdt
|
|
102
|
+
|
|
103
|
+
def differentiate_per_track(tracks, measurement, window_size=3, mode='bi'):
|
|
104
|
+
|
|
105
|
+
groupby_cols = ['TRACK_ID']
|
|
106
|
+
if 'position' in list(tracks.columns):
|
|
107
|
+
groupby_cols = ['position']+groupby_cols
|
|
108
|
+
|
|
109
|
+
tracks = tracks.sort_values(by=groupby_cols+['FRAME'],ignore_index=True)
|
|
110
|
+
tracks = tracks.reset_index(drop=True)
|
|
111
|
+
for tid, group in tracks.groupby(groupby_cols):
|
|
112
|
+
indices = group.index
|
|
113
|
+
timeline = group['FRAME'].values
|
|
114
|
+
signal = group[measurement].values
|
|
115
|
+
dsignal = derivative(signal, timeline, window_size, mode=mode)
|
|
116
|
+
tracks.loc[indices, 'd/dt.'+measurement] = dsignal
|
|
117
|
+
return tracks
|
|
118
|
+
|
|
119
|
+
def velocity_per_track(tracks, window_size=3, mode='bi'):
|
|
120
|
+
|
|
121
|
+
groupby_cols = ['TRACK_ID']
|
|
122
|
+
if 'position' in list(tracks.columns):
|
|
123
|
+
groupby_cols = ['position']+groupby_cols
|
|
124
|
+
|
|
125
|
+
tracks = tracks.sort_values(by=groupby_cols+['FRAME'],ignore_index=True)
|
|
126
|
+
tracks = tracks.reset_index(drop=True)
|
|
127
|
+
for tid, group in tracks.groupby(groupby_cols):
|
|
128
|
+
indices = group.index
|
|
129
|
+
timeline = group['FRAME'].values
|
|
130
|
+
x = group['POSITION_X'].values
|
|
131
|
+
y = group['POSITION_Y'].values
|
|
132
|
+
v = velocity(x,y,timeline,window=window_size,mode=mode)
|
|
133
|
+
v_abs = magnitude_velocity(v)
|
|
134
|
+
tracks.loc[indices, 'velocity'] = v_abs
|
|
135
|
+
return tracks
|
|
136
|
+
|
|
137
|
+
def velocity(x,y,timeline,window,mode='bi'):
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
Compute the velocity vector of a given 2D trajectory represented by arrays of x and y coordinates
|
|
141
|
+
with respect to time using a specified numerical differentiation method.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
x : array_like
|
|
146
|
+
The array of x-coordinates of the trajectory.
|
|
147
|
+
y : array_like
|
|
148
|
+
The array of y-coordinates of the trajectory.
|
|
149
|
+
timeline : array_like
|
|
150
|
+
The array representing the time points corresponding to the x and y coordinates.
|
|
151
|
+
window : int
|
|
152
|
+
The size of the window used for numerical differentiation. Must be a positive odd integer.
|
|
153
|
+
mode : {'bi', 'forward', 'backward'}, optional
|
|
154
|
+
The numerical differentiation method to be used:
|
|
155
|
+
- 'bi' (default): Bidirectional differentiation using a symmetric window.
|
|
156
|
+
- 'forward': Forward differentiation using a one-sided window.
|
|
157
|
+
- 'backward': Backward differentiation using a one-sided window.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
v : ndarray
|
|
162
|
+
The computed velocity vector of the 2D trajectory with respect to time.
|
|
163
|
+
The first column represents the x-component of velocity, and the second column represents the y-component.
|
|
164
|
+
|
|
165
|
+
Raises
|
|
166
|
+
------
|
|
167
|
+
AssertionError
|
|
168
|
+
If the window size is not an odd integer and mode is 'bi'.
|
|
169
|
+
|
|
170
|
+
Notes
|
|
171
|
+
-----
|
|
172
|
+
- For 'bi' mode, the window size must be an odd number.
|
|
173
|
+
- For 'forward' mode, the velocity at the edge points may not be accurate due to the one-sided window.
|
|
174
|
+
- For 'backward' mode, the velocity at the first few points may not be accurate due to the one-sided window.
|
|
175
|
+
|
|
176
|
+
Examples
|
|
177
|
+
--------
|
|
178
|
+
>>> import numpy as np
|
|
179
|
+
>>> x = np.array([1, 2, 4, 7, 11])
|
|
180
|
+
>>> y = np.array([0, 3, 5, 8, 10])
|
|
181
|
+
>>> timeline = np.array([0, 1, 2, 3, 4])
|
|
182
|
+
>>> window = 3
|
|
183
|
+
>>> velocity(x, y, timeline, window, mode='bi')
|
|
184
|
+
array([[3., 3.],
|
|
185
|
+
[3., 3.]])
|
|
186
|
+
|
|
187
|
+
>>> velocity(x, y, timeline, window, mode='forward')
|
|
188
|
+
array([[2., 2.],
|
|
189
|
+
[3., 3.]])
|
|
190
|
+
|
|
191
|
+
>>> velocity(x, y, timeline, window, mode='backward')
|
|
192
|
+
array([[3., 3.],
|
|
193
|
+
[3., 3.]])
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
v = np.zeros((len(x),2))
|
|
197
|
+
v[:,:] = np.nan
|
|
198
|
+
|
|
199
|
+
v[:,0] = derivative(x, timeline, window, mode=mode)
|
|
200
|
+
v[:,1] = derivative(y, timeline, window, mode=mode)
|
|
201
|
+
|
|
202
|
+
return v
|
|
203
|
+
|
|
204
|
+
def magnitude_velocity(v_matrix):
|
|
205
|
+
|
|
206
|
+
"""
|
|
207
|
+
Compute the magnitude of velocity vectors given a matrix representing 2D velocity vectors.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
v_matrix : array_like
|
|
212
|
+
The matrix where each row represents a 2D velocity vector with the first column
|
|
213
|
+
being the x-component and the second column being the y-component.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
magnitude : ndarray
|
|
218
|
+
The computed magnitudes of the input velocity vectors.
|
|
219
|
+
|
|
220
|
+
Notes
|
|
221
|
+
-----
|
|
222
|
+
- If a velocity vector has NaN components, the corresponding magnitude will be NaN.
|
|
223
|
+
- The function handles NaN values in the input matrix gracefully.
|
|
224
|
+
|
|
225
|
+
Examples
|
|
226
|
+
--------
|
|
227
|
+
>>> import numpy as np
|
|
228
|
+
>>> v_matrix = np.array([[3, 4],
|
|
229
|
+
... [2, 2],
|
|
230
|
+
... [3, 3]])
|
|
231
|
+
>>> magnitude_velocity(v_matrix)
|
|
232
|
+
array([5., 2.82842712, 4.24264069])
|
|
233
|
+
|
|
234
|
+
>>> v_matrix_with_nan = np.array([[3, 4],
|
|
235
|
+
... [np.nan, 2],
|
|
236
|
+
... [3, np.nan]])
|
|
237
|
+
>>> magnitude_velocity(v_matrix_with_nan)
|
|
238
|
+
array([5., nan, nan])
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
magnitude = np.zeros(len(v_matrix))
|
|
242
|
+
magnitude[:] = np.nan
|
|
243
|
+
for i in range(len(v_matrix)):
|
|
244
|
+
if v_matrix[i,0]==v_matrix[i,0]:
|
|
245
|
+
magnitude[i] = np.sqrt(v_matrix[i,0]**2 + v_matrix[i,1]**2)
|
|
246
|
+
return magnitude
|
|
247
|
+
|
|
248
|
+
def orientation(v_matrix):
|
|
249
|
+
|
|
250
|
+
"""
|
|
251
|
+
Compute the orientation angles (in radians) of 2D velocity vectors given a matrix representing velocity vectors.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
v_matrix : array_like
|
|
256
|
+
The matrix where each row represents a 2D velocity vector with the first column
|
|
257
|
+
being the x-component and the second column being the y-component.
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
orientation_array : ndarray
|
|
262
|
+
The computed orientation angles of the input velocity vectors in radians.
|
|
263
|
+
If a velocity vector has NaN components, the corresponding orientation angle will be NaN.
|
|
264
|
+
|
|
265
|
+
Examples
|
|
266
|
+
--------
|
|
267
|
+
>>> import numpy as np
|
|
268
|
+
>>> v_matrix = np.array([[3, 4],
|
|
269
|
+
... [2, 2],
|
|
270
|
+
... [-3, -3]])
|
|
271
|
+
>>> orientation(v_matrix)
|
|
272
|
+
array([0.92729522, 0.78539816, -2.35619449])
|
|
273
|
+
|
|
274
|
+
>>> v_matrix_with_nan = np.array([[3, 4],
|
|
275
|
+
... [np.nan, 2],
|
|
276
|
+
... [3, np.nan]])
|
|
277
|
+
>>> orientation(v_matrix_with_nan)
|
|
278
|
+
array([0.92729522, nan, nan])
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
orientation_array = np.zeros(len(v_matrix))
|
|
282
|
+
for t in range(len(orientation_array)):
|
|
283
|
+
if v_matrix[t,0]==v_matrix[t,0]:
|
|
284
|
+
orientation_array[t] = np.arctan2(v_matrix[t,0],v_matrix[t,1])
|
|
285
|
+
return orientation_array
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def estimate_unreliable_edge(activation_protocol=[['gauss',2],['std',4]]):
|
|
289
|
+
|
|
290
|
+
"""
|
|
291
|
+
Safely estimate the distance to the edge of an image in which the filtered image values can be artefactual.
|
|
292
|
+
|
|
293
|
+
Parameters
|
|
294
|
+
----------
|
|
295
|
+
activation_protocol : list of list, optional
|
|
296
|
+
A list of lists, where each sublist contains a string naming the filter function, followed by its arguments (usually a kernel size).
|
|
297
|
+
Default is [['gauss', 2], ['std', 4]].
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
int or None
|
|
302
|
+
The sum of the kernel sizes in the activation protocol if the protocol
|
|
303
|
+
is not empty. Returns None if the activation protocol is empty.
|
|
304
|
+
|
|
305
|
+
Notes
|
|
306
|
+
-----
|
|
307
|
+
This function assumes that the second element of each sublist in the
|
|
308
|
+
activation protocol is a kernel size.
|
|
309
|
+
|
|
310
|
+
Examples
|
|
311
|
+
--------
|
|
312
|
+
>>> estimate_unreliable_edge([['gauss', 2], ['std', 4]])
|
|
313
|
+
6
|
|
314
|
+
>>> estimate_unreliable_edge([])
|
|
315
|
+
None
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
if activation_protocol==[]:
|
|
319
|
+
return None
|
|
320
|
+
else:
|
|
321
|
+
edge=0
|
|
322
|
+
for fct in activation_protocol:
|
|
323
|
+
if isinstance(fct[1],(int,np.int_)):
|
|
324
|
+
edge+=fct[1]
|
|
325
|
+
return edge
|
|
326
|
+
|
|
327
|
+
def unpad(img, pad):
|
|
328
|
+
|
|
329
|
+
"""
|
|
330
|
+
Remove padding from an image.
|
|
331
|
+
|
|
332
|
+
This function removes the specified amount of padding from the borders
|
|
333
|
+
of an image. The padding is assumed to be the same on all sides.
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
img : ndarray
|
|
338
|
+
The input image from which the padding will be removed.
|
|
339
|
+
pad : int
|
|
340
|
+
The amount of padding to remove from each side of the image.
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
ndarray
|
|
345
|
+
The image with the padding removed.
|
|
346
|
+
|
|
347
|
+
Raises
|
|
348
|
+
------
|
|
349
|
+
ValueError
|
|
350
|
+
If `pad` is greater than or equal to half of the smallest dimension
|
|
351
|
+
of `img`.
|
|
352
|
+
|
|
353
|
+
See Also
|
|
354
|
+
--------
|
|
355
|
+
numpy.pad : Pads an array.
|
|
356
|
+
|
|
357
|
+
Notes
|
|
358
|
+
-----
|
|
359
|
+
This function assumes that the input image is a 2D array.
|
|
360
|
+
|
|
361
|
+
Examples
|
|
362
|
+
--------
|
|
363
|
+
>>> import numpy as np
|
|
364
|
+
>>> img = np.array([[0, 0, 0, 0, 0],
|
|
365
|
+
... [0, 1, 1, 1, 0],
|
|
366
|
+
... [0, 1, 1, 1, 0],
|
|
367
|
+
... [0, 1, 1, 1, 0],
|
|
368
|
+
... [0, 0, 0, 0, 0]])
|
|
369
|
+
>>> unpad(img, 1)
|
|
370
|
+
array([[1, 1, 1],
|
|
371
|
+
[1, 1, 1],
|
|
372
|
+
[1, 1, 1]])
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
return img[pad:-pad, pad:-pad]
|
|
376
|
+
|
|
377
|
+
def mask_edges(binary_mask, border_size):
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
Mask the edges of a binary mask.
|
|
381
|
+
|
|
382
|
+
This function sets the edges of a binary mask to False, effectively
|
|
383
|
+
masking out a border of the specified size.
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
binary_mask : ndarray
|
|
388
|
+
A 2D binary mask array where the edges will be masked.
|
|
389
|
+
border_size : int
|
|
390
|
+
The size of the border to mask (set to False) on all sides.
|
|
391
|
+
|
|
392
|
+
Returns
|
|
393
|
+
-------
|
|
394
|
+
ndarray
|
|
395
|
+
The binary mask with the edges masked out.
|
|
396
|
+
|
|
397
|
+
Raises
|
|
398
|
+
------
|
|
399
|
+
ValueError
|
|
400
|
+
If `border_size` is greater than or equal to half of the smallest
|
|
401
|
+
dimension of `binary_mask`.
|
|
402
|
+
|
|
403
|
+
Notes
|
|
404
|
+
-----
|
|
405
|
+
This function assumes that the input `binary_mask` is a 2D array. The
|
|
406
|
+
input mask is converted to a boolean array before masking the edges.
|
|
407
|
+
|
|
408
|
+
Examples
|
|
409
|
+
--------
|
|
410
|
+
>>> import numpy as np
|
|
411
|
+
>>> binary_mask = np.array([[1, 1, 1, 1, 1],
|
|
412
|
+
... [1, 1, 1, 1, 1],
|
|
413
|
+
... [1, 1, 1, 1, 1],
|
|
414
|
+
... [1, 1, 1, 1, 1],
|
|
415
|
+
... [1, 1, 1, 1, 1]])
|
|
416
|
+
>>> mask_edges(binary_mask, 1)
|
|
417
|
+
array([[False, False, False, False, False],
|
|
418
|
+
[False, True, True, True, False],
|
|
419
|
+
[False, True, True, True, False],
|
|
420
|
+
[False, True, True, True, False],
|
|
421
|
+
[False, False, False, False, False]])
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
binary_mask = binary_mask.astype(bool)
|
|
425
|
+
binary_mask[:border_size,:] = False
|
|
426
|
+
binary_mask[(binary_mask.shape[0]-border_size):,:] = False
|
|
427
|
+
binary_mask[:,:border_size] = False
|
|
428
|
+
binary_mask[:,(binary_mask.shape[1]-border_size):] = False
|
|
429
|
+
|
|
430
|
+
return binary_mask
|
|
431
|
+
|
|
25
432
|
|
|
26
433
|
def create_patch_mask(h, w, center=None, radius=None):
|
|
27
434
|
|
|
@@ -170,8 +577,6 @@ def rename_intensity_column(df, channels):
|
|
|
170
577
|
new_name = '_'.join(list(measure))
|
|
171
578
|
new_name = new_name.replace('radial_gradient', "radial_intercept")
|
|
172
579
|
to_rename.update({intensity_columns[k]: new_name.replace('-', '_')})
|
|
173
|
-
|
|
174
|
-
|
|
175
580
|
else:
|
|
176
581
|
to_rename = {}
|
|
177
582
|
for k in range(len(intensity_columns)):
|
|
@@ -1163,7 +1568,7 @@ def remove_trajectory_measurements(trajectories, column_labels):
|
|
|
1163
1568
|
tracks = trajectories.copy()
|
|
1164
1569
|
|
|
1165
1570
|
columns_to_keep = [column_labels['track'], column_labels['time'], column_labels['x'], column_labels['y'],column_labels['x']+'_um', column_labels['y']+'_um', 'class_id',
|
|
1166
|
-
't', 'state', 'generation', 'root', 'parent', 'ID', 't0', 'class', 'status', 'class_color', 'status_color', 'class_firstdetection', 't_firstdetection']
|
|
1571
|
+
't', 'state', 'generation', 'root', 'parent', 'ID', 't0', 'class', 'status', 'class_color', 'status_color', 'class_firstdetection', 't_firstdetection', 'velocity']
|
|
1167
1572
|
cols = tracks.columns
|
|
1168
1573
|
for c in columns_to_keep:
|
|
1169
1574
|
if c not in cols:
|
|
@@ -1563,26 +1968,29 @@ def normalize_per_channel(X, normalization_percentile_mode=True, normalization_v
|
|
|
1563
1968
|
assert len(normalization_clipping)==n_channels
|
|
1564
1969
|
assert len(normalization_percentile_mode)==n_channels
|
|
1565
1970
|
|
|
1971
|
+
X_normalized = []
|
|
1566
1972
|
for i in range(len(X)):
|
|
1567
|
-
x = X[i]
|
|
1973
|
+
x = X[i].copy()
|
|
1568
1974
|
loc_i,loc_j,loc_c = np.where(x==0.)
|
|
1569
1975
|
norm_x = np.zeros_like(x, dtype=np.float32)
|
|
1570
1976
|
for k in range(x.shape[-1]):
|
|
1571
|
-
chan = x[:,:,k]
|
|
1977
|
+
chan = x[:,:,k].copy()
|
|
1572
1978
|
if not np.all(chan.flatten()==0):
|
|
1573
1979
|
if normalization_percentile_mode[k]:
|
|
1574
|
-
min_val = np.
|
|
1575
|
-
max_val = np.
|
|
1980
|
+
min_val = np.nanpercentile(chan[chan!=0.].flatten(), normalization_values[k][0])
|
|
1981
|
+
max_val = np.nanpercentile(chan[chan!=0.].flatten(), normalization_values[k][1])
|
|
1576
1982
|
else:
|
|
1577
1983
|
min_val = normalization_values[k][0]
|
|
1578
1984
|
max_val = normalization_values[k][1]
|
|
1579
1985
|
|
|
1580
1986
|
clip_option = normalization_clipping[k]
|
|
1581
|
-
norm_x[:,:,k] = normalize_mi_ma(chan.astype(np.float32), min_val, max_val, clip=clip_option, eps=1e-20, dtype=np.float32)
|
|
1582
|
-
|
|
1583
|
-
|
|
1987
|
+
norm_x[:,:,k] = normalize_mi_ma(chan.astype(np.float32).copy(), min_val, max_val, clip=clip_option, eps=1e-20, dtype=np.float32)
|
|
1988
|
+
else:
|
|
1989
|
+
norm_x[:,:,k] = 0.
|
|
1990
|
+
norm_x[loc_i,loc_j,loc_c] = 0.
|
|
1991
|
+
X_normalized.append(norm_x.copy())
|
|
1584
1992
|
|
|
1585
|
-
return
|
|
1993
|
+
return X_normalized
|
|
1586
1994
|
|
|
1587
1995
|
def load_image_dataset(datasets, channels, train_spatial_calibration=None, mask_suffix='labelled'):
|
|
1588
1996
|
|
|
@@ -1688,8 +2096,7 @@ def load_image_dataset(datasets, channels, train_spatial_calibration=None, mask_
|
|
|
1688
2096
|
|
|
1689
2097
|
if im_calib != train_spatial_calibration:
|
|
1690
2098
|
factor = im_calib / train_spatial_calibration
|
|
1691
|
-
|
|
1692
|
-
image = zoom(image, [factor,factor,1], order=3)
|
|
2099
|
+
image = np.moveaxis([zoom(image[:,:,c].astype(float).copy(), [factor,factor], order=3, prefilter=False) for c in range(image.shape[-1])],0,-1) #zoom(image, [factor,factor,1], order=3)
|
|
1693
2100
|
mask = zoom(mask, [factor,factor], order=0)
|
|
1694
2101
|
|
|
1695
2102
|
X.append(image)
|
|
@@ -1807,17 +2214,24 @@ def download_zenodo_file(file, output_dir):
|
|
|
1807
2214
|
|
|
1808
2215
|
os.remove(path_to_zip_file)
|
|
1809
2216
|
|
|
1810
|
-
def interpolate_nan(
|
|
2217
|
+
def interpolate_nan(img, method='nearest'):
|
|
1811
2218
|
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2219
|
+
"""
|
|
2220
|
+
Interpolate NaN on single channel array 2D
|
|
2221
|
+
"""
|
|
2222
|
+
|
|
2223
|
+
if np.all(img==0):
|
|
2224
|
+
return img
|
|
2225
|
+
|
|
2226
|
+
if np.any(img.flatten()!=img.flatten()):
|
|
2227
|
+
# then need to interpolate
|
|
2228
|
+
x_grid, y_grid = np.meshgrid(np.arange(img.shape[1]),np.arange(img.shape[0]))
|
|
2229
|
+
mask = [~np.isnan(img)][0]
|
|
2230
|
+
x = x_grid[mask].reshape(-1)
|
|
2231
|
+
y = y_grid[mask].reshape(-1)
|
|
2232
|
+
points = np.array([x,y]).T
|
|
2233
|
+
values = img[mask].reshape(-1)
|
|
2234
|
+
interp_grid = griddata(points, values, (x_grid, y_grid), method=method)
|
|
2235
|
+
return interp_grid
|
|
2236
|
+
else:
|
|
2237
|
+
return img
|
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
celldetective/__init__.py,sha256=FEZpJKcskBH2IginYzeqPWoR1lVGuyYCXhv7Hnlkoo8,49
|
|
2
|
-
celldetective/__main__.py,sha256=
|
|
2
|
+
celldetective/__main__.py,sha256=XFAkq_2cBEkWAVXDGSNFagoQBglyl0Y-GOO3KFc8UqM,13888
|
|
3
3
|
celldetective/events.py,sha256=s2pWnR3Z1fcB15sET5WsF2Pi6v6qv16hks_m3WiklNs,3658
|
|
4
|
-
celldetective/extra_properties.py,sha256=
|
|
5
|
-
celldetective/filters.py,sha256=
|
|
6
|
-
celldetective/io.py,sha256=
|
|
7
|
-
celldetective/measure.py,sha256=
|
|
8
|
-
celldetective/neighborhood.py,sha256=
|
|
9
|
-
celldetective/preprocessing.py,sha256=
|
|
10
|
-
celldetective/segmentation.py,sha256=
|
|
11
|
-
celldetective/signals.py,sha256=
|
|
12
|
-
celldetective/tracking.py,sha256=
|
|
13
|
-
celldetective/utils.py,sha256=
|
|
4
|
+
celldetective/extra_properties.py,sha256=ZdWV046RR8jrQPYxLSVVb1cXkWYxfrrLj2tXfc358cU,4179
|
|
5
|
+
celldetective/filters.py,sha256=b0qKwHor1fvNA_dHovP17nQz8EsW5YlyhT2TJnayn08,3615
|
|
6
|
+
celldetective/io.py,sha256=ptaX4GadWuf0zOQ3ZWSLCzihEID-YAalPfxW1nqvgIU,80889
|
|
7
|
+
celldetective/measure.py,sha256=HDQZfSRx3daOCV5Snu1paYU5JYkwu8engO2qZqhTAUo,48089
|
|
8
|
+
celldetective/neighborhood.py,sha256=QCuhesMHGyr3c3ys9wWcNR1HM6CHdHe51R8upoolgPw,49514
|
|
9
|
+
celldetective/preprocessing.py,sha256=psCs4CAI7gG3YlKvxkXKnpClFL4SjWm5TToXq2ZwL-s,37137
|
|
10
|
+
celldetective/segmentation.py,sha256=Hu4-lOJ4UhPw2o0Hn_NPOCHsa6Iwsw5A3PR6X3Tzbb8,29196
|
|
11
|
+
celldetective/signals.py,sha256=P7eiDZGGIAYCFBKjGCBi8gMBvJYywxlxZNzyGgw-26Y,102783
|
|
12
|
+
celldetective/tracking.py,sha256=A0mhdF4uq4m8OX1-rhtuhG69rlh_6Pb34Aebu7hIeKM,37601
|
|
13
|
+
celldetective/utils.py,sha256=00jBr_Ur6wX3xeFQzwgpFv9XDhnt_VCh-e2usFdrfo4,77543
|
|
14
14
|
celldetective/datasets/segmentation_annotations/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
celldetective/datasets/signal_annotations/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
celldetective/gui/__init__.py,sha256=y2dvrUdJi17QMgPjl8WN3XFHYzJpu2ul4_8y7MQV1Bk,941
|
|
17
17
|
celldetective/gui/about.py,sha256=i-y54Opb10pKTVNUEcJC-D6Cbiqud2EJ3ZLayXqhdqc,1715
|
|
18
18
|
celldetective/gui/analyze_block.py,sha256=WD3JQQylx_dVozFCvNqrOyR6LcNHV7R1_gGh7XqOVeI,25423
|
|
19
19
|
celldetective/gui/btrack_options.py,sha256=eQEf63yTUsPCN-d1LqgAMmUQpfv2FH85FqnOSaR-9Q8,35575
|
|
20
|
-
celldetective/gui/classifier_widget.py,sha256=
|
|
20
|
+
celldetective/gui/classifier_widget.py,sha256=52a3kOfU3uMEzUYMIOHGqiy-Q5itMEvcG-3JYrO34I8,16918
|
|
21
21
|
celldetective/gui/configure_new_exp.py,sha256=ANJ-Zn4sjBphtj_aoJu6m1PFEKyv9gxeh9XqS6xOGjk,18969
|
|
22
|
-
celldetective/gui/control_panel.py,sha256=
|
|
22
|
+
celldetective/gui/control_panel.py,sha256=wcDqe4XaDJRMmPmWKJpxd0D9V4_DrdRGnEH6D7B_IK0,17557
|
|
23
23
|
celldetective/gui/gui_utils.py,sha256=PidFfdc8XASeIzZO5pfKgwqe4vROG7-KpYMcBZ42jdw,22673
|
|
24
24
|
celldetective/gui/json_readers.py,sha256=fTrNrlxv9NCae8ZJexBEHxI3yCLRqt6F0Yo1OeDycfA,3686
|
|
25
|
-
celldetective/gui/layouts.py,sha256=
|
|
26
|
-
celldetective/gui/measurement_options.py,sha256=
|
|
25
|
+
celldetective/gui/layouts.py,sha256=gC24lyA48hzOo2oabYF1vXTFh5r7WcQFVkqjAXXo5g0,26997
|
|
26
|
+
celldetective/gui/measurement_options.py,sha256=i0CdAfupHJAqhOT7RsufEK919sAzQnFBkQO4IAMYZL0,47704
|
|
27
27
|
celldetective/gui/neighborhood_options.py,sha256=sdKxVRliZtuKSpcPfnFxqkW4V8rN2tzjhDxOPVmElyE,20191
|
|
28
28
|
celldetective/gui/plot_measurements.py,sha256=xUoGxV6uXcen-t4yWtAmcGTUayICI-FxTVKPrWMNlfg,51799
|
|
29
|
-
celldetective/gui/plot_signals_ui.py,sha256=
|
|
30
|
-
celldetective/gui/process_block.py,sha256=
|
|
29
|
+
celldetective/gui/plot_signals_ui.py,sha256=TwWU2u3_mkRNsM8er0kI_kwr5EoZ29YEzlr0cQzyW4A,43732
|
|
30
|
+
celldetective/gui/process_block.py,sha256=7n9glZ1ojEi1bObqwIj4giNhrteT69X1EPMQ1hK63aU,53565
|
|
31
31
|
celldetective/gui/retrain_segmentation_model_options.py,sha256=-rkuUzI_vFFlZC3LAAYEELoJUKcz6PmkpCrxKZindhg,27218
|
|
32
32
|
celldetective/gui/retrain_signal_model_options.py,sha256=uHZy3FGsGMHfZL_nYnuFiXF57XaAMVzjYxVF2OXhYnY,24184
|
|
33
33
|
celldetective/gui/seg_model_loader.py,sha256=uKp8oab-4QdTGqb-tb6bOD-FLD_154GOvgWFYz97BwY,17350
|
|
34
34
|
celldetective/gui/signal_annotator.py,sha256=4ymMpo_GjSBsJSRkyNKrWRLy0EFXHINbFtp9ykDqfGE,84864
|
|
35
35
|
celldetective/gui/signal_annotator_options.py,sha256=-Q7f8eCwniqbgLJqMCa91Wc-V3VHAZidwt7LPd4Z5yU,10879
|
|
36
36
|
celldetective/gui/styles.py,sha256=Vw4wr6MQ4iBwhOY-ZWAxFDZZ3CNohmEnuPPazwhJaho,4129
|
|
37
|
-
celldetective/gui/survival_ui.py,sha256=
|
|
38
|
-
celldetective/gui/tableUI.py,sha256=
|
|
39
|
-
celldetective/gui/thresholds_gui.py,sha256=
|
|
40
|
-
celldetective/gui/viewers.py,sha256=
|
|
37
|
+
celldetective/gui/survival_ui.py,sha256=2JGLC5m6D_gVLwnBAM7uEvuCKw1Cli8nM9i5s7TIpGg,33612
|
|
38
|
+
celldetective/gui/tableUI.py,sha256=QRYlXc7751Ka5qbUHSGbE_nXTiiezAVfEWXiRKRmuRM,32188
|
|
39
|
+
celldetective/gui/thresholds_gui.py,sha256=b8SkG4DlfxBRjacKTe1NSNkq7rJm8lnSLifH-mg846k,49529
|
|
40
|
+
celldetective/gui/viewers.py,sha256=G8uNb0U_4tJiZkcAWX9BbCSBIUbF4tjedZD-5o4WKxY,27734
|
|
41
41
|
celldetective/icons/logo-large.png,sha256=FXSwV3u6zEKcfpuSn4unnqB0oUnN9cHqQ9BCKWytrpg,36631
|
|
42
42
|
celldetective/icons/logo.png,sha256=wV2OS8_dU5Td5cgdPbCOU3JpMpTwNuYLnfVcnQX0tJA,2437
|
|
43
43
|
celldetective/icons/signals_icon.png,sha256=vEiKoqWTtN0-uJgVqtAlwCuP-f4QeWYOlO3sdp2tg2w,3969
|
|
@@ -49,6 +49,9 @@ celldetective/icons/vignette_signals2.png,sha256=hsVOdQDpEfMGM45aaSeacEm3lvxbquR
|
|
|
49
49
|
celldetective/icons/vignette_signals2.svg,sha256=muGNcQudV1jG-bmFd9FwV-Wb8PcrRV5osdZ7pHR7Ekk,5947
|
|
50
50
|
celldetective/links/zenodo.json,sha256=7WKRuZY7MHTR-IChWBbU0i47H_479NtlxsCGaJn9-xM,22728
|
|
51
51
|
celldetective/models/segmentation_effectors/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
celldetective/models/segmentation_effectors/primNK_cfse/config_input.json,sha256=gwEGP5wc6iVcwLrR5IbEb-9lTbdQuPyLLFYXh00374U,509
|
|
53
|
+
celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer,sha256=ps4BAzGxr2i2R0uBGJqfF9wjg5bIeSeBGQb0YcUChhY,26559970
|
|
54
|
+
celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json,sha256=DqEO4_oQuQmJkZiefREXz8ts60svoqxIQB-LMScuCnc,909
|
|
52
55
|
celldetective/models/segmentation_generic/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
56
|
celldetective/models/segmentation_targets/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
57
|
celldetective/models/signal_detection/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -57,10 +60,10 @@ celldetective/models/tracking_configs/ricm.json,sha256=L-vmwCR1f89U-qnH2Ms0cBfPF
|
|
|
57
60
|
celldetective/models/tracking_configs/ricm2.json,sha256=DDjJ6ScYcDWvlsy7ujPID8v8H28vcNcMuZmNR8XmGxo,2718
|
|
58
61
|
celldetective/scripts/analyze_signals.py,sha256=23TXGNw-j5xT3ss4mXlnKdBgFLnQ50JUEQOC6_H7Q_0,2203
|
|
59
62
|
celldetective/scripts/measure_cells.py,sha256=4uRG6Dg0WsO-N8ZaBJ4loWOvX6FdHaCblIFXq6Dtirc,11000
|
|
60
|
-
celldetective/scripts/segment_cells.py,sha256=
|
|
63
|
+
celldetective/scripts/segment_cells.py,sha256=OSP52sPPHyhfXEjiXXdezkDY4MiFXLBWTb2_umRBD-M,8269
|
|
61
64
|
celldetective/scripts/segment_cells_thresholds.py,sha256=GbWXa6xoO8s4PinJPZIxAuosw4vpzyJ7FiFYpSURojk,4998
|
|
62
65
|
celldetective/scripts/track_cells.py,sha256=AaNiYEW4osYKKR2kbdVLOUnQEBbcZIA-D0mkhcxPWTY,7985
|
|
63
|
-
celldetective/scripts/train_segmentation_model.py,sha256=
|
|
66
|
+
celldetective/scripts/train_segmentation_model.py,sha256=UY493QK7_FhS9uHYl2eeEYx7t0kw1jhvc0YMY12YZpI,8614
|
|
64
67
|
celldetective/scripts/train_signal_model.py,sha256=9-dmPCLKJ9ypjsV9AwFd-Sb6B6YaHS0QGT218H5hUPo,1861
|
|
65
68
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
69
|
tests/test_events.py,sha256=eLFwwEEJfQAdwhews3-fn1HSvzozcNNFN_Qn0gOvQkE,685
|
|
@@ -68,13 +71,14 @@ tests/test_filters.py,sha256=iJksl_HgquqGzPPv46qpNtlD4rkBpZ5eVtIotgZ7LDs,656
|
|
|
68
71
|
tests/test_io.py,sha256=gk5FmoI7ANEczUtNXYRxc48KzkfYzemwS_eYaLq4_NI,2093
|
|
69
72
|
tests/test_measure.py,sha256=FEUAs1rVHylvIvubCb0bJDNGZLVmkgXNgI3NaGQ1dA8,4542
|
|
70
73
|
tests/test_neighborhood.py,sha256=gk5FmoI7ANEczUtNXYRxc48KzkfYzemwS_eYaLq4_NI,2093
|
|
74
|
+
tests/test_preprocessing.py,sha256=FI-Wk-kc4wWmOQg_NLCUIZC1oti396wr5cC-BauBai0,1436
|
|
71
75
|
tests/test_segmentation.py,sha256=-3b7o_fUVMYxfVwX5VHFqRF0dDXObSTtylf5XQGcq1A,3493
|
|
72
76
|
tests/test_signals.py,sha256=No4cah6KxplhDcKXnU8RrA7eDla4hWw6ccf7xGnBokU,3599
|
|
73
77
|
tests/test_tracking.py,sha256=8hebWSqEIuttD1ABn-6dKCT7EXKRR7-4RwyFWi1WPFo,8800
|
|
74
|
-
tests/test_utils.py,sha256=
|
|
75
|
-
celldetective-1.1.
|
|
76
|
-
celldetective-1.1.
|
|
77
|
-
celldetective-1.1.
|
|
78
|
-
celldetective-1.1.
|
|
79
|
-
celldetective-1.1.
|
|
80
|
-
celldetective-1.1.
|
|
78
|
+
tests/test_utils.py,sha256=NKRCAC1d89aBK5cWjTb7-pInYow901RrT-uBlIdz4KI,3692
|
|
79
|
+
celldetective-1.1.1.post1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
80
|
+
celldetective-1.1.1.post1.dist-info/METADATA,sha256=MItn6uMJhqWHbN0urj8068yO8xcUoc9c3kUsYTVre10,12418
|
|
81
|
+
celldetective-1.1.1.post1.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
|
|
82
|
+
celldetective-1.1.1.post1.dist-info/entry_points.txt,sha256=2NU6_EOByvPxqBbCvjwxlVlvnQreqZ3BKRCVIKEv3dg,62
|
|
83
|
+
celldetective-1.1.1.post1.dist-info/top_level.txt,sha256=6rsIKKfGMKgud7HPuATcpq6EhdXwcg_yknBVWn9x4C4,20
|
|
84
|
+
celldetective-1.1.1.post1.dist-info/RECORD,,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import numpy as np
|
|
4
|
+
import os
|
|
5
|
+
from celldetective.preprocessing import fit_background_model, field_correction
|
|
6
|
+
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
|
|
9
|
+
class TestFitPlane(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def setUpClass(self):
|
|
13
|
+
a = 5.
|
|
14
|
+
self.img = np.full((100,100),5.0)
|
|
15
|
+
self.img_with_cell = self.img.copy()
|
|
16
|
+
self.img_with_cell[:10,:10] = 25.0
|
|
17
|
+
|
|
18
|
+
def test_plane_is_well_fit(self):
|
|
19
|
+
mat = np.array(fit_background_model(self.img, cell_masks=None, model='plane', edge_exclusion=None))
|
|
20
|
+
self.assertTrue(np.allclose(self.img, mat))
|
|
21
|
+
|
|
22
|
+
def test_plane_is_well_fit_and_applied_with_division(self):
|
|
23
|
+
result = field_correction(self.img, threshold_on_std=1.0E05, operation='divide', model='plane', clip=False, return_bg=False, activation_protocol=[])
|
|
24
|
+
self.assertTrue(np.allclose(result, np.full((100,100), 1.0)))
|
|
25
|
+
|
|
26
|
+
def test_plane_is_well_fit_and_applied_with_subtraction(self):
|
|
27
|
+
result = field_correction(self.img, threshold_on_std=1.0E05, operation='subtract', model='plane', clip=False, return_bg=False, activation_protocol=[])
|
|
28
|
+
self.assertTrue(np.allclose(result, np.zeros((100,100))))
|
|
29
|
+
|
|
30
|
+
def test_plane_is_well_fit_with_cell(self):
|
|
31
|
+
cell_masks = np.zeros_like(self.img)
|
|
32
|
+
cell_masks[:10,:10] = 1.0
|
|
33
|
+
mat = np.array(fit_background_model(self.img, cell_masks=cell_masks, model='plane', edge_exclusion=None))
|
|
34
|
+
self.assertTrue(np.allclose(self.img, mat))
|
|
35
|
+
|
|
36
|
+
if __name__=="__main__":
|
|
37
|
+
unittest.main()
|