celldetective 1.1.0__py3-none-any.whl → 1.1.1__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 +62 -52
- celldetective/gui/control_panel.py +5 -0
- celldetective/gui/layouts.py +1 -0
- 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 +280 -28
- celldetective/gui/thresholds_gui.py +7 -5
- celldetective/gui/viewers.py +169 -22
- celldetective/io.py +14 -5
- 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 +15 -4
- celldetective/scripts/train_segmentation_model.py +35 -34
- celldetective/segmentation.py +14 -9
- celldetective/signals.py +13 -231
- celldetective/tracking.py +2 -1
- celldetective/utils.py +437 -26
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/METADATA +1 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/RECORD +32 -28
- tests/test_preprocessing.py +37 -0
- tests/test_utils.py +48 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +0 -0
|
@@ -22,6 +22,9 @@ from art import tprint
|
|
|
22
22
|
from scipy.ndimage import zoom
|
|
23
23
|
import threading
|
|
24
24
|
|
|
25
|
+
import matplotlib.pyplot as plt
|
|
26
|
+
import time
|
|
27
|
+
|
|
25
28
|
tprint("Segment")
|
|
26
29
|
|
|
27
30
|
parser = argparse.ArgumentParser(description="Segment a movie in position with the selected model",
|
|
@@ -159,10 +162,18 @@ def segment_index(indices):
|
|
|
159
162
|
for t in tqdm(indices,desc="frame"):
|
|
160
163
|
|
|
161
164
|
# Load channels at time t
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
values = []
|
|
166
|
+
percentiles = []
|
|
167
|
+
for k in range(len(normalization_percentile)):
|
|
168
|
+
if normalization_percentile[k]:
|
|
169
|
+
percentiles.append(normalization_values[k])
|
|
170
|
+
values.append(None)
|
|
171
|
+
else:
|
|
172
|
+
percentiles.append(None)
|
|
173
|
+
values.append(normalization_values[k])
|
|
174
|
+
|
|
175
|
+
f = load_frames(img_num_channels[:,t], file, scale=scale, normalize_input=True, normalize_kwargs={"percentiles": percentiles, 'values': values, 'clip': normalization_clip})
|
|
176
|
+
|
|
166
177
|
if np.any(img_num_channels[:,t]==-1):
|
|
167
178
|
f[:,:,np.where(img_num_channels[:,t]==-1)[0]] = 0.
|
|
168
179
|
|
|
@@ -11,28 +11,15 @@ from tqdm import tqdm
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
import random
|
|
13
13
|
|
|
14
|
-
from celldetective.utils import load_image_dataset, normalize_per_channel, augmenter
|
|
14
|
+
from celldetective.utils import load_image_dataset, normalize_per_channel, augmenter, interpolate_nan
|
|
15
|
+
from celldetective.io import normalize_multichannel
|
|
15
16
|
from stardist import fill_label_holes
|
|
16
17
|
from art import tprint
|
|
17
18
|
import matplotlib.pyplot as plt
|
|
18
19
|
|
|
19
|
-
def interpolate_nan(array_like):
|
|
20
|
-
array = array_like.copy()
|
|
21
|
-
|
|
22
|
-
isnan_array = ~np.isnan(array)
|
|
23
|
-
|
|
24
|
-
xp = isnan_array.ravel().nonzero()[0]
|
|
25
|
-
|
|
26
|
-
fp = array[~np.isnan(array)]
|
|
27
|
-
x = np.isnan(array).ravel().nonzero()[0]
|
|
28
|
-
|
|
29
|
-
array[np.isnan(array)] = np.interp(x, xp, fp)
|
|
30
|
-
|
|
31
|
-
return array
|
|
32
20
|
|
|
33
21
|
tprint("Train")
|
|
34
22
|
|
|
35
|
-
|
|
36
23
|
parser = argparse.ArgumentParser(description="Train a signal model from instructions.",
|
|
37
24
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
38
25
|
parser.add_argument('-c',"--config", required=True,help="Training instructions")
|
|
@@ -78,25 +65,39 @@ X,Y = load_image_dataset(datasets, target_channels, train_spatial_calibration=sp
|
|
|
78
65
|
print('Dataset loaded...')
|
|
79
66
|
|
|
80
67
|
# Normalize images
|
|
81
|
-
X = normalize_per_channel(X,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
68
|
+
# X = normalize_per_channel(X,
|
|
69
|
+
# normalization_percentile_mode=normalization_percentile,
|
|
70
|
+
# normalization_values=normalization_values,
|
|
71
|
+
# normalization_clipping=normalization_clip
|
|
72
|
+
# )
|
|
73
|
+
|
|
74
|
+
values = []
|
|
75
|
+
percentiles = []
|
|
76
|
+
for k in range(len(normalization_percentile)):
|
|
77
|
+
if normalization_percentile[k]:
|
|
78
|
+
percentiles.append(normalization_values[k])
|
|
79
|
+
values.append(None)
|
|
80
|
+
else:
|
|
81
|
+
percentiles.append(None)
|
|
82
|
+
values.append(normalization_values[k])
|
|
83
|
+
|
|
84
|
+
X = [normalize_multichannel(x, **{"percentiles": percentiles, 'values': values, 'clip': normalization_clip}) for x in X]
|
|
85
|
+
|
|
86
|
+
for k in range(len(X)):
|
|
87
|
+
x = X[k].copy()
|
|
88
|
+
x_interp = np.moveaxis([interpolate_nan(x[:,:,c].copy()) for c in range(x.shape[-1])],0,-1)
|
|
89
|
+
X[k] = x_interp
|
|
90
|
+
|
|
91
|
+
# for x in X[:10]:
|
|
92
|
+
# plt.imshow(x[:,:,0])
|
|
93
|
+
# plt.colorbar()
|
|
94
|
+
# plt.pause(2)
|
|
95
|
+
# plt.close()
|
|
96
|
+
|
|
97
|
+
# plt.imshow(x[:,:,1])
|
|
98
|
+
# plt.colorbar()
|
|
99
|
+
# plt.pause(2)
|
|
100
|
+
# plt.close()
|
|
100
101
|
|
|
101
102
|
Y = [fill_label_holes(y) for y in tqdm(Y)]
|
|
102
103
|
|
celldetective/segmentation.py
CHANGED
|
@@ -13,7 +13,8 @@ from cellpose.models import CellposeModel
|
|
|
13
13
|
from skimage.transform import resize
|
|
14
14
|
from celldetective.io import _view_on_napari, locate_labels, locate_stack, _view_on_napari
|
|
15
15
|
from celldetective.filters import * #rework this to give a name
|
|
16
|
-
from celldetective.utils import rename_intensity_column
|
|
16
|
+
from celldetective.utils import rename_intensity_column, mask_edges, estimate_unreliable_edge
|
|
17
|
+
from stardist import fill_label_holes
|
|
17
18
|
import scipy.ndimage as ndi
|
|
18
19
|
from skimage.segmentation import watershed
|
|
19
20
|
from skimage.feature import peak_local_max
|
|
@@ -132,7 +133,7 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
|
|
|
132
133
|
frame = normalize_multichannel(frame, values=normalization_values)
|
|
133
134
|
|
|
134
135
|
if scale is not None:
|
|
135
|
-
frame = ndi.zoom(frame, [scale,scale
|
|
136
|
+
frame = [ndi.zoom(frame[:,:,c].copy(), [scale,scale], order=3, prefilter=False) for c in range(frame.shape[-1])]
|
|
136
137
|
|
|
137
138
|
if model_type=="stardist":
|
|
138
139
|
|
|
@@ -273,7 +274,8 @@ def segment_frame_from_thresholds(frame, target_channel=0, thresholds=None, equa
|
|
|
273
274
|
img = match_histograms(img, equalize_reference)
|
|
274
275
|
img_mc = frame.copy()
|
|
275
276
|
img = filter_image(img, filters=filters)
|
|
276
|
-
|
|
277
|
+
edge = estimate_unreliable_edge(filters)
|
|
278
|
+
binary_image = threshold_image(img, thresholds[0], thresholds[1], fill_holes=True, edge_exclusion=edge)
|
|
277
279
|
coords,distance = identify_markers_from_binary(binary_image, marker_min_distance, footprint_size=marker_footprint_size, footprint=marker_footprint, return_edt=True)
|
|
278
280
|
instance_seg = apply_watershed(binary_image, coords, distance)
|
|
279
281
|
instance_seg = filter_on_property(instance_seg, intensity_image=img_mc, queries=feature_queries, channel_names=channel_names)
|
|
@@ -357,7 +359,7 @@ def filter_on_property(labels, intensity_image=None, queries=None, channel_names
|
|
|
357
359
|
return labels
|
|
358
360
|
|
|
359
361
|
|
|
360
|
-
def apply_watershed(binary_image, coords, distance):
|
|
362
|
+
def apply_watershed(binary_image, coords, distance, fill_holes=True):
|
|
361
363
|
|
|
362
364
|
"""
|
|
363
365
|
Applies the watershed algorithm to segment objects in a binary image using given markers and distance map.
|
|
@@ -404,7 +406,8 @@ def apply_watershed(binary_image, coords, distance):
|
|
|
404
406
|
mask[tuple(coords.T)] = True
|
|
405
407
|
markers, _ = ndi.label(mask)
|
|
406
408
|
labels = watershed(-distance, markers, mask=binary_image)
|
|
407
|
-
|
|
409
|
+
if fill_holes:
|
|
410
|
+
labels = fill_label_holes(labels)
|
|
408
411
|
return labels
|
|
409
412
|
|
|
410
413
|
def identify_markers_from_binary(binary_image, min_distance, footprint_size=20, footprint=None, return_edt=False):
|
|
@@ -457,7 +460,7 @@ def identify_markers_from_binary(binary_image, min_distance, footprint_size=20,
|
|
|
457
460
|
return coords
|
|
458
461
|
|
|
459
462
|
|
|
460
|
-
def threshold_image(img, min_threshold, max_threshold, foreground_value=255., fill_holes=True):
|
|
463
|
+
def threshold_image(img, min_threshold, max_threshold, foreground_value=255., fill_holes=True, edge_exclusion=None):
|
|
461
464
|
|
|
462
465
|
"""
|
|
463
466
|
|
|
@@ -496,10 +499,12 @@ def threshold_image(img, min_threshold, max_threshold, foreground_value=255., fi
|
|
|
496
499
|
|
|
497
500
|
"""
|
|
498
501
|
|
|
499
|
-
|
|
500
|
-
binary = (img>=min_threshold)*(img<=max_threshold) * foreground_value
|
|
502
|
+
binary = np.zeros_like(img).astype(bool)
|
|
503
|
+
binary[img==img] = (img[img==img]>=min_threshold)*(img[img==img]<=max_threshold) * foreground_value
|
|
504
|
+
if isinstance(edge_exclusion, (int,np.int_)):
|
|
505
|
+
binary = mask_edges(binary, edge_exclusion)
|
|
501
506
|
if fill_holes:
|
|
502
|
-
binary = ndi.binary_fill_holes(binary)
|
|
507
|
+
binary = ndi.binary_fill_holes(binary.astype(int))
|
|
503
508
|
return binary
|
|
504
509
|
|
|
505
510
|
def filter_image(img, filters=None):
|
celldetective/signals.py
CHANGED
|
@@ -2258,231 +2258,6 @@ def train_signal_model(config):
|
|
|
2258
2258
|
cmd = f'python "{script_path}" --config "{config}"'
|
|
2259
2259
|
subprocess.call(cmd, shell=True)
|
|
2260
2260
|
|
|
2261
|
-
def derivative(x, timeline, window, mode='bi'):
|
|
2262
|
-
|
|
2263
|
-
"""
|
|
2264
|
-
Compute the derivative of a given array of values with respect to time using a specified numerical differentiation method.
|
|
2265
|
-
|
|
2266
|
-
Parameters
|
|
2267
|
-
----------
|
|
2268
|
-
x : array_like
|
|
2269
|
-
The input array of values.
|
|
2270
|
-
timeline : array_like
|
|
2271
|
-
The array representing the time points corresponding to the input values.
|
|
2272
|
-
window : int
|
|
2273
|
-
The size of the window used for numerical differentiation. Must be a positive odd integer.
|
|
2274
|
-
mode : {'bi', 'forward', 'backward'}, optional
|
|
2275
|
-
The numerical differentiation method to be used:
|
|
2276
|
-
- 'bi' (default): Bidirectional differentiation using a symmetric window.
|
|
2277
|
-
- 'forward': Forward differentiation using a one-sided window.
|
|
2278
|
-
- 'backward': Backward differentiation using a one-sided window.
|
|
2279
|
-
|
|
2280
|
-
Returns
|
|
2281
|
-
-------
|
|
2282
|
-
dxdt : ndarray
|
|
2283
|
-
The computed derivative values of the input array with respect to time.
|
|
2284
|
-
|
|
2285
|
-
Raises
|
|
2286
|
-
------
|
|
2287
|
-
AssertionError
|
|
2288
|
-
If the window size is not an odd integer and mode is 'bi'.
|
|
2289
|
-
|
|
2290
|
-
Notes
|
|
2291
|
-
-----
|
|
2292
|
-
- For 'bi' mode, the window size must be an odd number.
|
|
2293
|
-
- For 'forward' mode, the derivative at the edge points may not be accurate due to the one-sided window.
|
|
2294
|
-
- For 'backward' mode, the derivative at the first few points may not be accurate due to the one-sided window.
|
|
2295
|
-
|
|
2296
|
-
Examples
|
|
2297
|
-
--------
|
|
2298
|
-
>>> import numpy as np
|
|
2299
|
-
>>> x = np.array([1, 2, 4, 7, 11])
|
|
2300
|
-
>>> timeline = np.array([0, 1, 2, 3, 4])
|
|
2301
|
-
>>> window = 3
|
|
2302
|
-
>>> derivative(x, timeline, window, mode='bi')
|
|
2303
|
-
array([3., 3., 3.])
|
|
2304
|
-
|
|
2305
|
-
>>> derivative(x, timeline, window, mode='forward')
|
|
2306
|
-
array([1., 2., 3.])
|
|
2307
|
-
|
|
2308
|
-
>>> derivative(x, timeline, window, mode='backward')
|
|
2309
|
-
array([3., 3., 3., 3.])
|
|
2310
|
-
"""
|
|
2311
|
-
|
|
2312
|
-
# modes = bi, forward, backward
|
|
2313
|
-
dxdt = np.zeros(len(x))
|
|
2314
|
-
dxdt[:] = np.nan
|
|
2315
|
-
|
|
2316
|
-
if mode=='bi':
|
|
2317
|
-
assert window%2==1,'Please set an odd window for the bidirectional mode'
|
|
2318
|
-
lower_bound = window//2
|
|
2319
|
-
upper_bound = len(x) - window//2 - 1
|
|
2320
|
-
elif mode=='forward':
|
|
2321
|
-
lower_bound = 0
|
|
2322
|
-
upper_bound = len(x) - window
|
|
2323
|
-
elif mode=='backward':
|
|
2324
|
-
lower_bound = window
|
|
2325
|
-
upper_bound = len(x)
|
|
2326
|
-
|
|
2327
|
-
for t in range(lower_bound,upper_bound):
|
|
2328
|
-
if mode=='bi':
|
|
2329
|
-
dxdt[t] = (x[t+window//2+1] - x[t-window//2]) / (timeline[t+window//2+1] - timeline[t-window//2])
|
|
2330
|
-
elif mode=='forward':
|
|
2331
|
-
dxdt[t] = (x[t+window] - x[t]) / (timeline[t+window] - timeline[t])
|
|
2332
|
-
elif mode=='backward':
|
|
2333
|
-
dxdt[t] = (x[t] - x[t-window]) / (timeline[t] - timeline[t-window])
|
|
2334
|
-
return dxdt
|
|
2335
|
-
|
|
2336
|
-
def velocity(x,y,timeline,window,mode='bi'):
|
|
2337
|
-
|
|
2338
|
-
"""
|
|
2339
|
-
Compute the velocity vector of a given 2D trajectory represented by arrays of x and y coordinates
|
|
2340
|
-
with respect to time using a specified numerical differentiation method.
|
|
2341
|
-
|
|
2342
|
-
Parameters
|
|
2343
|
-
----------
|
|
2344
|
-
x : array_like
|
|
2345
|
-
The array of x-coordinates of the trajectory.
|
|
2346
|
-
y : array_like
|
|
2347
|
-
The array of y-coordinates of the trajectory.
|
|
2348
|
-
timeline : array_like
|
|
2349
|
-
The array representing the time points corresponding to the x and y coordinates.
|
|
2350
|
-
window : int
|
|
2351
|
-
The size of the window used for numerical differentiation. Must be a positive odd integer.
|
|
2352
|
-
mode : {'bi', 'forward', 'backward'}, optional
|
|
2353
|
-
The numerical differentiation method to be used:
|
|
2354
|
-
- 'bi' (default): Bidirectional differentiation using a symmetric window.
|
|
2355
|
-
- 'forward': Forward differentiation using a one-sided window.
|
|
2356
|
-
- 'backward': Backward differentiation using a one-sided window.
|
|
2357
|
-
|
|
2358
|
-
Returns
|
|
2359
|
-
-------
|
|
2360
|
-
v : ndarray
|
|
2361
|
-
The computed velocity vector of the 2D trajectory with respect to time.
|
|
2362
|
-
The first column represents the x-component of velocity, and the second column represents the y-component.
|
|
2363
|
-
|
|
2364
|
-
Raises
|
|
2365
|
-
------
|
|
2366
|
-
AssertionError
|
|
2367
|
-
If the window size is not an odd integer and mode is 'bi'.
|
|
2368
|
-
|
|
2369
|
-
Notes
|
|
2370
|
-
-----
|
|
2371
|
-
- For 'bi' mode, the window size must be an odd number.
|
|
2372
|
-
- For 'forward' mode, the velocity at the edge points may not be accurate due to the one-sided window.
|
|
2373
|
-
- For 'backward' mode, the velocity at the first few points may not be accurate due to the one-sided window.
|
|
2374
|
-
|
|
2375
|
-
Examples
|
|
2376
|
-
--------
|
|
2377
|
-
>>> import numpy as np
|
|
2378
|
-
>>> x = np.array([1, 2, 4, 7, 11])
|
|
2379
|
-
>>> y = np.array([0, 3, 5, 8, 10])
|
|
2380
|
-
>>> timeline = np.array([0, 1, 2, 3, 4])
|
|
2381
|
-
>>> window = 3
|
|
2382
|
-
>>> velocity(x, y, timeline, window, mode='bi')
|
|
2383
|
-
array([[3., 3.],
|
|
2384
|
-
[3., 3.]])
|
|
2385
|
-
|
|
2386
|
-
>>> velocity(x, y, timeline, window, mode='forward')
|
|
2387
|
-
array([[2., 2.],
|
|
2388
|
-
[3., 3.]])
|
|
2389
|
-
|
|
2390
|
-
>>> velocity(x, y, timeline, window, mode='backward')
|
|
2391
|
-
array([[3., 3.],
|
|
2392
|
-
[3., 3.]])
|
|
2393
|
-
"""
|
|
2394
|
-
|
|
2395
|
-
v = np.zeros((len(x),2))
|
|
2396
|
-
v[:,:] = np.nan
|
|
2397
|
-
|
|
2398
|
-
v[:,0] = derivative(x, timeline, window, mode=mode)
|
|
2399
|
-
v[:,1] = derivative(y, timeline, window, mode=mode)
|
|
2400
|
-
|
|
2401
|
-
return v
|
|
2402
|
-
|
|
2403
|
-
def magnitude_velocity(v_matrix):
|
|
2404
|
-
|
|
2405
|
-
"""
|
|
2406
|
-
Compute the magnitude of velocity vectors given a matrix representing 2D velocity vectors.
|
|
2407
|
-
|
|
2408
|
-
Parameters
|
|
2409
|
-
----------
|
|
2410
|
-
v_matrix : array_like
|
|
2411
|
-
The matrix where each row represents a 2D velocity vector with the first column
|
|
2412
|
-
being the x-component and the second column being the y-component.
|
|
2413
|
-
|
|
2414
|
-
Returns
|
|
2415
|
-
-------
|
|
2416
|
-
magnitude : ndarray
|
|
2417
|
-
The computed magnitudes of the input velocity vectors.
|
|
2418
|
-
|
|
2419
|
-
Notes
|
|
2420
|
-
-----
|
|
2421
|
-
- If a velocity vector has NaN components, the corresponding magnitude will be NaN.
|
|
2422
|
-
- The function handles NaN values in the input matrix gracefully.
|
|
2423
|
-
|
|
2424
|
-
Examples
|
|
2425
|
-
--------
|
|
2426
|
-
>>> import numpy as np
|
|
2427
|
-
>>> v_matrix = np.array([[3, 4],
|
|
2428
|
-
... [2, 2],
|
|
2429
|
-
... [3, 3]])
|
|
2430
|
-
>>> magnitude_velocity(v_matrix)
|
|
2431
|
-
array([5., 2.82842712, 4.24264069])
|
|
2432
|
-
|
|
2433
|
-
>>> v_matrix_with_nan = np.array([[3, 4],
|
|
2434
|
-
... [np.nan, 2],
|
|
2435
|
-
... [3, np.nan]])
|
|
2436
|
-
>>> magnitude_velocity(v_matrix_with_nan)
|
|
2437
|
-
array([5., nan, nan])
|
|
2438
|
-
"""
|
|
2439
|
-
|
|
2440
|
-
magnitude = np.zeros(len(v_matrix))
|
|
2441
|
-
magnitude[:] = np.nan
|
|
2442
|
-
for i in range(len(v_matrix)):
|
|
2443
|
-
if v_matrix[i,0]==v_matrix[i,0]:
|
|
2444
|
-
magnitude[i] = np.sqrt(v_matrix[i,0]**2 + v_matrix[i,1]**2)
|
|
2445
|
-
return magnitude
|
|
2446
|
-
|
|
2447
|
-
def orientation(v_matrix):
|
|
2448
|
-
|
|
2449
|
-
"""
|
|
2450
|
-
Compute the orientation angles (in radians) of 2D velocity vectors given a matrix representing velocity vectors.
|
|
2451
|
-
|
|
2452
|
-
Parameters
|
|
2453
|
-
----------
|
|
2454
|
-
v_matrix : array_like
|
|
2455
|
-
The matrix where each row represents a 2D velocity vector with the first column
|
|
2456
|
-
being the x-component and the second column being the y-component.
|
|
2457
|
-
|
|
2458
|
-
Returns
|
|
2459
|
-
-------
|
|
2460
|
-
orientation_array : ndarray
|
|
2461
|
-
The computed orientation angles of the input velocity vectors in radians.
|
|
2462
|
-
If a velocity vector has NaN components, the corresponding orientation angle will be NaN.
|
|
2463
|
-
|
|
2464
|
-
Examples
|
|
2465
|
-
--------
|
|
2466
|
-
>>> import numpy as np
|
|
2467
|
-
>>> v_matrix = np.array([[3, 4],
|
|
2468
|
-
... [2, 2],
|
|
2469
|
-
... [-3, -3]])
|
|
2470
|
-
>>> orientation(v_matrix)
|
|
2471
|
-
array([0.92729522, 0.78539816, -2.35619449])
|
|
2472
|
-
|
|
2473
|
-
>>> v_matrix_with_nan = np.array([[3, 4],
|
|
2474
|
-
... [np.nan, 2],
|
|
2475
|
-
... [3, np.nan]])
|
|
2476
|
-
>>> orientation(v_matrix_with_nan)
|
|
2477
|
-
array([0.92729522, nan, nan])
|
|
2478
|
-
"""
|
|
2479
|
-
|
|
2480
|
-
orientation_array = np.zeros(len(v_matrix))
|
|
2481
|
-
for t in range(len(orientation_array)):
|
|
2482
|
-
if v_matrix[t,0]==v_matrix[t,0]:
|
|
2483
|
-
orientation_array[t] = np.arctan2(v_matrix[t,0],v_matrix[t,1])
|
|
2484
|
-
return orientation_array
|
|
2485
|
-
|
|
2486
2261
|
def T_MSD(x,y,dt):
|
|
2487
2262
|
|
|
2488
2263
|
"""
|
|
@@ -2859,14 +2634,14 @@ def columnwise_mean(matrix, min_nbr_values = 1):
|
|
|
2859
2634
|
|
|
2860
2635
|
for k in range(matrix.shape[1]):
|
|
2861
2636
|
values = matrix[:,k]
|
|
2862
|
-
values = values[values
|
|
2637
|
+
values = values[values==values]
|
|
2863
2638
|
if len(values[values==values])>min_nbr_values:
|
|
2864
2639
|
mean_line[k] = np.nanmean(values)
|
|
2865
2640
|
mean_line_std[k] = np.nanstd(values)
|
|
2866
2641
|
return mean_line, mean_line_std
|
|
2867
2642
|
|
|
2868
2643
|
|
|
2869
|
-
def mean_signal(df, signal_name, class_col, time_col=None, class_value=[0], return_matrix=False, forced_max_duration=None, min_nbr_values=2):
|
|
2644
|
+
def mean_signal(df, signal_name, class_col, time_col=None, class_value=[0], return_matrix=False, forced_max_duration=None, min_nbr_values=2,conflict_mode='mean'):
|
|
2870
2645
|
|
|
2871
2646
|
"""
|
|
2872
2647
|
Calculate the mean and standard deviation of a specified signal for tracks of a given class in the input DataFrame.
|
|
@@ -2912,12 +2687,13 @@ def mean_signal(df, signal_name, class_col, time_col=None, class_value=[0], retu
|
|
|
2912
2687
|
else:
|
|
2913
2688
|
max_duration = forced_max_duration
|
|
2914
2689
|
n_tracks = len(df.groupby(['position','TRACK_ID']))
|
|
2915
|
-
signal_matrix = np.zeros((n_tracks,max_duration*2 + 1))
|
|
2690
|
+
signal_matrix = np.zeros((n_tracks,int(max_duration)*2 + 1))
|
|
2916
2691
|
signal_matrix[:,:] = np.nan
|
|
2917
2692
|
|
|
2693
|
+
df = df.sort_values(by=['position','TRACK_ID','FRAME'])
|
|
2694
|
+
|
|
2918
2695
|
trackid=0
|
|
2919
2696
|
for track,track_group in df.loc[df[class_col].isin(class_value)].groupby(['position','TRACK_ID']):
|
|
2920
|
-
track_group = track_group.sort_values(by='FRAME')
|
|
2921
2697
|
cclass = track_group[class_col].to_numpy()[0]
|
|
2922
2698
|
if cclass != 0:
|
|
2923
2699
|
ref_time = 0
|
|
@@ -2926,8 +2702,14 @@ def mean_signal(df, signal_name, class_col, time_col=None, class_value=[0], retu
|
|
|
2926
2702
|
ref_time = floor(track_group[time_col].to_numpy()[0])
|
|
2927
2703
|
except:
|
|
2928
2704
|
continue
|
|
2929
|
-
|
|
2930
|
-
|
|
2705
|
+
if conflict_mode=='mean':
|
|
2706
|
+
signal = track_group.groupby('FRAME')[signal_name].mean().to_numpy()
|
|
2707
|
+
elif conflict_mode=='first':
|
|
2708
|
+
signal = track_group.groupby('FRAME')[signal_name].first().to_numpy()
|
|
2709
|
+
else:
|
|
2710
|
+
signal = track_group[signal_name].to_numpy()
|
|
2711
|
+
|
|
2712
|
+
timeline = track_group['FRAME'].unique().astype(int)
|
|
2931
2713
|
timeline_shifted = timeline - ref_time + max_duration
|
|
2932
2714
|
signal_matrix[trackid,timeline_shifted] = signal
|
|
2933
2715
|
trackid+=1
|
celldetective/tracking.py
CHANGED
|
@@ -7,7 +7,7 @@ from btrack.io.utils import localizations_to_objects
|
|
|
7
7
|
from btrack import BayesianTracker
|
|
8
8
|
|
|
9
9
|
from celldetective.measure import measure_features
|
|
10
|
-
from celldetective.utils import rename_intensity_column
|
|
10
|
+
from celldetective.utils import rename_intensity_column, velocity_per_track
|
|
11
11
|
from celldetective.io import view_on_napari_btrack, interpret_tracking_configuration
|
|
12
12
|
|
|
13
13
|
from btrack.datasets import cell_config
|
|
@@ -150,6 +150,7 @@ def track(labels, configuration=None, stack=None, spatial_calibration=1, feature
|
|
|
150
150
|
df[columns] = df_temp
|
|
151
151
|
|
|
152
152
|
df = df.sort_values(by=[column_labels['track'],column_labels['time']])
|
|
153
|
+
df = velocity_per_track(df, window_size=3, mode='bi')
|
|
153
154
|
|
|
154
155
|
if channel_names is not None:
|
|
155
156
|
df = rename_intensity_column(df, channel_names)
|