celldetective 1.0.2.post1__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 +7 -21
- celldetective/events.py +2 -44
- celldetective/extra_properties.py +62 -52
- celldetective/filters.py +4 -5
- celldetective/gui/__init__.py +1 -1
- celldetective/gui/analyze_block.py +37 -10
- celldetective/gui/btrack_options.py +24 -23
- celldetective/gui/classifier_widget.py +62 -19
- celldetective/gui/configure_new_exp.py +32 -35
- celldetective/gui/control_panel.py +120 -81
- celldetective/gui/gui_utils.py +674 -396
- celldetective/gui/json_readers.py +7 -6
- celldetective/gui/layouts.py +756 -0
- celldetective/gui/measurement_options.py +98 -513
- celldetective/gui/neighborhood_options.py +322 -270
- celldetective/gui/plot_measurements.py +1114 -0
- celldetective/gui/plot_signals_ui.py +21 -20
- celldetective/gui/process_block.py +449 -169
- celldetective/gui/retrain_segmentation_model_options.py +27 -26
- celldetective/gui/retrain_signal_model_options.py +25 -24
- celldetective/gui/seg_model_loader.py +31 -27
- celldetective/gui/signal_annotator.py +2326 -2295
- celldetective/gui/signal_annotator_options.py +18 -16
- celldetective/gui/styles.py +16 -1
- celldetective/gui/survival_ui.py +67 -39
- celldetective/gui/tableUI.py +337 -48
- celldetective/gui/thresholds_gui.py +75 -71
- celldetective/gui/viewers.py +743 -0
- celldetective/io.py +247 -27
- celldetective/measure.py +43 -263
- 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 +498 -27
- celldetective/preprocessing.py +1023 -0
- celldetective/scripts/analyze_signals.py +7 -0
- celldetective/scripts/measure_cells.py +12 -0
- celldetective/scripts/segment_cells.py +20 -4
- celldetective/scripts/track_cells.py +11 -0
- celldetective/scripts/train_segmentation_model.py +35 -34
- celldetective/segmentation.py +14 -9
- celldetective/signals.py +234 -329
- celldetective/tracking.py +2 -2
- celldetective/utils.py +602 -49
- celldetective-1.1.1.dist-info/METADATA +305 -0
- celldetective-1.1.1.dist-info/RECORD +84 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_events.py +28 -0
- tests/test_filters.py +24 -0
- tests/test_io.py +70 -0
- tests/test_measure.py +141 -0
- tests/test_neighborhood.py +70 -0
- tests/test_preprocessing.py +37 -0
- tests/test_segmentation.py +93 -0
- tests/test_signals.py +135 -0
- tests/test_tracking.py +164 -0
- tests/test_utils.py +118 -0
- celldetective-1.0.2.post1.dist-info/METADATA +0 -221
- celldetective-1.0.2.post1.dist-info/RECORD +0 -66
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
|
@@ -3,6 +3,7 @@ Copright © 2022 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import datetime
|
|
6
7
|
import os
|
|
7
8
|
import gc
|
|
8
9
|
from art import tprint
|
|
@@ -45,6 +46,12 @@ else:
|
|
|
45
46
|
print('The trajectories table could not be found. Abort.')
|
|
46
47
|
os.abort()
|
|
47
48
|
|
|
49
|
+
log=f'segmentation model: {model} \n'
|
|
50
|
+
|
|
51
|
+
with open(pos+f'log_{mode}.json', 'a') as f:
|
|
52
|
+
f.write(f'{datetime.datetime.now()} SIGNAL ANALYSIS \n')
|
|
53
|
+
f.write(log)
|
|
54
|
+
|
|
48
55
|
trajectories = analyze_signals(trajectories.copy(), model, interpolate_na=True, selected_signals=None, column_labels = column_labels, plot_outcome=True,output_dir=pos+'output/')
|
|
49
56
|
trajectories = trajectories.sort_values(by=[column_labels['track'], column_labels['time']])
|
|
50
57
|
trajectories.to_csv(pos+os.sep.join(['output','tables', table_name]), index=False)
|
|
@@ -20,6 +20,7 @@ from natsort import natsorted
|
|
|
20
20
|
from art import tprint
|
|
21
21
|
from tifffile import imread
|
|
22
22
|
import threading
|
|
23
|
+
import datetime
|
|
23
24
|
|
|
24
25
|
tprint("Measure")
|
|
25
26
|
|
|
@@ -195,6 +196,17 @@ if trajectories is None:
|
|
|
195
196
|
print('Use features as a substitute for the trajectory table.')
|
|
196
197
|
if 'label' not in features:
|
|
197
198
|
features.append('label')
|
|
199
|
+
features_log=f'features: {features}'
|
|
200
|
+
border_distances_log=f'border_distances: {border_distances}'
|
|
201
|
+
haralick_options_log=f'haralick_options: {haralick_options}'
|
|
202
|
+
background_correction_log=f'background_correction: {background_correction}'
|
|
203
|
+
spot_detection_log=f'spot_detection: {spot_detection}'
|
|
204
|
+
intensity_measurement_radii_log=f'intensity_measurement_radii: {intensity_measurement_radii}'
|
|
205
|
+
isotropic_options_log=f'isotropic_operations: {isotropic_operations} \n'
|
|
206
|
+
log='\n'.join([features_log,border_distances_log,haralick_options_log,background_correction_log,spot_detection_log,intensity_measurement_radii_log,isotropic_options_log])
|
|
207
|
+
with open(pos + f'log_{mode}.json', 'a') as f:
|
|
208
|
+
f.write(f'{datetime.datetime.now()} MEASURE \n')
|
|
209
|
+
f.write(log+'\n')
|
|
198
210
|
|
|
199
211
|
|
|
200
212
|
def measure_index(indices):
|
|
@@ -3,6 +3,7 @@ Copright © 2022 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import datetime
|
|
6
7
|
import os
|
|
7
8
|
import json
|
|
8
9
|
from stardist.models import StarDist2D
|
|
@@ -21,6 +22,9 @@ from art import tprint
|
|
|
21
22
|
from scipy.ndimage import zoom
|
|
22
23
|
import threading
|
|
23
24
|
|
|
25
|
+
import matplotlib.pyplot as plt
|
|
26
|
+
import time
|
|
27
|
+
|
|
24
28
|
tprint("Segment")
|
|
25
29
|
|
|
26
30
|
parser = argparse.ArgumentParser(description="Segment a movie in position with the selected model",
|
|
@@ -128,6 +132,10 @@ if os.path.exists(os.sep.join([pos,label_folder])):
|
|
|
128
132
|
rmtree(os.sep.join([pos,label_folder]))
|
|
129
133
|
os.mkdir(os.sep.join([pos,label_folder]))
|
|
130
134
|
print(f'Folder {os.sep.join([pos,label_folder])} successfully generated.')
|
|
135
|
+
log=f'segmentation model: {modelname}\n'
|
|
136
|
+
with open(pos+f'log_{mode}.json', 'a') as f:
|
|
137
|
+
f.write(f'{datetime.datetime.now()} SEGMENT \n')
|
|
138
|
+
f.write(log)
|
|
131
139
|
|
|
132
140
|
|
|
133
141
|
# Loop over all frames and segment
|
|
@@ -154,10 +162,18 @@ def segment_index(indices):
|
|
|
154
162
|
for t in tqdm(indices,desc="frame"):
|
|
155
163
|
|
|
156
164
|
# Load channels at time t
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
+
|
|
161
177
|
if np.any(img_num_channels[:,t]==-1):
|
|
162
178
|
f[:,:,np.where(img_num_channels[:,t]==-1)[0]] = 0.
|
|
163
179
|
|
|
@@ -3,6 +3,7 @@ Copright © 2022 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import datetime
|
|
6
7
|
import os
|
|
7
8
|
import json
|
|
8
9
|
from celldetective.io import auto_load_number_of_frames, load_frames, interpret_tracking_configuration
|
|
@@ -139,6 +140,16 @@ img_num_channels = _get_img_num_per_channel(channel_indices, len_movie, nbr_chan
|
|
|
139
140
|
#######################################
|
|
140
141
|
|
|
141
142
|
timestep_dataframes = []
|
|
143
|
+
features_log=f'features: {features}'
|
|
144
|
+
mask_channels_log=f'mask_channels: {mask_channels}'
|
|
145
|
+
haralick_option_log=f'haralick_options: {haralick_options}'
|
|
146
|
+
post_processing_option_log=f'post_processing_options: {post_processing_options}'
|
|
147
|
+
log_list=[features_log, mask_channels_log, haralick_option_log, post_processing_option_log]
|
|
148
|
+
log='\n'.join(log_list)
|
|
149
|
+
|
|
150
|
+
with open(pos+f'log_{mode}.json', 'a') as f:
|
|
151
|
+
f.write(f'{datetime.datetime.now()} TRACK \n')
|
|
152
|
+
f.write(log+"\n")
|
|
142
153
|
|
|
143
154
|
def measure_index(indices):
|
|
144
155
|
for t in tqdm(indices,desc="frame"):
|
|
@@ -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):
|