celldetective 1.3.5__py3-none-any.whl → 1.3.6.post2__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/_version.py +1 -1
- celldetective/filters.py +22 -2
- celldetective/gui/btrack_options.py +151 -1
- celldetective/gui/classifier_widget.py +26 -22
- celldetective/gui/configure_new_exp.py +13 -0
- celldetective/gui/control_panel.py +1 -0
- celldetective/gui/gui_utils.py +138 -8
- celldetective/gui/measurement_options.py +84 -24
- celldetective/gui/neighborhood_options.py +1 -1
- celldetective/gui/signal_annotator.py +6 -1
- celldetective/gui/signal_annotator2.py +22 -19
- celldetective/gui/survival_ui.py +0 -2
- celldetective/gui/thresholds_gui.py +9 -52
- celldetective/gui/viewers.py +58 -21
- celldetective/io.py +31 -8
- celldetective/measure.py +132 -157
- celldetective/scripts/measure_cells.py +28 -13
- celldetective/scripts/segment_cells.py +24 -20
- celldetective/scripts/segment_cells_thresholds.py +21 -21
- celldetective/scripts/track_cells.py +55 -17
- celldetective/tracking.py +78 -53
- celldetective/utils.py +5 -2
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/METADATA +2 -1
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/RECORD +28 -28
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/LICENSE +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/WHEEL +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/top_level.txt +0 -0
|
@@ -8,7 +8,7 @@ import os
|
|
|
8
8
|
import json
|
|
9
9
|
from stardist.models import StarDist2D
|
|
10
10
|
from cellpose.models import CellposeModel
|
|
11
|
-
from celldetective.io import locate_segmentation_model, auto_load_number_of_frames, load_frames
|
|
11
|
+
from celldetective.io import locate_segmentation_model, auto_load_number_of_frames, load_frames, extract_position_name
|
|
12
12
|
from celldetective.utils import interpolate_nan, _estimate_scale_factor, _extract_channel_indices_from_config, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel
|
|
13
13
|
from pathlib import Path, PurePath
|
|
14
14
|
from glob import glob
|
|
@@ -44,6 +44,7 @@ if use_gpu=='True' or use_gpu=='true' or use_gpu=='1':
|
|
|
44
44
|
n_threads = 1 # avoid misbehavior on GPU with multithreading
|
|
45
45
|
else:
|
|
46
46
|
use_gpu = False
|
|
47
|
+
#n_threads = 1 # force 1 threads since all CPUs seem to be in use anyway
|
|
47
48
|
|
|
48
49
|
if not use_gpu:
|
|
49
50
|
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
|
|
@@ -60,20 +61,22 @@ parent1 = Path(pos).parent
|
|
|
60
61
|
expfolder = parent1.parent
|
|
61
62
|
config = PurePath(expfolder,Path("config.ini"))
|
|
62
63
|
assert os.path.exists(config),'The configuration file for the experiment could not be located. Abort.'
|
|
64
|
+
|
|
65
|
+
print(f"Position: {extract_position_name(pos)}...")
|
|
63
66
|
print("Configuration file: ",config)
|
|
67
|
+
print(f"Population: {mode}...")
|
|
64
68
|
|
|
65
69
|
####################################
|
|
66
70
|
# Check model requirements #########
|
|
67
71
|
####################################
|
|
68
72
|
|
|
69
73
|
modelpath = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],"models"])
|
|
70
|
-
print(modelpath)
|
|
71
74
|
model_complete_path = locate_segmentation_model(modelname)
|
|
72
75
|
if model_complete_path is None:
|
|
73
76
|
print('Model could not be found. Abort.')
|
|
74
77
|
os.abort()
|
|
75
78
|
else:
|
|
76
|
-
print(f'Model
|
|
79
|
+
print(f'Model path: {model_complete_path}...')
|
|
77
80
|
|
|
78
81
|
# load config
|
|
79
82
|
assert os.path.exists(model_complete_path+"config_input.json"),'The configuration for the inputs to the model could not be located. Abort.'
|
|
@@ -86,7 +89,7 @@ required_channels = input_config["channels"]
|
|
|
86
89
|
channel_indices = _extract_channel_indices_from_config(config, required_channels)
|
|
87
90
|
print(f'Required channels: {required_channels} located at channel indices {channel_indices}.')
|
|
88
91
|
required_spatial_calibration = input_config['spatial_calibration']
|
|
89
|
-
print(f'
|
|
92
|
+
print(f'Spatial calibration expected by the model: {required_spatial_calibration}...')
|
|
90
93
|
|
|
91
94
|
normalization_percentile = input_config['normalization_percentile']
|
|
92
95
|
normalization_clip = input_config['normalization_clip']
|
|
@@ -117,18 +120,19 @@ if model_type=='cellpose':
|
|
|
117
120
|
flow_threshold = input_config['flow_threshold']
|
|
118
121
|
|
|
119
122
|
scale = _estimate_scale_factor(spatial_calibration, required_spatial_calibration)
|
|
120
|
-
print(f"Scale
|
|
123
|
+
print(f"Scale: {scale}...")
|
|
121
124
|
|
|
122
125
|
nbr_channels = _extract_nbr_channels_from_config(config)
|
|
123
|
-
print(f'Number of channels in the input movie: {nbr_channels}')
|
|
126
|
+
#print(f'Number of channels in the input movie: {nbr_channels}')
|
|
124
127
|
img_num_channels = _get_img_num_per_channel(channel_indices, int(len_movie), nbr_channels)
|
|
125
128
|
|
|
126
129
|
# If everything OK, prepare output, load models
|
|
127
|
-
print('Erasing previous segmentation folder.')
|
|
128
130
|
if os.path.exists(pos+label_folder):
|
|
131
|
+
print('Erasing the previous labels folder...')
|
|
129
132
|
rmtree(pos+label_folder)
|
|
130
133
|
os.mkdir(pos+label_folder)
|
|
131
|
-
print(f'
|
|
134
|
+
print(f'Labels folder successfully generated...')
|
|
135
|
+
|
|
132
136
|
log=f'segmentation model: {modelname}\n'
|
|
133
137
|
with open(pos+f'log_{mode}.json', 'a') as f:
|
|
134
138
|
f.write(f'{datetime.datetime.now()} SEGMENT \n')
|
|
@@ -137,6 +141,7 @@ with open(pos+f'log_{mode}.json', 'a') as f:
|
|
|
137
141
|
|
|
138
142
|
# Loop over all frames and segment
|
|
139
143
|
def segment_index(indices):
|
|
144
|
+
|
|
140
145
|
global scale
|
|
141
146
|
|
|
142
147
|
if model_type=='stardist':
|
|
@@ -179,8 +184,7 @@ def segment_index(indices):
|
|
|
179
184
|
f = np.moveaxis([interpolate_nan(f[:,:,c].copy()) for c in range(f.shape[-1])],0,-1)
|
|
180
185
|
|
|
181
186
|
if np.any(img_num_channels[:,t]==-1):
|
|
182
|
-
f[:,:,np.where(img_num_channels[:,t]==-1)[0]] = 0.
|
|
183
|
-
|
|
187
|
+
f[:,:,np.where(img_num_channels[:,t]==-1)[0]] = 0.
|
|
184
188
|
|
|
185
189
|
if model_type=="stardist":
|
|
186
190
|
Y_pred, details = model.predict_instances(f, n_tiles=model._guess_n_tiles(f), show_tile_progress=False, verbose=False)
|
|
@@ -206,6 +210,10 @@ def segment_index(indices):
|
|
|
206
210
|
del Y_pred;
|
|
207
211
|
gc.collect()
|
|
208
212
|
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
print(f"Starting the segmentation with {n_threads} thread(s) and GPU={use_gpu}...")
|
|
209
217
|
|
|
210
218
|
import concurrent.futures
|
|
211
219
|
|
|
@@ -214,16 +222,12 @@ indices = list(range(img_num_channels.shape[1]))
|
|
|
214
222
|
chunks = np.array_split(indices, n_threads)
|
|
215
223
|
|
|
216
224
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
217
|
-
executor.map(segment_index, chunks)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
# for th in threads:
|
|
224
|
-
# th.start()
|
|
225
|
-
# for th in threads:
|
|
226
|
-
# th.join()
|
|
225
|
+
results = executor.map(segment_index, chunks)
|
|
226
|
+
try:
|
|
227
|
+
for i,return_value in enumerate(results):
|
|
228
|
+
print(f"Thread {i} output check: ",return_value)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
print("Exception: ", e)
|
|
227
231
|
|
|
228
232
|
print('Done.')
|
|
229
233
|
|
|
@@ -5,7 +5,7 @@ Copright © 2022 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
|
5
5
|
import argparse
|
|
6
6
|
import os
|
|
7
7
|
import json
|
|
8
|
-
from celldetective.io import auto_load_number_of_frames, load_frames
|
|
8
|
+
from celldetective.io import auto_load_number_of_frames, load_frames, extract_position_name
|
|
9
9
|
from celldetective.segmentation import segment_frame_from_thresholds
|
|
10
10
|
from celldetective.utils import _extract_channel_indices_from_config, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel, extract_experiment_channels
|
|
11
11
|
from pathlib import Path, PurePath
|
|
@@ -48,8 +48,6 @@ else:
|
|
|
48
48
|
print('The configuration path is not valid. Abort.')
|
|
49
49
|
os.abort()
|
|
50
50
|
|
|
51
|
-
print('The following instructions were successfully loaded: ', threshold_instructions)
|
|
52
|
-
|
|
53
51
|
if mode.lower()=="target" or mode.lower()=="targets":
|
|
54
52
|
label_folder = "labels_targets"
|
|
55
53
|
elif mode.lower()=="effector" or mode.lower()=="effectors":
|
|
@@ -60,12 +58,14 @@ parent1 = Path(pos).parent
|
|
|
60
58
|
expfolder = parent1.parent
|
|
61
59
|
config = PurePath(expfolder,Path("config.ini"))
|
|
62
60
|
assert os.path.exists(config),'The configuration file for the experiment could not be located. Abort.'
|
|
63
|
-
print("Configuration file: ",config)
|
|
64
61
|
|
|
62
|
+
print(f"Position: {extract_position_name(pos)}...")
|
|
63
|
+
print("Configuration file: ",config)
|
|
64
|
+
print(f"Population: {mode}...")
|
|
65
65
|
|
|
66
66
|
channel_indices = _extract_channel_indices_from_config(config, required_channels)
|
|
67
67
|
# need to abort if channel not found
|
|
68
|
-
print(f'Required channels: {required_channels} located at channel indices {channel_indices}
|
|
68
|
+
print(f'Required channels: {required_channels} located at channel indices {channel_indices}...')
|
|
69
69
|
|
|
70
70
|
threshold_instructions.update({'target_channel': channel_indices[0]})
|
|
71
71
|
|
|
@@ -86,15 +86,15 @@ if len_movie_auto is not None:
|
|
|
86
86
|
len_movie = len_movie_auto
|
|
87
87
|
|
|
88
88
|
nbr_channels = _extract_nbr_channels_from_config(config)
|
|
89
|
-
print(f'Number of channels in the input movie: {nbr_channels}')
|
|
89
|
+
#print(f'Number of channels in the input movie: {nbr_channels}')
|
|
90
90
|
img_num_channels = _get_img_num_per_channel(np.arange(nbr_channels), len_movie, nbr_channels)
|
|
91
91
|
|
|
92
92
|
# If everything OK, prepare output, load models
|
|
93
|
-
print('Erasing previous segmentation folder.')
|
|
94
93
|
if os.path.exists(os.sep.join([pos,label_folder])):
|
|
94
|
+
print('Erasing the previous labels folder...')
|
|
95
95
|
rmtree(os.sep.join([pos,label_folder]))
|
|
96
96
|
os.mkdir(os.sep.join([pos,label_folder]))
|
|
97
|
-
print(f'
|
|
97
|
+
print(f'Labels folder successfully generated...')
|
|
98
98
|
|
|
99
99
|
if equalize:
|
|
100
100
|
f_reference = load_frames(img_num_channels[:,equalize_time], file, scale=None, normalize_input=False)
|
|
@@ -103,7 +103,7 @@ else:
|
|
|
103
103
|
f_reference = None
|
|
104
104
|
|
|
105
105
|
threshold_instructions.update({'equalize_reference': f_reference})
|
|
106
|
-
print(threshold_instructions)
|
|
106
|
+
print(f"Instructions: {threshold_instructions}...")
|
|
107
107
|
|
|
108
108
|
# Loop over all frames and segment
|
|
109
109
|
def segment_index(indices):
|
|
@@ -119,6 +119,11 @@ def segment_index(indices):
|
|
|
119
119
|
del mask;
|
|
120
120
|
gc.collect()
|
|
121
121
|
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
print(f"Starting the segmentation with {n_threads} thread(s)...")
|
|
126
|
+
|
|
122
127
|
import concurrent.futures
|
|
123
128
|
|
|
124
129
|
# Multithreading
|
|
@@ -126,20 +131,15 @@ indices = list(range(img_num_channels.shape[1]))
|
|
|
126
131
|
chunks = np.array_split(indices, n_threads)
|
|
127
132
|
|
|
128
133
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
129
|
-
executor.map(segment_index, chunks)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# thread_i = threading.Thread(target=segment_index, args=[chunks[i]])
|
|
136
|
-
# threads.append(thread_i)
|
|
137
|
-
# for th in threads:
|
|
138
|
-
# th.start()
|
|
139
|
-
# for th in threads:
|
|
140
|
-
# th.join()
|
|
134
|
+
results = executor.map(segment_index, chunks)
|
|
135
|
+
try:
|
|
136
|
+
for i,return_value in enumerate(results):
|
|
137
|
+
print(f"Thread {i} output check: ",return_value)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print("Exception: ", e)
|
|
141
140
|
|
|
142
141
|
print('Done.')
|
|
142
|
+
|
|
143
143
|
gc.collect()
|
|
144
144
|
|
|
145
145
|
|
|
@@ -6,7 +6,7 @@ import argparse
|
|
|
6
6
|
import datetime
|
|
7
7
|
import os
|
|
8
8
|
import json
|
|
9
|
-
from celldetective.io import auto_load_number_of_frames, load_frames, interpret_tracking_configuration
|
|
9
|
+
from celldetective.io import auto_load_number_of_frames, load_frames, interpret_tracking_configuration, extract_position_name
|
|
10
10
|
from celldetective.utils import extract_experiment_channels, ConfigSectionMap, _get_img_num_per_channel, extract_experiment_channels
|
|
11
11
|
from celldetective.measure import drop_tonal_features, measure_features
|
|
12
12
|
from celldetective.tracking import track
|
|
@@ -59,6 +59,10 @@ expfolder = parent1.parent
|
|
|
59
59
|
config = PurePath(expfolder,Path("config.ini"))
|
|
60
60
|
assert os.path.exists(config),'The configuration file for the experiment could not be located. Abort.'
|
|
61
61
|
|
|
62
|
+
print(f"Position: {extract_position_name(pos)}...")
|
|
63
|
+
print("Configuration file: ",config)
|
|
64
|
+
print(f"Population: {mode}...")
|
|
65
|
+
|
|
62
66
|
# from exp config fetch spatial calib, channel names
|
|
63
67
|
movie_prefix = ConfigSectionMap(config,"MovieSettings")["movie_prefix"]
|
|
64
68
|
spatial_calibration = float(ConfigSectionMap(config,"MovieSettings")["pxtoum"])
|
|
@@ -71,9 +75,10 @@ channel_names, channel_indices = extract_experiment_channels(config)
|
|
|
71
75
|
nbr_channels = len(channel_names)
|
|
72
76
|
|
|
73
77
|
# from tracking instructions, fetch btrack config, features, haralick, clean_traj, idea: fetch custom timeline?
|
|
78
|
+
print('Looking for tracking instruction file...')
|
|
74
79
|
instr_path = PurePath(expfolder,Path(f"{instruction_file}"))
|
|
75
80
|
if os.path.exists(instr_path):
|
|
76
|
-
print(f"Tracking
|
|
81
|
+
print(f"Tracking instruction file successfully loaded...")
|
|
77
82
|
with open(instr_path, 'r') as f:
|
|
78
83
|
instructions = json.load(f)
|
|
79
84
|
btrack_config = interpret_tracking_configuration(instructions['btrack_config_path'])
|
|
@@ -97,6 +102,16 @@ if os.path.exists(instr_path):
|
|
|
97
102
|
post_processing_options = instructions['post_processing_options']
|
|
98
103
|
else:
|
|
99
104
|
post_processing_options = None
|
|
105
|
+
|
|
106
|
+
btrack_option = True
|
|
107
|
+
if 'btrack_option' in instructions:
|
|
108
|
+
btrack_option = instructions['btrack_option']
|
|
109
|
+
search_range = None
|
|
110
|
+
if 'search_range' in instructions:
|
|
111
|
+
search_range = instructions['search_range']
|
|
112
|
+
memory = None
|
|
113
|
+
if 'memory' in instructions:
|
|
114
|
+
memory = instructions['memory']
|
|
100
115
|
else:
|
|
101
116
|
print('Tracking instructions could not be located... Using a standard bTrack motion model instead...')
|
|
102
117
|
btrack_config = interpret_tracking_configuration(None)
|
|
@@ -104,7 +119,9 @@ else:
|
|
|
104
119
|
mask_channels = None
|
|
105
120
|
haralick_options = None
|
|
106
121
|
post_processing_options = None
|
|
107
|
-
|
|
122
|
+
btrack_option = True
|
|
123
|
+
memory = None
|
|
124
|
+
search_range = None
|
|
108
125
|
if features is None:
|
|
109
126
|
features = []
|
|
110
127
|
|
|
@@ -147,7 +164,15 @@ with open(pos+f'log_{mode}.json', 'a') as f:
|
|
|
147
164
|
f.write(f'{datetime.datetime.now()} TRACK \n')
|
|
148
165
|
f.write(log+"\n")
|
|
149
166
|
|
|
167
|
+
|
|
168
|
+
if not btrack_option:
|
|
169
|
+
features = []
|
|
170
|
+
channel_names = None
|
|
171
|
+
haralick_options = None
|
|
172
|
+
|
|
173
|
+
|
|
150
174
|
def measure_index(indices):
|
|
175
|
+
|
|
151
176
|
for t in tqdm(indices,desc="frame"):
|
|
152
177
|
|
|
153
178
|
# Load channels at time t
|
|
@@ -160,24 +185,27 @@ def measure_index(indices):
|
|
|
160
185
|
df_props.rename(columns={'centroid-1': 'x', 'centroid-0': 'y'},inplace=True)
|
|
161
186
|
df_props['t'] = int(t)
|
|
162
187
|
timestep_dataframes.append(df_props)
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
print(f"Measuring features with {n_threads} thread(s)...")
|
|
193
|
+
|
|
194
|
+
import concurrent.futures
|
|
163
195
|
|
|
164
196
|
# Multithreading
|
|
165
197
|
indices = list(range(img_num_channels.shape[1]))
|
|
166
198
|
chunks = np.array_split(indices, n_threads)
|
|
167
199
|
|
|
168
|
-
import concurrent.futures
|
|
169
|
-
|
|
170
200
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
171
|
-
executor.map(measure_index, chunks)
|
|
201
|
+
results = executor.map(measure_index, chunks)
|
|
202
|
+
try:
|
|
203
|
+
for i,return_value in enumerate(results):
|
|
204
|
+
print(f"Thread {i} output check: ",return_value)
|
|
205
|
+
except Exception as e:
|
|
206
|
+
print("Exception: ", e)
|
|
172
207
|
|
|
173
|
-
|
|
174
|
-
# for i in range(n_threads):
|
|
175
|
-
# thread_i = threading.Thread(target=measure_index, args=[chunks[i]])
|
|
176
|
-
# threads.append(thread_i)
|
|
177
|
-
# for th in threads:
|
|
178
|
-
# th.start()
|
|
179
|
-
# for th in threads:
|
|
180
|
-
# th.join()
|
|
208
|
+
print('Features successfully measured...')
|
|
181
209
|
|
|
182
210
|
df = pd.concat(timestep_dataframes)
|
|
183
211
|
df.reset_index(inplace=True, drop=True)
|
|
@@ -193,6 +221,13 @@ if mask_channels is not None:
|
|
|
193
221
|
df = df.drop(cols_to_drop, axis=1)
|
|
194
222
|
|
|
195
223
|
# do tracking
|
|
224
|
+
if btrack_option:
|
|
225
|
+
tracker = 'bTrack'
|
|
226
|
+
else:
|
|
227
|
+
tracker = 'trackpy'
|
|
228
|
+
|
|
229
|
+
print(f"Start the tracking step using the {tracker} tracker...")
|
|
230
|
+
|
|
196
231
|
trajectories, napari_data = track(None,
|
|
197
232
|
configuration=btrack_config,
|
|
198
233
|
objects=df,
|
|
@@ -203,15 +238,18 @@ trajectories, napari_data = track(None,
|
|
|
203
238
|
track_kwargs={'step_size': 100},
|
|
204
239
|
clean_trajectories_kwargs=post_processing_options,
|
|
205
240
|
volume=(shape_x, shape_y),
|
|
241
|
+
btrack_option=btrack_option,
|
|
242
|
+
search_range=search_range,
|
|
243
|
+
memory=memory,
|
|
206
244
|
)
|
|
245
|
+
print(f"Tracking successfully performed...")
|
|
207
246
|
|
|
208
247
|
# out trajectory table, create POSITION_X_um, POSITION_Y_um, TIME_min (new ones)
|
|
209
|
-
# Save napari data
|
|
248
|
+
# Save napari data # deprecated, should disappear progressively
|
|
210
249
|
np.save(pos+os.sep.join(['output', 'tables', napari_name]), napari_data, allow_pickle=True)
|
|
211
|
-
print(f"napari data successfully saved in {pos+os.sep.join(['output', 'tables'])}")
|
|
212
250
|
|
|
213
251
|
trajectories.to_csv(pos+os.sep.join(['output', 'tables', table_name]), index=False)
|
|
214
|
-
print(f"
|
|
252
|
+
print(f"Trajectory table successfully exported in {os.sep.join(['output', 'tables'])}...")
|
|
215
253
|
|
|
216
254
|
if os.path.exists(pos+os.sep.join(['output', 'tables', table_name.replace('.csv','.pkl')])):
|
|
217
255
|
os.remove(pos+os.sep.join(['output', 'tables', table_name.replace('.csv','.pkl')]))
|
celldetective/tracking.py
CHANGED
|
@@ -12,13 +12,14 @@ from celldetective.io import interpret_tracking_configuration
|
|
|
12
12
|
|
|
13
13
|
import os
|
|
14
14
|
import subprocess
|
|
15
|
+
import trackpy as tp
|
|
15
16
|
|
|
16
17
|
abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],'celldetective'])
|
|
17
18
|
|
|
18
19
|
def track(labels, configuration=None, stack=None, spatial_calibration=1, features=None, channel_names=None,
|
|
19
20
|
haralick_options=None, return_napari_data=False, view_on_napari=False, mask_timepoints=None, mask_channels=None, volume=(2048,2048),
|
|
20
21
|
optimizer_options = {'tm_lim': int(12e4)}, track_kwargs={'step_size': 100}, objects=None,
|
|
21
|
-
clean_trajectories_kwargs=None, column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'},
|
|
22
|
+
clean_trajectories_kwargs=None, btrack_option=True, search_range=None, memory=None,column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'},
|
|
22
23
|
):
|
|
23
24
|
|
|
24
25
|
"""
|
|
@@ -90,6 +91,12 @@ def track(labels, configuration=None, stack=None, spatial_calibration=1, feature
|
|
|
90
91
|
configuration = interpret_tracking_configuration(configuration)
|
|
91
92
|
|
|
92
93
|
if objects is None:
|
|
94
|
+
|
|
95
|
+
if not btrack_option:
|
|
96
|
+
features = []
|
|
97
|
+
channel_names = None
|
|
98
|
+
haralick_options = None
|
|
99
|
+
|
|
93
100
|
objects = extract_objects_and_features(labels, stack, features,
|
|
94
101
|
channel_names=channel_names,
|
|
95
102
|
haralick_options=haralick_options,
|
|
@@ -97,63 +104,81 @@ def track(labels, configuration=None, stack=None, spatial_calibration=1, feature
|
|
|
97
104
|
mask_channels=mask_channels,
|
|
98
105
|
)
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
scaler = StandardScaler()
|
|
109
|
-
if columns:
|
|
110
|
-
x = objects[columns].values
|
|
111
|
-
x_scaled = scaler.fit_transform(x)
|
|
112
|
-
df_temp = pd.DataFrame(x_scaled, columns=columns, index = objects.index)
|
|
113
|
-
objects[columns] = df_temp
|
|
114
|
-
else:
|
|
115
|
-
print('Warning: no features were passed to bTrack...')
|
|
107
|
+
if btrack_option:
|
|
108
|
+
columns = list(objects.columns)
|
|
109
|
+
to_remove = ['x','y','class_id','t']
|
|
110
|
+
for tr in to_remove:
|
|
111
|
+
try:
|
|
112
|
+
columns.remove(tr)
|
|
113
|
+
except:
|
|
114
|
+
print(f'column {tr} could not be found...')
|
|
116
115
|
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
scaler = StandardScaler()
|
|
117
|
+
if columns:
|
|
118
|
+
x = objects[columns].values
|
|
119
|
+
x_scaled = scaler.fit_transform(x)
|
|
120
|
+
df_temp = pd.DataFrame(x_scaled, columns=columns, index = objects.index)
|
|
121
|
+
objects[columns] = df_temp
|
|
122
|
+
else:
|
|
123
|
+
print('Warning: no features were passed to bTrack...')
|
|
119
124
|
|
|
120
|
-
|
|
125
|
+
# 2) track the objects
|
|
126
|
+
new_btrack_objects = localizations_to_objects(objects)
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
with BayesianTracker() as tracker:
|
|
123
129
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
tracker.configure(configuration)
|
|
131
|
+
|
|
132
|
+
if columns:
|
|
133
|
+
tracking_updates = ["motion","visual"]
|
|
134
|
+
#tracker.tracking_updates = ["motion","visual"]
|
|
135
|
+
tracker.features = columns
|
|
136
|
+
else:
|
|
137
|
+
tracking_updates = ["motion"]
|
|
138
|
+
|
|
139
|
+
tracker.append(new_btrack_objects)
|
|
140
|
+
tracker.volume = ((0,volume[0]), (0,volume[1]), (-1e5, 1e5)) #(-1e5, 1e5)
|
|
141
|
+
#print(tracker.volume)
|
|
142
|
+
tracker.track(tracking_updates=tracking_updates, **track_kwargs)
|
|
143
|
+
tracker.optimize(options=optimizer_options)
|
|
144
|
+
|
|
145
|
+
data, properties, graph = tracker.to_napari() #ndim=2
|
|
146
|
+
# do the table post processing and napari options
|
|
147
|
+
if data.shape[1]==4:
|
|
148
|
+
df = pd.DataFrame(data, columns=[column_labels['track'],column_labels['time'],column_labels['y'],column_labels['x']])
|
|
149
|
+
elif data.shape[1]==5:
|
|
150
|
+
df = pd.DataFrame(data, columns=[column_labels['track'],column_labels['time'],"z",column_labels['y'],column_labels['x']])
|
|
151
|
+
df = df.drop(columns=['z'])
|
|
152
|
+
df[column_labels['x']+'_um'] = df[column_labels['x']]*spatial_calibration
|
|
153
|
+
df[column_labels['y']+'_um'] = df[column_labels['y']]*spatial_calibration
|
|
154
|
+
|
|
155
|
+
else:
|
|
156
|
+
properties = None
|
|
157
|
+
graph = {}
|
|
158
|
+
print(f"{objects=} {objects.columns=}")
|
|
159
|
+
objects = objects.rename(columns={"t": "frame"})
|
|
160
|
+
if search_range is not None and memory is not None:
|
|
161
|
+
data = tp.link(objects, search_range, memory=memory,link_strategy='auto')
|
|
128
162
|
else:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
df = df.merge(pd.DataFrame(properties),left_index=True, right_index=True)
|
|
149
|
-
if columns:
|
|
150
|
-
x = df[columns].values
|
|
151
|
-
x_scaled = scaler.inverse_transform(x)
|
|
152
|
-
df_temp = pd.DataFrame(x_scaled, columns=columns, index = df.index)
|
|
153
|
-
df[columns] = df_temp
|
|
154
|
-
|
|
155
|
-
# set dummy features to NaN
|
|
156
|
-
df.loc[df['dummy'],['class_id']+columns] = np.nan
|
|
163
|
+
print('Please provide a valid search range and memory value...')
|
|
164
|
+
return None
|
|
165
|
+
data['particle'] = data['particle'] + 1 # force track id to start at 1
|
|
166
|
+
df = data.rename(columns={'frame': column_labels['time'], 'x': column_labels['x'], 'y': column_labels['y'], 'particle': column_labels['track']})
|
|
167
|
+
df['state'] = 5.0; df['generation'] = 0.0; df['root'] = 1.0; df['parent'] = 1.0; df['dummy'] = False; df['z'] = 0.0;
|
|
168
|
+
data = df[[column_labels['track'],column_labels['time'],"z",column_labels['y'],column_labels['x']]].to_numpy()
|
|
169
|
+
print(f"{df=}")
|
|
170
|
+
|
|
171
|
+
if btrack_option:
|
|
172
|
+
df = df.merge(pd.DataFrame(properties),left_index=True, right_index=True)
|
|
173
|
+
if columns:
|
|
174
|
+
x = df[columns].values
|
|
175
|
+
x_scaled = scaler.inverse_transform(x)
|
|
176
|
+
df_temp = pd.DataFrame(x_scaled, columns=columns, index = df.index)
|
|
177
|
+
df[columns] = df_temp
|
|
178
|
+
|
|
179
|
+
# set dummy features to NaN
|
|
180
|
+
df.loc[df['dummy'],['class_id']+columns] = np.nan
|
|
181
|
+
|
|
157
182
|
df = df.sort_values(by=[column_labels['track'],column_labels['time']])
|
|
158
183
|
df = velocity_per_track(df, window_size=3, mode='bi')
|
|
159
184
|
|
celldetective/utils.py
CHANGED
|
@@ -424,7 +424,7 @@ def estimate_unreliable_edge(activation_protocol=[['gauss',2],['std',4]]):
|
|
|
424
424
|
else:
|
|
425
425
|
edge=0
|
|
426
426
|
for fct in activation_protocol:
|
|
427
|
-
if isinstance(fct[1],(int,np.int_)):
|
|
427
|
+
if isinstance(fct[1],(int,np.int_)) and not fct[0]=='invert':
|
|
428
428
|
edge+=fct[1]
|
|
429
429
|
return edge
|
|
430
430
|
|
|
@@ -1250,7 +1250,10 @@ def ConfigSectionMap(path,section):
|
|
|
1250
1250
|
Config = configparser.ConfigParser()
|
|
1251
1251
|
Config.read(path)
|
|
1252
1252
|
dict1 = {}
|
|
1253
|
-
|
|
1253
|
+
try:
|
|
1254
|
+
options = Config.options(section)
|
|
1255
|
+
except:
|
|
1256
|
+
return None
|
|
1254
1257
|
for option in options:
|
|
1255
1258
|
try:
|
|
1256
1259
|
dict1[option] = Config.get(section, option)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: celldetective
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.6.post2
|
|
4
4
|
Summary: description
|
|
5
5
|
Home-page: http://github.com/remyeltorro/celldetective
|
|
6
6
|
Author: Rémy Torro
|
|
@@ -42,6 +42,7 @@ Requires-Dist: pytest-qt
|
|
|
42
42
|
Requires-Dist: h5py
|
|
43
43
|
Requires-Dist: cliffs_delta
|
|
44
44
|
Requires-Dist: requests
|
|
45
|
+
Requires-Dist: trackpy
|
|
45
46
|
|
|
46
47
|
# Celldetective
|
|
47
48
|
|