celldetective 1.1.1.post4__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/__init__.py +2 -1
- celldetective/extra_properties.py +62 -34
- celldetective/gui/__init__.py +1 -0
- celldetective/gui/analyze_block.py +2 -1
- celldetective/gui/classifier_widget.py +8 -7
- celldetective/gui/control_panel.py +50 -6
- celldetective/gui/layouts.py +5 -4
- celldetective/gui/neighborhood_options.py +10 -8
- celldetective/gui/plot_signals_ui.py +39 -11
- celldetective/gui/process_block.py +413 -95
- celldetective/gui/retrain_segmentation_model_options.py +17 -4
- celldetective/gui/retrain_signal_model_options.py +106 -6
- celldetective/gui/signal_annotator.py +25 -5
- celldetective/gui/signal_annotator2.py +2708 -0
- celldetective/gui/signal_annotator_options.py +3 -1
- celldetective/gui/survival_ui.py +15 -6
- celldetective/gui/tableUI.py +235 -39
- celldetective/io.py +537 -421
- celldetective/measure.py +919 -969
- celldetective/models/pair_signal_detection/blank +0 -0
- celldetective/neighborhood.py +426 -354
- celldetective/relative_measurements.py +648 -0
- celldetective/scripts/analyze_signals.py +1 -1
- celldetective/scripts/measure_cells.py +28 -8
- celldetective/scripts/measure_relative.py +103 -0
- celldetective/scripts/segment_cells.py +5 -5
- celldetective/scripts/track_cells.py +4 -1
- celldetective/scripts/train_segmentation_model.py +23 -18
- celldetective/scripts/train_signal_model.py +33 -0
- celldetective/signals.py +402 -8
- celldetective/tracking.py +8 -2
- celldetective/utils.py +93 -0
- {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/METADATA +8 -8
- {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/RECORD +38 -34
- {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/WHEEL +1 -1
- {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/LICENSE +0 -0
- {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/top_level.txt +0 -0
celldetective/io.py
CHANGED
|
@@ -30,7 +30,7 @@ from scipy.interpolate import griddata
|
|
|
30
30
|
def get_experiment_wells(experiment):
|
|
31
31
|
|
|
32
32
|
"""
|
|
33
|
-
Retrieves the list of well directories from a given experiment directory, sorted
|
|
33
|
+
Retrieves the list of well directories from a given experiment directory, sorted
|
|
34
34
|
naturally and returned as a NumPy array of strings.
|
|
35
35
|
|
|
36
36
|
Parameters
|
|
@@ -41,104 +41,107 @@ def get_experiment_wells(experiment):
|
|
|
41
41
|
Returns
|
|
42
42
|
-------
|
|
43
43
|
np.ndarray
|
|
44
|
-
An array of strings, each representing the full path to a well directory within the specified
|
|
44
|
+
An array of strings, each representing the full path to a well directory within the specified
|
|
45
45
|
experiment. The array is empty if no well directories are found.
|
|
46
46
|
|
|
47
47
|
Notes
|
|
48
48
|
-----
|
|
49
|
-
- The function assumes well directories are prefixed with 'W' and uses this to filter directories
|
|
49
|
+
- The function assumes well directories are prefixed with 'W' and uses this to filter directories
|
|
50
50
|
within the experiment folder.
|
|
51
51
|
|
|
52
|
-
- Natural sorting is applied to the list of wells to ensure that the order is intuitive (e.g., 'W2'
|
|
53
|
-
comes before 'W10'). This sorting method is especially useful when dealing with numerical sequences
|
|
52
|
+
- Natural sorting is applied to the list of wells to ensure that the order is intuitive (e.g., 'W2'
|
|
53
|
+
comes before 'W10'). This sorting method is especially useful when dealing with numerical sequences
|
|
54
54
|
that are part of the directory names.
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
"""
|
|
57
57
|
|
|
58
58
|
if not experiment.endswith(os.sep):
|
|
59
59
|
experiment += os.sep
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
wells = natsorted(glob(experiment + "W*" + os.sep))
|
|
62
|
-
return np.array(wells,dtype=str)
|
|
62
|
+
return np.array(wells, dtype=str)
|
|
63
|
+
|
|
63
64
|
|
|
64
65
|
def get_config(experiment):
|
|
65
66
|
|
|
66
67
|
if not experiment.endswith(os.sep):
|
|
67
68
|
experiment += os.sep
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
config = experiment + 'config.ini'
|
|
70
71
|
config = rf"{config}"
|
|
71
|
-
assert os.path.exists(config),'The experiment configuration could not be located...'
|
|
72
|
-
return config
|
|
72
|
+
assert os.path.exists(config), 'The experiment configuration could not be located...'
|
|
73
|
+
return config
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def get_spatial_calibration(experiment):
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
config = get_config(experiment)
|
|
79
|
-
PxToUm = float(ConfigSectionMap(config,"MovieSettings")["pxtoum"])
|
|
80
|
-
|
|
80
|
+
PxToUm = float(ConfigSectionMap(config, "MovieSettings")["pxtoum"])
|
|
81
|
+
|
|
81
82
|
return PxToUm
|
|
82
83
|
|
|
84
|
+
|
|
83
85
|
def get_temporal_calibration(experiment):
|
|
84
86
|
|
|
85
87
|
config = get_config(experiment)
|
|
86
|
-
FrameToMin = float(ConfigSectionMap(config,"MovieSettings")["frametomin"])
|
|
87
|
-
|
|
88
|
+
FrameToMin = float(ConfigSectionMap(config, "MovieSettings")["frametomin"])
|
|
89
|
+
|
|
88
90
|
return FrameToMin
|
|
89
91
|
|
|
92
|
+
|
|
90
93
|
def get_experiment_concentrations(experiment, dtype=str):
|
|
91
94
|
|
|
92
95
|
|
|
93
96
|
config = get_config(experiment)
|
|
94
97
|
wells = get_experiment_wells(experiment)
|
|
95
98
|
nbr_of_wells = len(wells)
|
|
96
|
-
|
|
97
|
-
concentrations = ConfigSectionMap(config,"Labels")["concentrations"].split(",")
|
|
99
|
+
|
|
100
|
+
concentrations = ConfigSectionMap(config, "Labels")["concentrations"].split(",")
|
|
98
101
|
if nbr_of_wells != len(concentrations):
|
|
99
|
-
concentrations = [str(s) for s in np.linspace(0,nbr_of_wells-1,nbr_of_wells)]
|
|
100
|
-
|
|
102
|
+
concentrations = [str(s) for s in np.linspace(0, nbr_of_wells - 1, nbr_of_wells)]
|
|
103
|
+
|
|
101
104
|
return np.array([dtype(c) for c in concentrations])
|
|
102
105
|
|
|
106
|
+
|
|
103
107
|
def get_experiment_cell_types(experiment, dtype=str):
|
|
104
|
-
|
|
105
108
|
config = get_config(experiment)
|
|
106
109
|
wells = get_experiment_wells(experiment)
|
|
107
110
|
nbr_of_wells = len(wells)
|
|
108
|
-
|
|
109
|
-
cell_types = ConfigSectionMap(config,"Labels")["cell_types"].split(",")
|
|
111
|
+
|
|
112
|
+
cell_types = ConfigSectionMap(config, "Labels")["cell_types"].split(",")
|
|
110
113
|
if nbr_of_wells != len(cell_types):
|
|
111
|
-
cell_types = [str(s) for s in np.linspace(0,nbr_of_wells-1,nbr_of_wells)]
|
|
112
|
-
|
|
114
|
+
cell_types = [str(s) for s in np.linspace(0, nbr_of_wells - 1, nbr_of_wells)]
|
|
115
|
+
|
|
113
116
|
return np.array([dtype(c) for c in cell_types])
|
|
114
117
|
|
|
118
|
+
|
|
115
119
|
def get_experiment_antibodies(experiment, dtype=str):
|
|
116
120
|
|
|
117
121
|
config = get_config(experiment)
|
|
118
122
|
wells = get_experiment_wells(experiment)
|
|
119
123
|
nbr_of_wells = len(wells)
|
|
120
|
-
|
|
121
|
-
antibodies = ConfigSectionMap(config,"Labels")["antibodies"].split(",")
|
|
124
|
+
|
|
125
|
+
antibodies = ConfigSectionMap(config, "Labels")["antibodies"].split(",")
|
|
122
126
|
if nbr_of_wells != len(antibodies):
|
|
123
|
-
antibodies = [str(s) for s in np.linspace(0,nbr_of_wells-1,nbr_of_wells)]
|
|
124
|
-
|
|
127
|
+
antibodies = [str(s) for s in np.linspace(0, nbr_of_wells - 1, nbr_of_wells)]
|
|
128
|
+
|
|
125
129
|
return np.array([dtype(c) for c in antibodies])
|
|
126
130
|
|
|
131
|
+
|
|
127
132
|
def get_experiment_pharmaceutical_agents(experiment, dtype=str):
|
|
128
|
-
|
|
129
133
|
config = get_config(experiment)
|
|
130
134
|
wells = get_experiment_wells(experiment)
|
|
131
135
|
nbr_of_wells = len(wells)
|
|
132
|
-
|
|
133
|
-
pharmaceutical_agents = ConfigSectionMap(config,"Labels")["pharmaceutical_agents"].split(",")
|
|
136
|
+
|
|
137
|
+
pharmaceutical_agents = ConfigSectionMap(config, "Labels")["pharmaceutical_agents"].split(",")
|
|
134
138
|
if nbr_of_wells != len(pharmaceutical_agents):
|
|
135
|
-
pharmaceutical_agents = [str(s) for s in np.linspace(0,nbr_of_wells-1,nbr_of_wells)]
|
|
136
|
-
|
|
139
|
+
pharmaceutical_agents = [str(s) for s in np.linspace(0, nbr_of_wells - 1, nbr_of_wells)]
|
|
140
|
+
|
|
137
141
|
return np.array([dtype(c) for c in pharmaceutical_agents])
|
|
138
142
|
|
|
139
143
|
|
|
140
144
|
def interpret_wells_and_positions(experiment, well_option, position_option):
|
|
141
|
-
|
|
142
145
|
"""
|
|
143
146
|
Interpret well and position options for a given experiment.
|
|
144
147
|
|
|
@@ -173,26 +176,26 @@ def interpret_wells_and_positions(experiment, well_option, position_option):
|
|
|
173
176
|
>>> experiment = ... # Some experiment object
|
|
174
177
|
>>> interpret_wells_and_positions(experiment, '*', '*')
|
|
175
178
|
(array([0, 1, 2, ..., n-1]), None)
|
|
176
|
-
|
|
179
|
+
|
|
177
180
|
>>> interpret_wells_and_positions(experiment, 2, '*')
|
|
178
181
|
([2], None)
|
|
179
|
-
|
|
182
|
+
|
|
180
183
|
>>> interpret_wells_and_positions(experiment, [1, 3, 5], 2)
|
|
181
184
|
([1, 3, 5], array([2]))
|
|
182
185
|
|
|
183
186
|
"""
|
|
184
|
-
|
|
187
|
+
|
|
185
188
|
wells = get_experiment_wells(experiment)
|
|
186
189
|
nbr_of_wells = len(wells)
|
|
187
190
|
|
|
188
|
-
if well_option=='*':
|
|
191
|
+
if well_option == '*':
|
|
189
192
|
well_indices = np.arange(nbr_of_wells)
|
|
190
193
|
elif isinstance(well_option, int) or isinstance(well_option, np.int_):
|
|
191
194
|
well_indices = [int(well_option)]
|
|
192
195
|
elif isinstance(well_option, list):
|
|
193
196
|
well_indices = well_option
|
|
194
|
-
|
|
195
|
-
if position_option=='*':
|
|
197
|
+
|
|
198
|
+
if position_option == '*':
|
|
196
199
|
position_indices = None
|
|
197
200
|
elif isinstance(position_option, int):
|
|
198
201
|
position_indices = np.array([position_option], dtype=int)
|
|
@@ -200,9 +203,9 @@ def interpret_wells_and_positions(experiment, well_option, position_option):
|
|
|
200
203
|
position_indices = position_option
|
|
201
204
|
|
|
202
205
|
return well_indices, position_indices
|
|
203
|
-
|
|
206
|
+
|
|
207
|
+
|
|
204
208
|
def extract_well_name_and_number(well):
|
|
205
|
-
|
|
206
209
|
"""
|
|
207
210
|
Extract the well name and number from a given well path.
|
|
208
211
|
|
|
@@ -238,10 +241,11 @@ def extract_well_name_and_number(well):
|
|
|
238
241
|
split_well_path = well.split(os.sep)
|
|
239
242
|
split_well_path = list(filter(None, split_well_path))
|
|
240
243
|
well_name = split_well_path[-1]
|
|
241
|
-
well_number = int(split_well_path[-1].replace('W',''))
|
|
242
|
-
|
|
244
|
+
well_number = int(split_well_path[-1].replace('W', ''))
|
|
245
|
+
|
|
243
246
|
return well_name, well_number
|
|
244
247
|
|
|
248
|
+
|
|
245
249
|
def extract_position_name(pos):
|
|
246
250
|
|
|
247
251
|
"""
|
|
@@ -275,17 +279,18 @@ def extract_position_name(pos):
|
|
|
275
279
|
split_pos_path = pos.split(os.sep)
|
|
276
280
|
split_pos_path = list(filter(None, split_pos_path))
|
|
277
281
|
pos_name = split_pos_path[-1]
|
|
278
|
-
|
|
282
|
+
|
|
279
283
|
return pos_name
|
|
280
284
|
|
|
285
|
+
|
|
281
286
|
def get_position_table(pos, population, return_path=False):
|
|
282
287
|
|
|
283
288
|
"""
|
|
284
289
|
Retrieves the data table for a specified population at a given position, optionally returning the table's file path.
|
|
285
290
|
|
|
286
|
-
This function locates and loads a CSV data table associated with a specific population (e.g., 'targets', 'cells')
|
|
287
|
-
from a specified position directory. The position directory should contain an 'output/tables' subdirectory where
|
|
288
|
-
the CSV file named 'trajectories_{population}.csv' is expected to be found. If the file exists, it is loaded into
|
|
291
|
+
This function locates and loads a CSV data table associated with a specific population (e.g., 'targets', 'cells')
|
|
292
|
+
from a specified position directory. The position directory should contain an 'output/tables' subdirectory where
|
|
293
|
+
the CSV file named 'trajectories_{population}.csv' is expected to be found. If the file exists, it is loaded into
|
|
289
294
|
a pandas DataFrame; otherwise, None is returned.
|
|
290
295
|
|
|
291
296
|
Parameters
|
|
@@ -293,17 +298,17 @@ def get_position_table(pos, population, return_path=False):
|
|
|
293
298
|
pos : str
|
|
294
299
|
The path to the position directory from which to load the data table.
|
|
295
300
|
population : str
|
|
296
|
-
The name of the population for which the data table is to be retrieved. This name is used to construct the
|
|
301
|
+
The name of the population for which the data table is to be retrieved. This name is used to construct the
|
|
297
302
|
file name of the CSV file to be loaded.
|
|
298
303
|
return_path : bool, optional
|
|
299
|
-
If True, returns a tuple containing the loaded data table (or None) and the path to the CSV file. If False,
|
|
304
|
+
If True, returns a tuple containing the loaded data table (or None) and the path to the CSV file. If False,
|
|
300
305
|
only the loaded data table (or None) is returned (default is False).
|
|
301
306
|
|
|
302
307
|
Returns
|
|
303
308
|
-------
|
|
304
309
|
pandas.DataFrame or None, or (pandas.DataFrame or None, str)
|
|
305
|
-
If return_path is False, returns the loaded data table as a pandas DataFrame, or None if the table file does
|
|
306
|
-
not exist. If return_path is True, returns a tuple where the first element is the data table (or None) and the
|
|
310
|
+
If return_path is False, returns the loaded data table as a pandas DataFrame, or None if the table file does
|
|
311
|
+
not exist. If return_path is True, returns a tuple where the first element is the data table (or None) and the
|
|
307
312
|
second element is the path to the CSV file.
|
|
308
313
|
|
|
309
314
|
Examples
|
|
@@ -313,13 +318,13 @@ def get_position_table(pos, population, return_path=False):
|
|
|
313
318
|
|
|
314
319
|
>>> df_pos, table_path = get_position_table('/path/to/position', 'targets', return_path=True)
|
|
315
320
|
# This will load the 'trajectories_targets.csv' table and also return the path to the CSV file.
|
|
316
|
-
|
|
321
|
+
|
|
317
322
|
"""
|
|
318
|
-
|
|
323
|
+
|
|
319
324
|
if not pos.endswith(os.sep):
|
|
320
|
-
table = os.sep.join([pos,'output','tables',f'trajectories_{population}.csv'])
|
|
325
|
+
table = os.sep.join([pos, 'output', 'tables', f'trajectories_{population}.csv'])
|
|
321
326
|
else:
|
|
322
|
-
table = pos + os.sep.join(['output','tables',f'trajectories_{population}.csv'])
|
|
327
|
+
table = pos + os.sep.join(['output', 'tables', f'trajectories_{population}.csv'])
|
|
323
328
|
|
|
324
329
|
if os.path.exists(table):
|
|
325
330
|
df_pos = pd.read_csv(table, low_memory=False)
|
|
@@ -332,13 +337,13 @@ def get_position_table(pos, population, return_path=False):
|
|
|
332
337
|
return df_pos
|
|
333
338
|
|
|
334
339
|
def get_position_pickle(pos, population, return_path=False):
|
|
335
|
-
|
|
340
|
+
|
|
336
341
|
"""
|
|
337
342
|
Retrieves the data table for a specified population at a given position, optionally returning the table's file path.
|
|
338
343
|
|
|
339
|
-
This function locates and loads a CSV data table associated with a specific population (e.g., 'targets', 'cells')
|
|
340
|
-
from a specified position directory. The position directory should contain an 'output/tables' subdirectory where
|
|
341
|
-
the CSV file named 'trajectories_{population}.csv' is expected to be found. If the file exists, it is loaded into
|
|
344
|
+
This function locates and loads a CSV data table associated with a specific population (e.g., 'targets', 'cells')
|
|
345
|
+
from a specified position directory. The position directory should contain an 'output/tables' subdirectory where
|
|
346
|
+
the CSV file named 'trajectories_{population}.csv' is expected to be found. If the file exists, it is loaded into
|
|
342
347
|
a pandas DataFrame; otherwise, None is returned.
|
|
343
348
|
|
|
344
349
|
Parameters
|
|
@@ -346,17 +351,17 @@ def get_position_pickle(pos, population, return_path=False):
|
|
|
346
351
|
pos : str
|
|
347
352
|
The path to the position directory from which to load the data table.
|
|
348
353
|
population : str
|
|
349
|
-
The name of the population for which the data table is to be retrieved. This name is used to construct the
|
|
354
|
+
The name of the population for which the data table is to be retrieved. This name is used to construct the
|
|
350
355
|
file name of the CSV file to be loaded.
|
|
351
356
|
return_path : bool, optional
|
|
352
|
-
If True, returns a tuple containing the loaded data table (or None) and the path to the CSV file. If False,
|
|
357
|
+
If True, returns a tuple containing the loaded data table (or None) and the path to the CSV file. If False,
|
|
353
358
|
only the loaded data table (or None) is returned (default is False).
|
|
354
359
|
|
|
355
360
|
Returns
|
|
356
361
|
-------
|
|
357
362
|
pandas.DataFrame or None, or (pandas.DataFrame or None, str)
|
|
358
|
-
If return_path is False, returns the loaded data table as a pandas DataFrame, or None if the table file does
|
|
359
|
-
not exist. If return_path is True, returns a tuple where the first element is the data table (or None) and the
|
|
363
|
+
If return_path is False, returns the loaded data table as a pandas DataFrame, or None if the table file does
|
|
364
|
+
not exist. If return_path is True, returns a tuple where the first element is the data table (or None) and the
|
|
360
365
|
second element is the path to the CSV file.
|
|
361
366
|
|
|
362
367
|
Examples
|
|
@@ -366,19 +371,19 @@ def get_position_pickle(pos, population, return_path=False):
|
|
|
366
371
|
|
|
367
372
|
>>> df_pos, table_path = get_position_table('/path/to/position', 'targets', return_path=True)
|
|
368
373
|
# This will load the 'trajectories_targets.csv' table and also return the path to the CSV file.
|
|
369
|
-
|
|
374
|
+
|
|
370
375
|
"""
|
|
371
|
-
|
|
376
|
+
|
|
372
377
|
if not pos.endswith(os.sep):
|
|
373
378
|
table = os.sep.join([pos,'output','tables',f'trajectories_{population}.pkl'])
|
|
374
379
|
else:
|
|
375
|
-
table = pos + os.sep.join(['output','tables',f'trajectories_{population}.pkl'])
|
|
380
|
+
table = pos + os.sep.join(['output','tables',f'trajectories_{population}.pkl'])
|
|
376
381
|
|
|
377
382
|
if os.path.exists(table):
|
|
378
383
|
df_pos = np.load(table, allow_pickle=True)
|
|
379
384
|
else:
|
|
380
385
|
df_pos = None
|
|
381
|
-
|
|
386
|
+
|
|
382
387
|
if return_path:
|
|
383
388
|
return df_pos, table
|
|
384
389
|
else:
|
|
@@ -423,29 +428,27 @@ def get_position_movie_path(pos, prefix=''):
|
|
|
423
428
|
|
|
424
429
|
|
|
425
430
|
if not pos.endswith(os.sep):
|
|
426
|
-
pos+=os.sep
|
|
427
|
-
movies = glob(pos+os.sep.join(['movie',prefix+'*.tif']))
|
|
428
|
-
if len(movies)>0:
|
|
431
|
+
pos += os.sep
|
|
432
|
+
movies = glob(pos + os.sep.join(['movie', prefix + '*.tif']))
|
|
433
|
+
if len(movies) > 0:
|
|
429
434
|
stack_path = movies[0]
|
|
430
435
|
else:
|
|
431
436
|
stack_path = None
|
|
432
|
-
|
|
433
|
-
return stack_path
|
|
434
|
-
|
|
435
437
|
|
|
438
|
+
return stack_path
|
|
436
439
|
|
|
437
|
-
def load_experiment_tables(experiment, population='targets', well_option='*',position_option='*', return_pos_info=False):
|
|
438
|
-
|
|
439
440
|
|
|
441
|
+
def load_experiment_tables(experiment, population='targets', well_option='*', position_option='*',
|
|
442
|
+
return_pos_info=False, load_pickle=False):
|
|
440
443
|
"""
|
|
441
|
-
Loads and aggregates data tables for specified wells and positions within an experiment,
|
|
444
|
+
Loads and aggregates data tables for specified wells and positions within an experiment,
|
|
442
445
|
optionally returning position information alongside the aggregated data table.
|
|
443
446
|
|
|
444
|
-
This function collects data from tables associated with specific population types across
|
|
445
|
-
various wells and positions within an experiment. It uses the experiment's configuration
|
|
446
|
-
to gather metadata such as movie prefix, concentrations, cell types, antibodies, and
|
|
447
|
-
pharmaceutical agents. Users can specify which wells and positions to include in the
|
|
448
|
-
aggregation through pattern matching, and whether to include detailed position information
|
|
447
|
+
This function collects data from tables associated with specific population types across
|
|
448
|
+
various wells and positions within an experiment. It uses the experiment's configuration
|
|
449
|
+
to gather metadata such as movie prefix, concentrations, cell types, antibodies, and
|
|
450
|
+
pharmaceutical agents. Users can specify which wells and positions to include in the
|
|
451
|
+
aggregation through pattern matching, and whether to include detailed position information
|
|
449
452
|
in the output.
|
|
450
453
|
|
|
451
454
|
Parameters
|
|
@@ -459,15 +462,15 @@ def load_experiment_tables(experiment, population='targets', well_option='*',pos
|
|
|
459
462
|
position_option : str, optional
|
|
460
463
|
A pattern to specify which positions to include (default is '*', which includes all positions).
|
|
461
464
|
return_pos_info : bool, optional
|
|
462
|
-
If True, returns a tuple where the first element is the aggregated data table and the
|
|
465
|
+
If True, returns a tuple where the first element is the aggregated data table and the
|
|
463
466
|
second element is detailed position information (default is False).
|
|
464
467
|
|
|
465
468
|
Returns
|
|
466
469
|
-------
|
|
467
470
|
pandas.DataFrame or (pandas.DataFrame, pandas.DataFrame)
|
|
468
|
-
If return_pos_info is False, returns a pandas DataFrame aggregating the data from the
|
|
469
|
-
specified tables. If return_pos_info is True, returns a tuple where the first element
|
|
470
|
-
is the aggregated data table and the second element is a DataFrame with detailed position
|
|
471
|
+
If return_pos_info is False, returns a pandas DataFrame aggregating the data from the
|
|
472
|
+
specified tables. If return_pos_info is True, returns a tuple where the first element
|
|
473
|
+
is the aggregated data table and the second element is a DataFrame with detailed position
|
|
471
474
|
information.
|
|
472
475
|
|
|
473
476
|
Raises
|
|
@@ -479,50 +482,49 @@ def load_experiment_tables(experiment, population='targets', well_option='*',pos
|
|
|
479
482
|
|
|
480
483
|
Notes
|
|
481
484
|
-----
|
|
482
|
-
- This function assumes that the naming conventions and directory structure of the experiment
|
|
485
|
+
- This function assumes that the naming conventions and directory structure of the experiment
|
|
483
486
|
follow a specific format, as outlined in the experiment's configuration file.
|
|
484
|
-
- The function utilizes several helper functions to extract metadata, interpret well and
|
|
485
|
-
position patterns, and load individual position tables. Errors in these helper functions
|
|
487
|
+
- The function utilizes several helper functions to extract metadata, interpret well and
|
|
488
|
+
position patterns, and load individual position tables. Errors in these helper functions
|
|
486
489
|
may propagate up and affect the behavior of this function.
|
|
487
490
|
|
|
488
491
|
Examples
|
|
489
492
|
--------
|
|
490
493
|
>>> load_experiment_tables('/path/to/experiment', population='targets', well_option='W1', position_option='1-*')
|
|
491
494
|
# This will load and aggregate tables for the 'targets' population within well 'W1' and positions matching '1-*'.
|
|
492
|
-
|
|
493
|
-
"""
|
|
494
495
|
|
|
496
|
+
"""
|
|
495
497
|
|
|
496
498
|
config = get_config(experiment)
|
|
497
499
|
wells = get_experiment_wells(experiment)
|
|
498
500
|
|
|
499
|
-
movie_prefix = ConfigSectionMap(config,"MovieSettings")["movie_prefix"]
|
|
501
|
+
movie_prefix = ConfigSectionMap(config, "MovieSettings")["movie_prefix"]
|
|
500
502
|
concentrations = get_experiment_concentrations(experiment, dtype=float)
|
|
501
503
|
cell_types = get_experiment_cell_types(experiment)
|
|
502
504
|
antibodies = get_experiment_antibodies(experiment)
|
|
503
505
|
pharmaceutical_agents = get_experiment_pharmaceutical_agents(experiment)
|
|
504
|
-
well_labels = _extract_labels_from_config(config,len(wells))
|
|
505
|
-
|
|
506
|
+
well_labels = _extract_labels_from_config(config, len(wells))
|
|
507
|
+
|
|
506
508
|
well_indices, position_indices = interpret_wells_and_positions(experiment, well_option, position_option)
|
|
507
509
|
|
|
508
510
|
df = []
|
|
509
511
|
df_pos_info = []
|
|
510
512
|
real_well_index = 0
|
|
511
|
-
|
|
513
|
+
|
|
512
514
|
for k, well_path in enumerate(tqdm(wells[well_indices])):
|
|
513
|
-
|
|
514
|
-
any_table = False
|
|
515
|
-
|
|
515
|
+
|
|
516
|
+
any_table = False # assume no table
|
|
517
|
+
|
|
516
518
|
well_name, well_number = extract_well_name_and_number(well_path)
|
|
517
519
|
widx = well_indices[k]
|
|
518
520
|
|
|
519
521
|
well_alias = well_labels[widx]
|
|
520
|
-
|
|
522
|
+
|
|
521
523
|
well_concentration = concentrations[widx]
|
|
522
524
|
well_antibody = antibodies[widx]
|
|
523
525
|
well_cell_type = cell_types[widx]
|
|
524
526
|
well_pharmaceutical_agent = pharmaceutical_agents[widx]
|
|
525
|
-
|
|
527
|
+
|
|
526
528
|
positions = get_positions_in_well(well_path)
|
|
527
529
|
if position_indices is not None:
|
|
528
530
|
try:
|
|
@@ -532,13 +534,17 @@ def load_experiment_tables(experiment, population='targets', well_option='*',pos
|
|
|
532
534
|
continue
|
|
533
535
|
|
|
534
536
|
real_pos_index = 0
|
|
535
|
-
for pidx,pos_path in enumerate(positions):
|
|
536
|
-
|
|
537
|
+
for pidx, pos_path in enumerate(positions):
|
|
538
|
+
|
|
537
539
|
pos_name = extract_position_name(pos_path)
|
|
538
|
-
|
|
540
|
+
|
|
539
541
|
stack_path = get_position_movie_path(pos_path, prefix=movie_prefix)
|
|
540
|
-
|
|
541
|
-
|
|
542
|
+
|
|
543
|
+
if not load_pickle:
|
|
544
|
+
df_pos, table = get_position_table(pos_path, population=population, return_path=True)
|
|
545
|
+
else:
|
|
546
|
+
df_pos, table = get_position_pickle(pos_path, population=population, return_path=True)
|
|
547
|
+
|
|
542
548
|
if df_pos is not None:
|
|
543
549
|
|
|
544
550
|
df_pos['position'] = pos_path
|
|
@@ -551,25 +557,28 @@ def load_experiment_tables(experiment, population='targets', well_option='*',pos
|
|
|
551
557
|
df_pos['antibody'] = well_antibody
|
|
552
558
|
df_pos['cell_type'] = well_cell_type
|
|
553
559
|
df_pos['pharmaceutical_agent'] = well_pharmaceutical_agent
|
|
554
|
-
|
|
560
|
+
|
|
555
561
|
df.append(df_pos)
|
|
556
562
|
any_table = True
|
|
557
|
-
|
|
558
|
-
df_pos_info.append(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
+
|
|
564
|
+
df_pos_info.append(
|
|
565
|
+
{'pos_path': pos_path, 'pos_index': real_pos_index, 'pos_name': pos_name, 'table_path': table,
|
|
566
|
+
'stack_path': stack_path,
|
|
567
|
+
'well_path': well_path, 'well_index': real_well_index, 'well_name': well_name,
|
|
568
|
+
'well_number': well_number, 'well_alias': well_alias})
|
|
569
|
+
|
|
570
|
+
real_pos_index += 1
|
|
571
|
+
|
|
563
572
|
if any_table:
|
|
564
573
|
real_well_index += 1
|
|
565
|
-
|
|
574
|
+
|
|
566
575
|
df_pos_info = pd.DataFrame(df_pos_info)
|
|
567
|
-
if len(df)>0:
|
|
576
|
+
if len(df) > 0:
|
|
568
577
|
df = pd.concat(df)
|
|
569
578
|
df = df.reset_index(drop=True)
|
|
570
579
|
else:
|
|
571
580
|
df = None
|
|
572
|
-
|
|
581
|
+
|
|
573
582
|
if return_pos_info:
|
|
574
583
|
return df, df_pos_info
|
|
575
584
|
else:
|
|
@@ -615,15 +624,15 @@ def locate_stack(position, prefix='Aligned'):
|
|
|
615
624
|
"""
|
|
616
625
|
|
|
617
626
|
if not position.endswith(os.sep):
|
|
618
|
-
position+=os.sep
|
|
627
|
+
position += os.sep
|
|
619
628
|
|
|
620
|
-
stack_path = glob(position+os.sep.join(['movie', f'{prefix}*.tif']))
|
|
621
|
-
assert len(stack_path)>0,f"No movie with prefix {prefix} found..."
|
|
622
|
-
stack = imread(stack_path[0].replace('\\','/'))
|
|
623
|
-
if stack.ndim==4:
|
|
629
|
+
stack_path = glob(position + os.sep.join(['movie', f'{prefix}*.tif']))
|
|
630
|
+
assert len(stack_path) > 0, f"No movie with prefix {prefix} found..."
|
|
631
|
+
stack = imread(stack_path[0].replace('\\', '/'), is_mmstack=False)
|
|
632
|
+
if stack.ndim == 4:
|
|
624
633
|
stack = np.moveaxis(stack, 1, -1)
|
|
625
|
-
elif stack.ndim==3:
|
|
626
|
-
stack = stack[
|
|
634
|
+
elif stack.ndim == 3:
|
|
635
|
+
stack = stack[:, :, :, np.newaxis]
|
|
627
636
|
|
|
628
637
|
return stack
|
|
629
638
|
|
|
@@ -638,7 +647,7 @@ def locate_labels(position, population='target'):
|
|
|
638
647
|
position : str
|
|
639
648
|
The position folder within the well where the stack is located.
|
|
640
649
|
population : str, optional
|
|
641
|
-
The population for which to locate the labels.
|
|
650
|
+
The population for which to locate the labels.
|
|
642
651
|
Valid options are 'target' and 'effector'.
|
|
643
652
|
The default is 'target'.
|
|
644
653
|
|
|
@@ -662,13 +671,13 @@ def locate_labels(position, population='target'):
|
|
|
662
671
|
"""
|
|
663
672
|
|
|
664
673
|
if not position.endswith(os.sep):
|
|
665
|
-
position+=os.sep
|
|
666
|
-
|
|
667
|
-
if population.lower()=="target" or population.lower()=="targets":
|
|
668
|
-
label_path = natsorted(glob(position+os.sep.join(["labels_targets", "*.tif"])))
|
|
669
|
-
elif population.lower()=="effector" or population.lower()=="effectors":
|
|
670
|
-
label_path = natsorted(glob(position+os.sep.join(["labels_effectors", "*.tif"])))
|
|
671
|
-
labels = np.array([imread(i.replace('\\','/')) for i in label_path])
|
|
674
|
+
position += os.sep
|
|
675
|
+
|
|
676
|
+
if population.lower() == "target" or population.lower() == "targets":
|
|
677
|
+
label_path = natsorted(glob(position + os.sep.join(["labels_targets", "*.tif"])))
|
|
678
|
+
elif population.lower() == "effector" or population.lower() == "effectors":
|
|
679
|
+
label_path = natsorted(glob(position + os.sep.join(["labels_effectors", "*.tif"])))
|
|
680
|
+
labels = np.array([imread(i.replace('\\', '/')) for i in label_path])
|
|
672
681
|
|
|
673
682
|
return labels
|
|
674
683
|
|
|
@@ -711,20 +720,20 @@ def locate_stack_and_labels(position, prefix='Aligned', population="target"):
|
|
|
711
720
|
--------
|
|
712
721
|
>>> stack, labels = locate_stack_and_labels(position, prefix='Aligned', population="target")
|
|
713
722
|
# Locate and load the stack and segmentation labels for further processing.
|
|
714
|
-
|
|
723
|
+
|
|
715
724
|
"""
|
|
716
725
|
|
|
717
|
-
position = position.replace('\\','/')
|
|
726
|
+
position = position.replace('\\', '/')
|
|
718
727
|
labels = locate_labels(position, population=population)
|
|
719
728
|
stack = locate_stack(position, prefix=prefix)
|
|
720
|
-
assert len(stack)==len(
|
|
729
|
+
assert len(stack) == len(
|
|
730
|
+
labels), f"The shape of the stack {stack.shape} does not match with the shape of the labels {labels.shape}"
|
|
721
731
|
|
|
722
|
-
return stack,labels
|
|
732
|
+
return stack, labels
|
|
723
733
|
|
|
724
734
|
def load_tracking_data(position, prefix="Aligned", population="target"):
|
|
725
|
-
|
|
726
735
|
"""
|
|
727
|
-
|
|
736
|
+
|
|
728
737
|
Load the tracking data, labels, and stack for a given position and population.
|
|
729
738
|
|
|
730
739
|
Parameters
|
|
@@ -759,15 +768,15 @@ def load_tracking_data(position, prefix="Aligned", population="target"):
|
|
|
759
768
|
|
|
760
769
|
"""
|
|
761
770
|
|
|
762
|
-
position = position.replace('\\','/')
|
|
763
|
-
if population.lower()=="target" or population.lower()=="targets":
|
|
764
|
-
trajectories = pd.read_csv(position+os.sep.join(['output', 'tables', 'trajectories_targets.csv']))
|
|
765
|
-
elif population.lower()=="effector" or population.lower()=="effectors":
|
|
766
|
-
trajectories = pd.read_csv(position+os.sep.join(['output', 'tables', 'trajectories_effectors.csv']))
|
|
771
|
+
position = position.replace('\\', '/')
|
|
772
|
+
if population.lower() == "target" or population.lower() == "targets":
|
|
773
|
+
trajectories = pd.read_csv(position + os.sep.join(['output', 'tables', 'trajectories_targets.csv']))
|
|
774
|
+
elif population.lower() == "effector" or population.lower() == "effectors":
|
|
775
|
+
trajectories = pd.read_csv(position + os.sep.join(['output', 'tables', 'trajectories_effectors.csv']))
|
|
767
776
|
|
|
768
|
-
stack,labels = locate_stack_and_labels(position, prefix=prefix, population=population)
|
|
777
|
+
stack, labels = locate_stack_and_labels(position, prefix=prefix, population=population)
|
|
769
778
|
|
|
770
|
-
return trajectories,labels,stack
|
|
779
|
+
return trajectories, labels, stack
|
|
771
780
|
|
|
772
781
|
|
|
773
782
|
def auto_load_number_of_frames(stack_path):
|
|
@@ -798,11 +807,11 @@ def auto_load_number_of_frames(stack_path):
|
|
|
798
807
|
--------
|
|
799
808
|
>>> len_movie = auto_load_number_of_frames(stack_path)
|
|
800
809
|
# Automatically estimate the number of frames in the stack.
|
|
801
|
-
|
|
810
|
+
|
|
802
811
|
"""
|
|
803
812
|
|
|
804
813
|
# Try to estimate automatically # frames
|
|
805
|
-
|
|
814
|
+
|
|
806
815
|
if stack_path is None:
|
|
807
816
|
return None
|
|
808
817
|
|
|
@@ -821,7 +830,7 @@ def auto_load_number_of_frames(stack_path):
|
|
|
821
830
|
try:
|
|
822
831
|
# Try nframes
|
|
823
832
|
nslices = int(attr[np.argmax([s.startswith("frames") for s in attr])].split("=")[-1])
|
|
824
|
-
if nslices>1:
|
|
833
|
+
if nslices > 1:
|
|
825
834
|
len_movie = nslices
|
|
826
835
|
print(f"Auto-detected movie length movie: {len_movie}")
|
|
827
836
|
else:
|
|
@@ -841,27 +850,35 @@ def auto_load_number_of_frames(stack_path):
|
|
|
841
850
|
del img_desc;
|
|
842
851
|
except:
|
|
843
852
|
pass
|
|
853
|
+
|
|
854
|
+
if 'len_movie' not in locals():
|
|
855
|
+
stack = imread(stack_path)
|
|
856
|
+
len_movie = len(stack)
|
|
857
|
+
del stack
|
|
844
858
|
gc.collect()
|
|
845
859
|
|
|
860
|
+
print(f'Automatically detected stack length: {len_movie}...')
|
|
861
|
+
|
|
846
862
|
return len_movie if 'len_movie' in locals() else None
|
|
847
863
|
|
|
864
|
+
|
|
848
865
|
def parse_isotropic_radii(string):
|
|
849
866
|
sections = re.split(',| ', string)
|
|
850
867
|
radii = []
|
|
851
|
-
for k,s in enumerate(sections):
|
|
868
|
+
for k, s in enumerate(sections):
|
|
852
869
|
if s.isdigit():
|
|
853
870
|
radii.append(int(s))
|
|
854
871
|
if '[' in s:
|
|
855
|
-
ring = [int(s.replace('[','')), int(sections[k+1].replace(']',''))]
|
|
872
|
+
ring = [int(s.replace('[', '')), int(sections[k + 1].replace(']', ''))]
|
|
856
873
|
radii.append(ring)
|
|
857
874
|
else:
|
|
858
875
|
pass
|
|
859
876
|
return radii
|
|
860
877
|
|
|
861
|
-
def get_tracking_configs_list(return_path=False):
|
|
862
878
|
|
|
879
|
+
def get_tracking_configs_list(return_path=False):
|
|
863
880
|
"""
|
|
864
|
-
|
|
881
|
+
|
|
865
882
|
Retrieve a list of available tracking configurations.
|
|
866
883
|
|
|
867
884
|
Parameters
|
|
@@ -892,26 +909,30 @@ def get_tracking_configs_list(return_path=False):
|
|
|
892
909
|
|
|
893
910
|
"""
|
|
894
911
|
|
|
895
|
-
modelpath = os.sep.join(
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
available_models =
|
|
899
|
-
|
|
912
|
+
modelpath = os.sep.join(
|
|
913
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models", "tracking_configs",
|
|
914
|
+
os.sep])
|
|
915
|
+
available_models = glob(modelpath + '*.json')
|
|
916
|
+
available_models = [m.replace('\\', '/').split('/')[-1] for m in available_models]
|
|
917
|
+
available_models = [m.replace('\\', '/').split('.')[0] for m in available_models]
|
|
900
918
|
|
|
901
919
|
if not return_path:
|
|
902
920
|
return available_models
|
|
903
921
|
else:
|
|
904
922
|
return available_models, modelpath
|
|
905
923
|
|
|
924
|
+
|
|
906
925
|
def interpret_tracking_configuration(config):
|
|
907
926
|
|
|
908
927
|
if isinstance(config, str):
|
|
909
928
|
if os.path.exists(config):
|
|
910
929
|
return config
|
|
911
930
|
else:
|
|
912
|
-
modelpath = os.sep.join(
|
|
913
|
-
|
|
914
|
-
|
|
931
|
+
modelpath = os.sep.join(
|
|
932
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models",
|
|
933
|
+
"tracking_configs", os.sep])
|
|
934
|
+
if os.path.exists(modelpath + config + '.json'):
|
|
935
|
+
return modelpath + config + '.json'
|
|
915
936
|
else:
|
|
916
937
|
config = cell_config()
|
|
917
938
|
elif config is None:
|
|
@@ -919,10 +940,11 @@ def interpret_tracking_configuration(config):
|
|
|
919
940
|
|
|
920
941
|
return config
|
|
921
942
|
|
|
943
|
+
|
|
922
944
|
def get_signal_models_list(return_path=False):
|
|
923
945
|
|
|
924
946
|
"""
|
|
925
|
-
|
|
947
|
+
|
|
926
948
|
Retrieve a list of available signal detection models.
|
|
927
949
|
|
|
928
950
|
Parameters
|
|
@@ -953,11 +975,13 @@ def get_signal_models_list(return_path=False):
|
|
|
953
975
|
|
|
954
976
|
"""
|
|
955
977
|
|
|
956
|
-
modelpath = os.sep.join(
|
|
957
|
-
|
|
978
|
+
modelpath = os.sep.join(
|
|
979
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models", "signal_detection",
|
|
980
|
+
os.sep])
|
|
981
|
+
repository_models = get_zenodo_files(cat=os.sep.join(["models", "signal_detection"]))
|
|
958
982
|
|
|
959
|
-
available_models = glob(modelpath+f'*{os.sep}')
|
|
960
|
-
available_models = [m.replace('\\','/').split('/')[-2] for m in available_models]
|
|
983
|
+
available_models = glob(modelpath + f'*{os.sep}')
|
|
984
|
+
available_models = [m.replace('\\', '/').split('/')[-2] for m in available_models]
|
|
961
985
|
for rm in repository_models:
|
|
962
986
|
if rm not in available_models:
|
|
963
987
|
available_models.append(rm)
|
|
@@ -967,21 +991,72 @@ def get_signal_models_list(return_path=False):
|
|
|
967
991
|
else:
|
|
968
992
|
return available_models, modelpath
|
|
969
993
|
|
|
994
|
+
def get_pair_signal_models_list(return_path=False):
|
|
995
|
+
"""
|
|
970
996
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
997
|
+
Retrieve a list of available signal detection models.
|
|
998
|
+
|
|
999
|
+
Parameters
|
|
1000
|
+
----------
|
|
1001
|
+
return_path : bool, optional
|
|
1002
|
+
If True, also returns the path to the models. Default is False.
|
|
1003
|
+
|
|
1004
|
+
Returns
|
|
1005
|
+
-------
|
|
1006
|
+
list or tuple
|
|
1007
|
+
If return_path is False, returns a list of available signal detection models.
|
|
1008
|
+
If return_path is True, returns a tuple containing the list of models and the path to the models.
|
|
1009
|
+
|
|
1010
|
+
Notes
|
|
1011
|
+
-----
|
|
1012
|
+
This function retrieves the list of available signal detection models by searching for model directories
|
|
1013
|
+
in the predefined model path. The model path is derived from the parent directory of the current script
|
|
1014
|
+
location and the path to the model directory. By default, it returns only the names of the models.
|
|
1015
|
+
If return_path is set to True, it also returns the path to the models.
|
|
1016
|
+
|
|
1017
|
+
Examples
|
|
1018
|
+
--------
|
|
1019
|
+
>>> models = get_signal_models_list()
|
|
1020
|
+
# Retrieve a list of available signal detection models.
|
|
1021
|
+
|
|
1022
|
+
>>> models, path = get_signal_models_list(return_path=True)
|
|
1023
|
+
# Retrieve a list of available signal detection models and the path to the models.
|
|
1024
|
+
|
|
1025
|
+
"""
|
|
1026
|
+
|
|
1027
|
+
modelpath = os.sep.join(
|
|
1028
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models", "pair_signal_detection",
|
|
1029
|
+
os.sep])
|
|
1030
|
+
#repository_models = get_zenodo_files(cat=os.sep.join(["models", "pair_signal_detection"]))
|
|
1031
|
+
|
|
1032
|
+
available_models = glob(modelpath + f'*{os.sep}')
|
|
1033
|
+
available_models = [m.replace('\\', '/').split('/')[-2] for m in available_models]
|
|
1034
|
+
#for rm in repository_models:
|
|
1035
|
+
# if rm not in available_models:
|
|
1036
|
+
# available_models.append(rm)
|
|
1037
|
+
|
|
1038
|
+
if not return_path:
|
|
1039
|
+
return available_models
|
|
1040
|
+
else:
|
|
1041
|
+
return available_models, modelpath
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
def locate_signal_model(name, path=None, pairs=False):
|
|
1045
|
+
|
|
1046
|
+
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective"])
|
|
974
1047
|
modelpath = os.sep.join([main_dir, "models", "signal_detection", os.sep])
|
|
1048
|
+
if pairs:
|
|
1049
|
+
modelpath = os.sep.join([main_dir, "models", "pair_signal_detection", os.sep])
|
|
975
1050
|
print(f'Looking for {name} in {modelpath}')
|
|
976
|
-
models = glob(modelpath+f'*{os.sep}')
|
|
1051
|
+
models = glob(modelpath + f'*{os.sep}')
|
|
977
1052
|
if path is not None:
|
|
978
1053
|
if not path.endswith(os.sep):
|
|
979
1054
|
path += os.sep
|
|
980
|
-
models += glob(path+f'*{os.sep}')
|
|
1055
|
+
models += glob(path + f'*{os.sep}')
|
|
981
1056
|
|
|
982
|
-
match=None
|
|
1057
|
+
match = None
|
|
983
1058
|
for m in models:
|
|
984
|
-
if name==m.replace('\\',os.sep).split(os.sep)[-2]:
|
|
1059
|
+
if name == m.replace('\\', os.sep).split(os.sep)[-2]:
|
|
985
1060
|
match = m
|
|
986
1061
|
return match
|
|
987
1062
|
# else no match, try zenodo
|
|
@@ -990,9 +1065,18 @@ def locate_signal_model(name, path=None):
|
|
|
990
1065
|
index = files.index(name)
|
|
991
1066
|
cat = categories[index]
|
|
992
1067
|
download_zenodo_file(name, os.sep.join([main_dir, cat]))
|
|
993
|
-
match = os.sep.join([main_dir, cat, name])+os.sep
|
|
1068
|
+
match = os.sep.join([main_dir, cat, name]) + os.sep
|
|
994
1069
|
return match
|
|
995
1070
|
|
|
1071
|
+
def locate_pair_signal_model(name, path=None):
|
|
1072
|
+
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective"])
|
|
1073
|
+
modelpath = os.sep.join([main_dir, "models", "pair_signal_detection", os.sep])
|
|
1074
|
+
print(f'Looking for {name} in {modelpath}')
|
|
1075
|
+
models = glob(modelpath + f'*{os.sep}')
|
|
1076
|
+
if path is not None:
|
|
1077
|
+
if not path.endswith(os.sep):
|
|
1078
|
+
path += os.sep
|
|
1079
|
+
models += glob(path + f'*{os.sep}')
|
|
996
1080
|
|
|
997
1081
|
def relabel_segmentation(labels, data, properties, column_labels={'track': "track", 'frame': 'frame', 'y': 'y', 'x': 'x', 'label': 'class_id'}, threads=1):
|
|
998
1082
|
|
|
@@ -1034,7 +1118,11 @@ def relabel_segmentation(labels, data, properties, column_labels={'track': "trac
|
|
|
1034
1118
|
|
|
1035
1119
|
|
|
1036
1120
|
n_threads = threads
|
|
1037
|
-
|
|
1121
|
+
if data.shape[1]==4:
|
|
1122
|
+
df = pd.DataFrame(data,columns=[column_labels['track'],column_labels['frame'],column_labels['y'],column_labels['x']])
|
|
1123
|
+
else:
|
|
1124
|
+
df = pd.DataFrame(data,columns=[column_labels['track'],column_labels['frame'],'z', column_labels['y'],column_labels['x']])
|
|
1125
|
+
df = df.drop(columns=['z'])
|
|
1038
1126
|
df = df.merge(pd.DataFrame(properties),left_index=True, right_index=True)
|
|
1039
1127
|
df = df.sort_values(by=[column_labels['track'],column_labels['frame']])
|
|
1040
1128
|
|
|
@@ -1044,15 +1132,15 @@ def relabel_segmentation(labels, data, properties, column_labels={'track': "trac
|
|
|
1044
1132
|
|
|
1045
1133
|
for t in tqdm(indices):
|
|
1046
1134
|
f = int(t)
|
|
1047
|
-
tracks_at_t = df.loc[df[column_labels['frame']]==f, column_labels['track']].to_numpy()
|
|
1048
|
-
identities = df.loc[df[column_labels['frame']]==f, column_labels['label']].to_numpy()
|
|
1135
|
+
tracks_at_t = df.loc[df[column_labels['frame']] == f, column_labels['track']].to_numpy()
|
|
1136
|
+
identities = df.loc[df[column_labels['frame']] == f, column_labels['label']].to_numpy()
|
|
1049
1137
|
|
|
1050
|
-
tracks_at_t = tracks_at_t[identities==identities]
|
|
1051
|
-
identities = identities[identities==identities]
|
|
1138
|
+
tracks_at_t = tracks_at_t[identities == identities]
|
|
1139
|
+
identities = identities[identities == identities]
|
|
1052
1140
|
|
|
1053
1141
|
for k in range(len(identities)):
|
|
1054
|
-
loc_i,loc_j = np.where(labels[f]==identities[k])
|
|
1055
|
-
new_labels[f,loc_i,loc_j] = int(tracks_at_t[k])
|
|
1142
|
+
loc_i, loc_j = np.where(labels[f] == identities[k])
|
|
1143
|
+
new_labels[f, loc_i, loc_j] = int(tracks_at_t[k])
|
|
1056
1144
|
|
|
1057
1145
|
# Multithreading
|
|
1058
1146
|
indices = list(df[column_labels['frame']].unique())
|
|
@@ -1152,11 +1240,13 @@ def control_tracking_btrack(position, prefix="Aligned", population="target", rel
|
|
|
1152
1240
|
|
|
1153
1241
|
"""
|
|
1154
1242
|
|
|
1155
|
-
data,properties,graph,labels,stack = load_napari_data(position, prefix=prefix, population=population)
|
|
1156
|
-
view_on_napari_btrack(data,properties,graph,labels=labels, stack=stack, relabel=relabel,
|
|
1243
|
+
data, properties, graph, labels, stack = load_napari_data(position, prefix=prefix, population=population)
|
|
1244
|
+
view_on_napari_btrack(data, properties, graph, labels=labels, stack=stack, relabel=relabel,
|
|
1245
|
+
flush_memory=flush_memory, threads=threads)
|
|
1157
1246
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1247
|
+
|
|
1248
|
+
def view_on_napari_btrack(data, properties, graph, stack=None, labels=None, relabel=True, flush_memory=True,
|
|
1249
|
+
position=None, threads=1):
|
|
1160
1250
|
"""
|
|
1161
1251
|
|
|
1162
1252
|
Visualize btrack data, including stack, labels, points, and tracks, using the napari viewer.
|
|
@@ -1189,20 +1279,28 @@ def view_on_napari_btrack(data,properties,graph,stack=None,labels=None,relabel=T
|
|
|
1189
1279
|
|
|
1190
1280
|
"""
|
|
1191
1281
|
|
|
1192
|
-
if (labels is not None)*relabel:
|
|
1282
|
+
if (labels is not None) * relabel:
|
|
1193
1283
|
print('Relabeling the cell masks with the track ID.')
|
|
1194
1284
|
labels = relabel_segmentation(labels, data, properties, threads=threads)
|
|
1195
1285
|
|
|
1196
|
-
|
|
1286
|
+
if data.shape[1]==4:
|
|
1287
|
+
vertices = data[:, 1:]
|
|
1288
|
+
else:
|
|
1289
|
+
vertices = data[:, 2:]
|
|
1197
1290
|
viewer = napari.Viewer()
|
|
1198
1291
|
if stack is not None:
|
|
1199
|
-
|
|
1292
|
+
print(f'{stack.shape=}')
|
|
1293
|
+
viewer.add_image(stack, channel_axis=-1, colormap=["gray"] * stack.shape[-1])
|
|
1200
1294
|
if labels is not None:
|
|
1201
|
-
viewer.add_labels(labels, name='segmentation',opacity=0.4)
|
|
1295
|
+
viewer.add_labels(labels, name='segmentation', opacity=0.4)
|
|
1202
1296
|
viewer.add_points(vertices, size=4, name='points', opacity=0.3)
|
|
1203
|
-
|
|
1297
|
+
if data.shape[1]==4:
|
|
1298
|
+
viewer.add_tracks(data, properties=properties, graph=graph, name='tracks')
|
|
1299
|
+
else:
|
|
1300
|
+
print(data)
|
|
1301
|
+
viewer.add_tracks(data[:,[0,1,3,4]], properties=properties, graph=graph, name='tracks')
|
|
1204
1302
|
viewer.show(block=True)
|
|
1205
|
-
|
|
1303
|
+
|
|
1206
1304
|
if flush_memory:
|
|
1207
1305
|
# temporary fix for slight napari memory leak
|
|
1208
1306
|
for i in range(10000):
|
|
@@ -1216,8 +1314,8 @@ def view_on_napari_btrack(data,properties,graph,stack=None,labels=None,relabel=T
|
|
|
1216
1314
|
del labels
|
|
1217
1315
|
gc.collect()
|
|
1218
1316
|
|
|
1219
|
-
def load_napari_data(position, prefix="Aligned", population="target", return_stack=True):
|
|
1220
1317
|
|
|
1318
|
+
def load_napari_data(position, prefix="Aligned", population="target", return_stack=True):
|
|
1221
1319
|
"""
|
|
1222
1320
|
Load the necessary data for visualization in napari.
|
|
1223
1321
|
|
|
@@ -1239,7 +1337,7 @@ def load_napari_data(position, prefix="Aligned", population="target", return_sta
|
|
|
1239
1337
|
--------
|
|
1240
1338
|
>>> data, properties, graph, labels, stack = load_napari_data("path/to/position")
|
|
1241
1339
|
# Load the necessary data for visualization of target trajectories.
|
|
1242
|
-
|
|
1340
|
+
|
|
1243
1341
|
"""
|
|
1244
1342
|
position = position.replace('\\','/')
|
|
1245
1343
|
if population.lower()=="target" or population.lower()=="targets":
|
|
@@ -1261,14 +1359,16 @@ def load_napari_data(position, prefix="Aligned", population="target", return_sta
|
|
|
1261
1359
|
properties = None
|
|
1262
1360
|
graph = None
|
|
1263
1361
|
if return_stack:
|
|
1264
|
-
stack,labels = locate_stack_and_labels(position, prefix=prefix, population=population)
|
|
1362
|
+
stack, labels = locate_stack_and_labels(position, prefix=prefix, population=population)
|
|
1265
1363
|
else:
|
|
1266
|
-
labels=locate_labels(position,population=population)
|
|
1364
|
+
labels = locate_labels(position, population=population)
|
|
1267
1365
|
stack = None
|
|
1268
|
-
return data,properties,graph,labels,stack
|
|
1366
|
+
return data, properties, graph, labels, stack
|
|
1367
|
+
|
|
1269
1368
|
|
|
1270
1369
|
from skimage.measure import label
|
|
1271
1370
|
|
|
1371
|
+
|
|
1272
1372
|
def auto_correct_masks(masks):
|
|
1273
1373
|
|
|
1274
1374
|
"""
|
|
@@ -1300,25 +1400,25 @@ def auto_correct_masks(masks):
|
|
|
1300
1400
|
|
|
1301
1401
|
"""
|
|
1302
1402
|
|
|
1303
|
-
props = pd.DataFrame(regionprops_table(masks,properties=('label','area','area_bbox')))
|
|
1403
|
+
props = pd.DataFrame(regionprops_table(masks, properties=('label', 'area', 'area_bbox')))
|
|
1304
1404
|
max_lbl = props['label'].max()
|
|
1305
1405
|
corrected_lbl = masks.copy().astype(int)
|
|
1306
1406
|
|
|
1307
1407
|
for cell in props['label'].unique():
|
|
1308
1408
|
|
|
1309
|
-
bbox_area = props.loc[props['label']==cell, 'area_bbox'].values
|
|
1310
|
-
area = props.loc[props['label']==cell, 'area'].values
|
|
1409
|
+
bbox_area = props.loc[props['label'] == cell, 'area_bbox'].values
|
|
1410
|
+
area = props.loc[props['label'] == cell, 'area'].values
|
|
1311
1411
|
|
|
1312
|
-
if bbox_area > 1.75*area:
|
|
1412
|
+
if bbox_area > 1.75 * area: # condition for anomaly
|
|
1313
1413
|
|
|
1314
|
-
lbl = masks==cell
|
|
1414
|
+
lbl = masks == cell
|
|
1315
1415
|
lbl = lbl.astype(int)
|
|
1316
1416
|
|
|
1317
|
-
relabelled = label(lbl,connectivity=2)
|
|
1417
|
+
relabelled = label(lbl, connectivity=2)
|
|
1318
1418
|
relabelled += max_lbl
|
|
1319
|
-
relabelled[np.where(lbl==0)] = 0
|
|
1419
|
+
relabelled[np.where(lbl == 0)] = 0
|
|
1320
1420
|
|
|
1321
|
-
corrected_lbl[np.where(relabelled != 0)] = relabelled[np.where(relabelled!=0)]
|
|
1421
|
+
corrected_lbl[np.where(relabelled != 0)] = relabelled[np.where(relabelled != 0)]
|
|
1322
1422
|
|
|
1323
1423
|
max_lbl = np.amax(corrected_lbl)
|
|
1324
1424
|
|
|
@@ -1329,7 +1429,7 @@ def auto_correct_masks(masks):
|
|
|
1329
1429
|
def control_segmentation_napari(position, prefix='Aligned', population="target", flush_memory=False):
|
|
1330
1430
|
|
|
1331
1431
|
"""
|
|
1332
|
-
|
|
1432
|
+
|
|
1333
1433
|
Control the visualization of segmentation labels using the napari viewer.
|
|
1334
1434
|
|
|
1335
1435
|
Parameters
|
|
@@ -1355,22 +1455,22 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
|
|
|
1355
1455
|
|
|
1356
1456
|
def export_labels():
|
|
1357
1457
|
labels_layer = viewer.layers['segmentation'].data
|
|
1358
|
-
for t,im in enumerate(tqdm(labels_layer)):
|
|
1359
|
-
|
|
1458
|
+
for t, im in enumerate(tqdm(labels_layer)):
|
|
1459
|
+
|
|
1360
1460
|
try:
|
|
1361
1461
|
im = auto_correct_masks(im)
|
|
1362
1462
|
except Exception as e:
|
|
1363
1463
|
print(e)
|
|
1364
1464
|
|
|
1365
|
-
save_tiff_imagej_compatible(output_folder+f"{str(t).zfill(4)}.tif", im.astype(np.int16), axes='YX')
|
|
1465
|
+
save_tiff_imagej_compatible(output_folder + f"{str(t).zfill(4)}.tif", im.astype(np.int16), axes='YX')
|
|
1366
1466
|
print("The labels have been successfully rewritten.")
|
|
1367
1467
|
|
|
1368
1468
|
def export_annotation():
|
|
1369
|
-
|
|
1469
|
+
|
|
1370
1470
|
# Locate experiment config
|
|
1371
1471
|
parent1 = Path(position).parent
|
|
1372
1472
|
expfolder = parent1.parent
|
|
1373
|
-
config = PurePath(expfolder,Path("config.ini"))
|
|
1473
|
+
config = PurePath(expfolder, Path("config.ini"))
|
|
1374
1474
|
expfolder = str(expfolder)
|
|
1375
1475
|
exp_name = os.path.split(expfolder)[-1]
|
|
1376
1476
|
print(exp_name)
|
|
@@ -1392,7 +1492,7 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
|
|
|
1392
1492
|
|
|
1393
1493
|
print('exporting!')
|
|
1394
1494
|
t = viewer.dims.current_step[0]
|
|
1395
|
-
labels_layer = viewer.layers['segmentation'].data[t]
|
|
1495
|
+
labels_layer = viewer.layers['segmentation'].data[t] # at current time
|
|
1396
1496
|
|
|
1397
1497
|
try:
|
|
1398
1498
|
labels_layer = auto_correct_masks(labels_layer)
|
|
@@ -1400,37 +1500,37 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
|
|
|
1400
1500
|
print(e)
|
|
1401
1501
|
|
|
1402
1502
|
fov_export = True
|
|
1403
|
-
|
|
1503
|
+
|
|
1404
1504
|
if "Shapes" in viewer.layers:
|
|
1405
1505
|
squares = viewer.layers['Shapes'].data
|
|
1406
|
-
test_in_frame = np.array([squares[i][0,0]==t and len(squares[i])==4 for i in range(len(squares))])
|
|
1506
|
+
test_in_frame = np.array([squares[i][0, 0] == t and len(squares[i]) == 4 for i in range(len(squares))])
|
|
1407
1507
|
squares = np.array(squares)
|
|
1408
1508
|
squares = squares[test_in_frame]
|
|
1409
1509
|
nbr_squares = len(squares)
|
|
1410
1510
|
print(f"Found {nbr_squares} ROIS")
|
|
1411
|
-
if nbr_squares>0:
|
|
1511
|
+
if nbr_squares > 0:
|
|
1412
1512
|
# deactivate field of view mode
|
|
1413
1513
|
fov_export = False
|
|
1414
1514
|
|
|
1415
|
-
for k,sq in enumerate(squares):
|
|
1515
|
+
for k, sq in enumerate(squares):
|
|
1416
1516
|
print(f"ROI: {sq}")
|
|
1417
|
-
xmin = int(sq[0,1])
|
|
1418
|
-
xmax = int(sq[2,1])
|
|
1419
|
-
if xmax<xmin:
|
|
1420
|
-
xmax,xmin = xmin,xmax
|
|
1421
|
-
ymin = int(sq[0,2])
|
|
1422
|
-
ymax = int(sq[1,2])
|
|
1423
|
-
if ymax<ymin:
|
|
1424
|
-
ymax,ymin = ymin,ymax
|
|
1517
|
+
xmin = int(sq[0, 1])
|
|
1518
|
+
xmax = int(sq[2, 1])
|
|
1519
|
+
if xmax < xmin:
|
|
1520
|
+
xmax, xmin = xmin, xmax
|
|
1521
|
+
ymin = int(sq[0, 2])
|
|
1522
|
+
ymax = int(sq[1, 2])
|
|
1523
|
+
if ymax < ymin:
|
|
1524
|
+
ymax, ymin = ymin, ymax
|
|
1425
1525
|
print(f"{xmin=};{xmax=};{ymin=};{ymax=}")
|
|
1426
|
-
frame = viewer.layers['Image'].data[t][xmin:xmax,ymin:ymax]
|
|
1526
|
+
frame = viewer.layers['Image'].data[t][xmin:xmax, ymin:ymax]
|
|
1427
1527
|
if frame.shape[1] < 256 or frame.shape[0] < 256:
|
|
1428
1528
|
print("crop too small!")
|
|
1429
1529
|
continue
|
|
1430
1530
|
multichannel = [frame]
|
|
1431
|
-
for i in range(len(channel_indices)-1):
|
|
1531
|
+
for i in range(len(channel_indices) - 1):
|
|
1432
1532
|
try:
|
|
1433
|
-
frame = viewer.layers[f'Image [{i+1}]'].data[t][xmin:xmax,ymin:ymax]
|
|
1533
|
+
frame = viewer.layers[f'Image [{i + 1}]'].data[t][xmin:xmax, ymin:ymax]
|
|
1434
1534
|
multichannel.append(frame)
|
|
1435
1535
|
except:
|
|
1436
1536
|
pass
|
|
@@ -1441,13 +1541,13 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
|
|
|
1441
1541
|
info_name = annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}_roi_{xmin}_{xmax}_{ymin}_{ymax}.json"
|
|
1442
1542
|
with open(info_name, 'w') as f:
|
|
1443
1543
|
json.dump(info, f, indent=4)
|
|
1444
|
-
|
|
1544
|
+
|
|
1445
1545
|
if fov_export:
|
|
1446
1546
|
frame = viewer.layers['Image'].data[t]
|
|
1447
1547
|
multichannel = [frame]
|
|
1448
|
-
for i in range(len(channel_indices)-1):
|
|
1548
|
+
for i in range(len(channel_indices) - 1):
|
|
1449
1549
|
try:
|
|
1450
|
-
frame = viewer.layers[f'Image [{i+1}]'].data[t]
|
|
1550
|
+
frame = viewer.layers[f'Image [{i + 1}]'].data[t]
|
|
1451
1551
|
multichannel.append(frame)
|
|
1452
1552
|
except:
|
|
1453
1553
|
pass
|
|
@@ -1468,15 +1568,15 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
|
|
|
1468
1568
|
def export_widget():
|
|
1469
1569
|
return export_annotation()
|
|
1470
1570
|
|
|
1471
|
-
stack,labels = locate_stack_and_labels(position, prefix=prefix, population=population)
|
|
1571
|
+
stack, labels = locate_stack_and_labels(position, prefix=prefix, population=population)
|
|
1472
1572
|
|
|
1473
1573
|
if not population.endswith('s'):
|
|
1474
|
-
population+='s'
|
|
1475
|
-
output_folder = position+f'labels_{population}{os.sep}'
|
|
1574
|
+
population += 's'
|
|
1575
|
+
output_folder = position + f'labels_{population}{os.sep}'
|
|
1476
1576
|
|
|
1477
1577
|
viewer = napari.Viewer()
|
|
1478
|
-
viewer.add_image(stack,channel_axis=-1,colormap=["gray"]*stack.shape[-1])
|
|
1479
|
-
viewer.add_labels(labels.astype(int), name='segmentation',opacity=0.4)
|
|
1578
|
+
viewer.add_image(stack, channel_axis=-1, colormap=["gray"] * stack.shape[-1])
|
|
1579
|
+
viewer.add_labels(labels.astype(int), name='segmentation', opacity=0.4)
|
|
1480
1580
|
viewer.window.add_dock_widget(save_widget, area='right')
|
|
1481
1581
|
viewer.window.add_dock_widget(export_widget, area='right')
|
|
1482
1582
|
viewer.show(block=True)
|
|
@@ -1499,7 +1599,7 @@ def correct_annotation(filename):
|
|
|
1499
1599
|
"""
|
|
1500
1600
|
New function to reannotate an annotation image in post, using napari and save update inplace.
|
|
1501
1601
|
"""
|
|
1502
|
-
|
|
1602
|
+
|
|
1503
1603
|
def export_labels():
|
|
1504
1604
|
labels_layer = viewer.layers['segmentation'].data
|
|
1505
1605
|
for t,im in enumerate(tqdm(labels_layer)):
|
|
@@ -1515,26 +1615,26 @@ def correct_annotation(filename):
|
|
|
1515
1615
|
@magicgui(call_button='Save the modified labels')
|
|
1516
1616
|
def save_widget():
|
|
1517
1617
|
return export_labels()
|
|
1518
|
-
|
|
1519
|
-
img = imread(filename.replace('\\','/'))
|
|
1618
|
+
|
|
1619
|
+
img = imread(filename.replace('\\','/'),is_mmstack=False)
|
|
1520
1620
|
if img.ndim==3:
|
|
1521
1621
|
img = np.moveaxis(img, 0, -1)
|
|
1522
1622
|
elif img.ndim==2:
|
|
1523
1623
|
img = img[:,:,np.newaxis]
|
|
1524
|
-
|
|
1624
|
+
|
|
1525
1625
|
existing_lbl = filename.replace('.tif','_labelled.tif')
|
|
1526
1626
|
if os.path.exists(existing_lbl):
|
|
1527
1627
|
labels = imread(existing_lbl)[np.newaxis,:,:].astype(int)
|
|
1528
1628
|
else:
|
|
1529
1629
|
labels = np.zeros_like(img[:,:,0]).astype(int)[np.newaxis,:,:]
|
|
1530
|
-
|
|
1630
|
+
|
|
1531
1631
|
stack = img[np.newaxis,:,:,:]
|
|
1532
|
-
|
|
1632
|
+
|
|
1533
1633
|
viewer = napari.Viewer()
|
|
1534
1634
|
viewer.add_image(stack,channel_axis=-1,colormap=["gray"]*stack.shape[-1])
|
|
1535
1635
|
viewer.add_labels(labels, name='segmentation',opacity=0.4)
|
|
1536
1636
|
viewer.window.add_dock_widget(save_widget, area='right')
|
|
1537
|
-
viewer.show(block=True)
|
|
1637
|
+
viewer.show(block=True)
|
|
1538
1638
|
|
|
1539
1639
|
# temporary fix for slight napari memory leak
|
|
1540
1640
|
for i in range(100):
|
|
@@ -1582,23 +1682,24 @@ def _view_on_napari(tracks=None, stack=None, labels=None):
|
|
|
1582
1682
|
>>> labels = np.random.randint(0, 2, (100, 100))
|
|
1583
1683
|
>>> view_on_napari(tracks, stack=stack, labels=labels)
|
|
1584
1684
|
# Visualize tracks, stack, and labels using Napari.
|
|
1585
|
-
|
|
1685
|
+
|
|
1586
1686
|
"""
|
|
1587
1687
|
|
|
1588
1688
|
viewer = napari.Viewer()
|
|
1589
1689
|
if stack is not None:
|
|
1590
|
-
viewer.add_image(stack,channel_axis=-1,colormap=["gray"]*stack.shape[-1])
|
|
1690
|
+
viewer.add_image(stack, channel_axis=-1, colormap=["gray"] * stack.shape[-1])
|
|
1591
1691
|
if labels is not None:
|
|
1592
|
-
viewer.add_labels(labels, name='segmentation',opacity=0.4)
|
|
1692
|
+
viewer.add_labels(labels, name='segmentation', opacity=0.4)
|
|
1593
1693
|
if tracks is not None:
|
|
1594
1694
|
viewer.add_tracks(tracks, name='tracks')
|
|
1595
1695
|
viewer.show(block=True)
|
|
1596
1696
|
|
|
1597
|
-
def control_tracking_table(position, calibration=1, prefix="Aligned", population="target",
|
|
1598
|
-
column_labels={'track': "TRACK_ID", 'frame': 'FRAME', 'y': 'POSITION_Y', 'x': 'POSITION_X', 'label': 'class_id'}):
|
|
1599
1697
|
|
|
1698
|
+
def control_tracking_table(position, calibration=1, prefix="Aligned", population="target",
|
|
1699
|
+
column_labels={'track': "TRACK_ID", 'frame': 'FRAME', 'y': 'POSITION_Y', 'x': 'POSITION_X',
|
|
1700
|
+
'label': 'class_id'}):
|
|
1600
1701
|
"""
|
|
1601
|
-
|
|
1702
|
+
|
|
1602
1703
|
Control the tracking table and visualize tracks using Napari.
|
|
1603
1704
|
|
|
1604
1705
|
Parameters
|
|
@@ -1633,27 +1734,33 @@ def control_tracking_table(position, calibration=1, prefix="Aligned", population
|
|
|
1633
1734
|
|
|
1634
1735
|
"""
|
|
1635
1736
|
|
|
1636
|
-
position = position.replace('\\','/')
|
|
1637
|
-
tracks,labels,stack = load_tracking_data(position, prefix=prefix, population=population)
|
|
1638
|
-
tracks = tracks.loc[:,
|
|
1639
|
-
|
|
1640
|
-
|
|
1737
|
+
position = position.replace('\\', '/')
|
|
1738
|
+
tracks, labels, stack = load_tracking_data(position, prefix=prefix, population=population)
|
|
1739
|
+
tracks = tracks.loc[:,
|
|
1740
|
+
[column_labels['track'], column_labels['frame'], column_labels['y'], column_labels['x']]].to_numpy()
|
|
1741
|
+
tracks[:, -2:] /= calibration
|
|
1742
|
+
_view_on_napari(tracks, labels=labels, stack=stack)
|
|
1641
1743
|
|
|
1642
1744
|
|
|
1643
1745
|
def get_segmentation_models_list(mode='targets', return_path=False):
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
repository_models = get_zenodo_files(cat=os.sep.join(["models","
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1746
|
+
if mode == 'targets':
|
|
1747
|
+
modelpath = os.sep.join(
|
|
1748
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models",
|
|
1749
|
+
"segmentation_targets", os.sep])
|
|
1750
|
+
repository_models = get_zenodo_files(cat=os.sep.join(["models", "segmentation_targets"]))
|
|
1751
|
+
elif mode == 'effectors':
|
|
1752
|
+
modelpath = os.sep.join(
|
|
1753
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models",
|
|
1754
|
+
"segmentation_effectors", os.sep])
|
|
1755
|
+
repository_models = get_zenodo_files(cat=os.sep.join(["models", "segmentation_effectors"]))
|
|
1756
|
+
elif mode == 'generic':
|
|
1757
|
+
modelpath = os.sep.join(
|
|
1758
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "models",
|
|
1759
|
+
"segmentation_generic", os.sep])
|
|
1760
|
+
repository_models = get_zenodo_files(cat=os.sep.join(["models", "segmentation_generic"]))
|
|
1761
|
+
|
|
1762
|
+
available_models = natsorted(glob(modelpath + '*/'))
|
|
1763
|
+
available_models = [m.replace('\\', '/').split('/')[-2] for m in available_models]
|
|
1657
1764
|
for rm in repository_models:
|
|
1658
1765
|
if rm not in available_models:
|
|
1659
1766
|
available_models.append(rm)
|
|
@@ -1663,16 +1770,17 @@ def get_segmentation_models_list(mode='targets', return_path=False):
|
|
|
1663
1770
|
else:
|
|
1664
1771
|
return available_models, modelpath
|
|
1665
1772
|
|
|
1773
|
+
|
|
1666
1774
|
def locate_segmentation_model(name):
|
|
1667
1775
|
|
|
1668
1776
|
"""
|
|
1669
|
-
Locates a specified segmentation model within the local 'celldetective' directory or
|
|
1777
|
+
Locates a specified segmentation model within the local 'celldetective' directory or
|
|
1670
1778
|
downloads it from Zenodo if not found locally.
|
|
1671
1779
|
|
|
1672
|
-
This function attempts to find a segmentation model by name within a predefined directory
|
|
1673
|
-
structure starting from the 'celldetective/models/segmentation*' path. If the model is not
|
|
1674
|
-
found locally, it then tries to locate and download the model from Zenodo, placing it into
|
|
1675
|
-
the appropriate category directory within 'celldetective'. The function prints the search
|
|
1780
|
+
This function attempts to find a segmentation model by name within a predefined directory
|
|
1781
|
+
structure starting from the 'celldetective/models/segmentation*' path. If the model is not
|
|
1782
|
+
found locally, it then tries to locate and download the model from Zenodo, placing it into
|
|
1783
|
+
the appropriate category directory within 'celldetective'. The function prints the search
|
|
1676
1784
|
directory path and returns the path to the found or downloaded model.
|
|
1677
1785
|
|
|
1678
1786
|
Parameters
|
|
@@ -1683,7 +1791,7 @@ def locate_segmentation_model(name):
|
|
|
1683
1791
|
Returns
|
|
1684
1792
|
-------
|
|
1685
1793
|
str or None
|
|
1686
|
-
The full path to the located or downloaded segmentation model directory, or None if the
|
|
1794
|
+
The full path to the located or downloaded segmentation model directory, or None if the
|
|
1687
1795
|
model could not be found or downloaded.
|
|
1688
1796
|
|
|
1689
1797
|
Raises
|
|
@@ -1696,11 +1804,11 @@ def locate_segmentation_model(name):
|
|
|
1696
1804
|
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],"celldetective"])
|
|
1697
1805
|
modelpath = os.sep.join([main_dir, "models", "segmentation*"]) + os.sep
|
|
1698
1806
|
print(f'Looking for {name} in {modelpath}')
|
|
1699
|
-
models = glob(modelpath+f'*{os.sep}')
|
|
1807
|
+
models = glob(modelpath + f'*{os.sep}')
|
|
1700
1808
|
|
|
1701
|
-
match=None
|
|
1809
|
+
match = None
|
|
1702
1810
|
for m in models:
|
|
1703
|
-
if name==m.replace('\\',os.sep).split(os.sep)[-2]:
|
|
1811
|
+
if name == m.replace('\\', os.sep).split(os.sep)[-2]:
|
|
1704
1812
|
match = m
|
|
1705
1813
|
return match
|
|
1706
1814
|
# else no match, try zenodo
|
|
@@ -1709,42 +1817,42 @@ def locate_segmentation_model(name):
|
|
|
1709
1817
|
index = files.index(name)
|
|
1710
1818
|
cat = categories[index]
|
|
1711
1819
|
download_zenodo_file(name, os.sep.join([main_dir, cat]))
|
|
1712
|
-
match = os.sep.join([main_dir, cat, name])+os.sep
|
|
1820
|
+
match = os.sep.join([main_dir, cat, name]) + os.sep
|
|
1713
1821
|
return match
|
|
1714
1822
|
|
|
1715
1823
|
|
|
1716
1824
|
def get_segmentation_datasets_list(return_path=False):
|
|
1717
|
-
|
|
1718
1825
|
"""
|
|
1719
|
-
Retrieves a list of available segmentation datasets from both the local 'celldetective/datasets/segmentation_annotations'
|
|
1826
|
+
Retrieves a list of available segmentation datasets from both the local 'celldetective/datasets/segmentation_annotations'
|
|
1720
1827
|
directory and a Zenodo repository, optionally returning the path to the local datasets directory.
|
|
1721
1828
|
|
|
1722
|
-
This function compiles a list of available segmentation datasets by first identifying datasets stored locally
|
|
1723
|
-
within a specified path related to the script's directory. It then extends this list with datasets available
|
|
1724
|
-
in a Zenodo repository, ensuring no duplicates are added. The function can return just the list of dataset
|
|
1829
|
+
This function compiles a list of available segmentation datasets by first identifying datasets stored locally
|
|
1830
|
+
within a specified path related to the script's directory. It then extends this list with datasets available
|
|
1831
|
+
in a Zenodo repository, ensuring no duplicates are added. The function can return just the list of dataset
|
|
1725
1832
|
names or, if specified, also return the path to the local datasets directory.
|
|
1726
1833
|
|
|
1727
1834
|
Parameters
|
|
1728
1835
|
----------
|
|
1729
1836
|
return_path : bool, optional
|
|
1730
|
-
If True, the function returns a tuple containing the list of available dataset names and the path to the
|
|
1837
|
+
If True, the function returns a tuple containing the list of available dataset names and the path to the
|
|
1731
1838
|
local datasets directory. If False, only the list of dataset names is returned (default is False).
|
|
1732
1839
|
|
|
1733
1840
|
Returns
|
|
1734
1841
|
-------
|
|
1735
1842
|
list or (list, str)
|
|
1736
|
-
If return_path is False, returns a list of strings, each string being the name of an available dataset.
|
|
1737
|
-
If return_path is True, returns a tuple where the first element is this list and the second element is a
|
|
1843
|
+
If return_path is False, returns a list of strings, each string being the name of an available dataset.
|
|
1844
|
+
If return_path is True, returns a tuple where the first element is this list and the second element is a
|
|
1738
1845
|
string representing the path to the local datasets directory.
|
|
1739
|
-
|
|
1846
|
+
|
|
1740
1847
|
"""
|
|
1741
1848
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1849
|
+
datasets_path = os.sep.join(
|
|
1850
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "datasets",
|
|
1851
|
+
"segmentation_annotations", os.sep])
|
|
1852
|
+
repository_datasets = get_zenodo_files(cat=os.sep.join(["datasets", "segmentation_annotations"]))
|
|
1745
1853
|
|
|
1746
|
-
available_datasets = natsorted(glob(datasets_path+'*/'))
|
|
1747
|
-
available_datasets = [m.replace('\\','/').split('/')[-2] for m in available_datasets]
|
|
1854
|
+
available_datasets = natsorted(glob(datasets_path + '*/'))
|
|
1855
|
+
available_datasets = [m.replace('\\', '/').split('/')[-2] for m in available_datasets]
|
|
1748
1856
|
for rm in repository_datasets:
|
|
1749
1857
|
if rm not in available_datasets:
|
|
1750
1858
|
available_datasets.append(rm)
|
|
@@ -1759,12 +1867,12 @@ def get_segmentation_datasets_list(return_path=False):
|
|
|
1759
1867
|
def locate_segmentation_dataset(name):
|
|
1760
1868
|
|
|
1761
1869
|
"""
|
|
1762
|
-
Locates a specified segmentation dataset within the local 'celldetective/datasets/segmentation_annotations' directory
|
|
1870
|
+
Locates a specified segmentation dataset within the local 'celldetective/datasets/segmentation_annotations' directory
|
|
1763
1871
|
or downloads it from Zenodo if not found locally.
|
|
1764
1872
|
|
|
1765
|
-
This function attempts to find a segmentation dataset by name within a predefined directory structure. If the dataset
|
|
1766
|
-
is not found locally, it then tries to locate and download the dataset from Zenodo, placing it into the appropriate
|
|
1767
|
-
category directory within 'celldetective'. The function prints the search directory path and returns the path to the
|
|
1873
|
+
This function attempts to find a segmentation dataset by name within a predefined directory structure. If the dataset
|
|
1874
|
+
is not found locally, it then tries to locate and download the dataset from Zenodo, placing it into the appropriate
|
|
1875
|
+
category directory within 'celldetective'. The function prints the search directory path and returns the path to the
|
|
1768
1876
|
found or downloaded dataset.
|
|
1769
1877
|
|
|
1770
1878
|
Parameters
|
|
@@ -1775,24 +1883,24 @@ def locate_segmentation_dataset(name):
|
|
|
1775
1883
|
Returns
|
|
1776
1884
|
-------
|
|
1777
1885
|
str or None
|
|
1778
|
-
The full path to the located or downloaded segmentation dataset directory, or None if the dataset could not be
|
|
1886
|
+
The full path to the located or downloaded segmentation dataset directory, or None if the dataset could not be
|
|
1779
1887
|
found or downloaded.
|
|
1780
1888
|
|
|
1781
1889
|
Raises
|
|
1782
1890
|
------
|
|
1783
1891
|
FileNotFoundError
|
|
1784
1892
|
If the dataset cannot be found locally and also cannot be found or downloaded from Zenodo.
|
|
1785
|
-
|
|
1893
|
+
|
|
1786
1894
|
"""
|
|
1787
1895
|
|
|
1788
|
-
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],"celldetective"])
|
|
1896
|
+
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective"])
|
|
1789
1897
|
modelpath = os.sep.join([main_dir, "datasets", "segmentation_annotations", os.sep])
|
|
1790
1898
|
print(f'Looking for {name} in {modelpath}')
|
|
1791
|
-
models = glob(modelpath+f'*{os.sep}')
|
|
1899
|
+
models = glob(modelpath + f'*{os.sep}')
|
|
1792
1900
|
|
|
1793
|
-
match=None
|
|
1901
|
+
match = None
|
|
1794
1902
|
for m in models:
|
|
1795
|
-
if name==m.replace('\\',os.sep).split(os.sep)[-2]:
|
|
1903
|
+
if name == m.replace('\\', os.sep).split(os.sep)[-2]:
|
|
1796
1904
|
match = m
|
|
1797
1905
|
return match
|
|
1798
1906
|
# else no match, try zenodo
|
|
@@ -1801,41 +1909,43 @@ def locate_segmentation_dataset(name):
|
|
|
1801
1909
|
index = files.index(name)
|
|
1802
1910
|
cat = categories[index]
|
|
1803
1911
|
download_zenodo_file(name, os.sep.join([main_dir, cat]))
|
|
1804
|
-
match = os.sep.join([main_dir, cat, name])+os.sep
|
|
1912
|
+
match = os.sep.join([main_dir, cat, name]) + os.sep
|
|
1805
1913
|
return match
|
|
1806
1914
|
|
|
1807
1915
|
|
|
1808
1916
|
def get_signal_datasets_list(return_path=False):
|
|
1809
1917
|
|
|
1810
1918
|
"""
|
|
1811
|
-
Retrieves a list of available signal datasets from both the local 'celldetective/datasets/signal_annotations' directory
|
|
1919
|
+
Retrieves a list of available signal datasets from both the local 'celldetective/datasets/signal_annotations' directory
|
|
1812
1920
|
and a Zenodo repository, optionally returning the path to the local datasets directory.
|
|
1813
1921
|
|
|
1814
|
-
This function compiles a list of available signal datasets by first identifying datasets stored locally within a specified
|
|
1815
|
-
path related to the script's directory. It then extends this list with datasets available in a Zenodo repository, ensuring
|
|
1816
|
-
no duplicates are added. The function can return just the list of dataset names or, if specified, also return the path to
|
|
1922
|
+
This function compiles a list of available signal datasets by first identifying datasets stored locally within a specified
|
|
1923
|
+
path related to the script's directory. It then extends this list with datasets available in a Zenodo repository, ensuring
|
|
1924
|
+
no duplicates are added. The function can return just the list of dataset names or, if specified, also return the path to
|
|
1817
1925
|
the local datasets directory.
|
|
1818
1926
|
|
|
1819
1927
|
Parameters
|
|
1820
1928
|
----------
|
|
1821
1929
|
return_path : bool, optional
|
|
1822
|
-
If True, the function returns a tuple containing the list of available dataset names and the path to the local datasets
|
|
1930
|
+
If True, the function returns a tuple containing the list of available dataset names and the path to the local datasets
|
|
1823
1931
|
directory. If False, only the list of dataset names is returned (default is False).
|
|
1824
1932
|
|
|
1825
1933
|
Returns
|
|
1826
1934
|
-------
|
|
1827
1935
|
list or (list, str)
|
|
1828
|
-
If return_path is False, returns a list of strings, each string being the name of an available dataset. If return_path
|
|
1829
|
-
is True, returns a tuple where the first element is this list and the second element is a string representing the path
|
|
1936
|
+
If return_path is False, returns a list of strings, each string being the name of an available dataset. If return_path
|
|
1937
|
+
is True, returns a tuple where the first element is this list and the second element is a string representing the path
|
|
1830
1938
|
to the local datasets directory.
|
|
1831
1939
|
|
|
1832
|
-
"""
|
|
1940
|
+
"""
|
|
1833
1941
|
|
|
1834
|
-
datasets_path = os.sep.join(
|
|
1835
|
-
|
|
1942
|
+
datasets_path = os.sep.join(
|
|
1943
|
+
[os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective", "datasets",
|
|
1944
|
+
"signal_annotations", os.sep])
|
|
1945
|
+
repository_datasets = get_zenodo_files(cat=os.sep.join(["datasets", "signal_annotations"]))
|
|
1836
1946
|
|
|
1837
|
-
available_datasets = natsorted(glob(datasets_path+'*/'))
|
|
1838
|
-
available_datasets = [m.replace('\\','/').split('/')[-2] for m in available_datasets]
|
|
1947
|
+
available_datasets = natsorted(glob(datasets_path + '*/'))
|
|
1948
|
+
available_datasets = [m.replace('\\', '/').split('/')[-2] for m in available_datasets]
|
|
1839
1949
|
for rm in repository_datasets:
|
|
1840
1950
|
if rm not in available_datasets:
|
|
1841
1951
|
available_datasets.append(rm)
|
|
@@ -1845,15 +1955,16 @@ def get_signal_datasets_list(return_path=False):
|
|
|
1845
1955
|
else:
|
|
1846
1956
|
return available_datasets, datasets_path
|
|
1847
1957
|
|
|
1958
|
+
|
|
1848
1959
|
def locate_signal_dataset(name):
|
|
1849
1960
|
|
|
1850
1961
|
"""
|
|
1851
|
-
Locates a specified signal dataset within the local 'celldetective/datasets/signal_annotations' directory or downloads
|
|
1962
|
+
Locates a specified signal dataset within the local 'celldetective/datasets/signal_annotations' directory or downloads
|
|
1852
1963
|
it from Zenodo if not found locally.
|
|
1853
1964
|
|
|
1854
|
-
This function attempts to find a signal dataset by name within a predefined directory structure. If the dataset is not
|
|
1855
|
-
found locally, it then tries to locate and download the dataset from Zenodo, placing it into the appropriate category
|
|
1856
|
-
directory within 'celldetective'. The function prints the search directory path and returns the path to the found or
|
|
1965
|
+
This function attempts to find a signal dataset by name within a predefined directory structure. If the dataset is not
|
|
1966
|
+
found locally, it then tries to locate and download the dataset from Zenodo, placing it into the appropriate category
|
|
1967
|
+
directory within 'celldetective'. The function prints the search directory path and returns the path to the found or
|
|
1857
1968
|
downloaded dataset.
|
|
1858
1969
|
|
|
1859
1970
|
Parameters
|
|
@@ -1864,7 +1975,7 @@ def locate_signal_dataset(name):
|
|
|
1864
1975
|
Returns
|
|
1865
1976
|
-------
|
|
1866
1977
|
str or None
|
|
1867
|
-
The full path to the located or downloaded signal dataset directory, or None if the dataset could not be found or
|
|
1978
|
+
The full path to the located or downloaded signal dataset directory, or None if the dataset could not be found or
|
|
1868
1979
|
downloaded.
|
|
1869
1980
|
|
|
1870
1981
|
Raises
|
|
@@ -1874,14 +1985,14 @@ def locate_signal_dataset(name):
|
|
|
1874
1985
|
|
|
1875
1986
|
"""
|
|
1876
1987
|
|
|
1877
|
-
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],"celldetective"])
|
|
1988
|
+
main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], "celldetective"])
|
|
1878
1989
|
modelpath = os.sep.join([main_dir, "datasets", "signal_annotations", os.sep])
|
|
1879
1990
|
print(f'Looking for {name} in {modelpath}')
|
|
1880
|
-
models = glob(modelpath+f'*{os.sep}')
|
|
1991
|
+
models = glob(modelpath + f'*{os.sep}')
|
|
1881
1992
|
|
|
1882
|
-
match=None
|
|
1993
|
+
match = None
|
|
1883
1994
|
for m in models:
|
|
1884
|
-
if name==m.replace('\\',os.sep).split(os.sep)[-2]:
|
|
1995
|
+
if name == m.replace('\\', os.sep).split(os.sep)[-2]:
|
|
1885
1996
|
match = m
|
|
1886
1997
|
return match
|
|
1887
1998
|
# else no match, try zenodo
|
|
@@ -1890,13 +2001,13 @@ def locate_signal_dataset(name):
|
|
|
1890
2001
|
index = files.index(name)
|
|
1891
2002
|
cat = categories[index]
|
|
1892
2003
|
download_zenodo_file(name, os.sep.join([main_dir, cat]))
|
|
1893
|
-
match = os.sep.join([main_dir, cat, name])+os.sep
|
|
2004
|
+
match = os.sep.join([main_dir, cat, name]) + os.sep
|
|
1894
2005
|
return match
|
|
1895
2006
|
|
|
1896
2007
|
def normalize(frame, percentiles=(0.0,99.99), values=None, ignore_gray_value=0., clip=False, amplification=None, dtype=float):
|
|
1897
2008
|
|
|
1898
2009
|
"""
|
|
1899
|
-
|
|
2010
|
+
|
|
1900
2011
|
Normalize the intensity values of a frame.
|
|
1901
2012
|
|
|
1902
2013
|
Parameters
|
|
@@ -1946,15 +2057,16 @@ def normalize(frame, percentiles=(0.0,99.99), values=None, ignore_gray_value=0.,
|
|
|
1946
2057
|
frame = frame.astype(float)
|
|
1947
2058
|
|
|
1948
2059
|
if ignore_gray_value is not None:
|
|
1949
|
-
subframe = frame[frame!=ignore_gray_value]
|
|
2060
|
+
subframe = frame[frame != ignore_gray_value]
|
|
1950
2061
|
else:
|
|
1951
2062
|
subframe = frame.copy()
|
|
1952
2063
|
|
|
1953
2064
|
if values is not None:
|
|
1954
|
-
mi = values[0];
|
|
2065
|
+
mi = values[0];
|
|
2066
|
+
ma = values[1]
|
|
1955
2067
|
else:
|
|
1956
|
-
mi = np.nanpercentile(subframe.flatten(),percentiles[0],keepdims=True)
|
|
1957
|
-
ma = np.nanpercentile(subframe.flatten(),percentiles[1],keepdims=True)
|
|
2068
|
+
mi = np.nanpercentile(subframe.flatten(), percentiles[0], keepdims=True)
|
|
2069
|
+
ma = np.nanpercentile(subframe.flatten(), percentiles[1], keepdims=True)
|
|
1958
2070
|
|
|
1959
2071
|
frame0 = frame.copy()
|
|
1960
2072
|
frame = normalize_mi_ma(frame0, mi, ma, clip=False, eps=1e-20, dtype=np.float32)
|
|
@@ -1963,41 +2075,42 @@ def normalize(frame, percentiles=(0.0,99.99), values=None, ignore_gray_value=0.,
|
|
|
1963
2075
|
if clip:
|
|
1964
2076
|
if amplification is None:
|
|
1965
2077
|
amplification = 1.
|
|
1966
|
-
frame[frame>=amplification] = amplification
|
|
1967
|
-
frame[frame<=0.] = 0.
|
|
2078
|
+
frame[frame >= amplification] = amplification
|
|
2079
|
+
frame[frame <= 0.] = 0.
|
|
1968
2080
|
if ignore_gray_value is not None:
|
|
1969
|
-
frame[np.where(frame0)==ignore_gray_value] = ignore_gray_value
|
|
2081
|
+
frame[np.where(frame0) == ignore_gray_value] = ignore_gray_value
|
|
1970
2082
|
|
|
1971
2083
|
return frame.copy().astype(dtype)
|
|
1972
2084
|
|
|
2085
|
+
|
|
1973
2086
|
def normalize_multichannel(multichannel_frame, percentiles=None,
|
|
1974
2087
|
values=None, ignore_gray_value=0., clip=False,
|
|
1975
2088
|
amplification=None, dtype=float):
|
|
1976
2089
|
|
|
1977
2090
|
"""
|
|
1978
|
-
Normalizes a multichannel frame by adjusting the intensity values of each channel based on specified percentiles,
|
|
2091
|
+
Normalizes a multichannel frame by adjusting the intensity values of each channel based on specified percentiles,
|
|
1979
2092
|
direct value ranges, or amplification factors, with options to ignore a specific gray value and to clip the output.
|
|
1980
2093
|
|
|
1981
2094
|
Parameters
|
|
1982
2095
|
----------
|
|
1983
2096
|
multichannel_frame : ndarray
|
|
1984
|
-
The input multichannel image frame to be normalized, expected to be a 3-dimensional array where the last dimension
|
|
2097
|
+
The input multichannel image frame to be normalized, expected to be a 3-dimensional array where the last dimension
|
|
1985
2098
|
represents the channels.
|
|
1986
2099
|
percentiles : list of tuples or tuple, optional
|
|
1987
|
-
Percentile ranges (low, high) for each channel used to scale the intensity values. If a single tuple is provided,
|
|
2100
|
+
Percentile ranges (low, high) for each channel used to scale the intensity values. If a single tuple is provided,
|
|
1988
2101
|
it is applied to all channels. If None, the default percentile range of (0., 99.99) is used for each channel.
|
|
1989
2102
|
values : list of tuples or tuple, optional
|
|
1990
|
-
Direct value ranges (min, max) for each channel to scale the intensity values. If a single tuple is provided, it
|
|
2103
|
+
Direct value ranges (min, max) for each channel to scale the intensity values. If a single tuple is provided, it
|
|
1991
2104
|
is applied to all channels. This parameter overrides `percentiles` if provided.
|
|
1992
2105
|
ignore_gray_value : float, optional
|
|
1993
2106
|
A specific gray value to ignore during normalization (default is 0.).
|
|
1994
2107
|
clip : bool, optional
|
|
1995
|
-
If True, clips the output values to the range [0, 1] or the specified `dtype` range if `dtype` is not float
|
|
2108
|
+
If True, clips the output values to the range [0, 1] or the specified `dtype` range if `dtype` is not float
|
|
1996
2109
|
(default is False).
|
|
1997
2110
|
amplification : float, optional
|
|
1998
2111
|
A factor by which to amplify the intensity values after normalization. If None, no amplification is applied.
|
|
1999
2112
|
dtype : data-type, optional
|
|
2000
|
-
The desired data-type for the output normalized frame. The default is float, but other types can be specified
|
|
2113
|
+
The desired data-type for the output normalized frame. The default is float, but other types can be specified
|
|
2001
2114
|
to change the range of the output values.
|
|
2002
2115
|
|
|
2003
2116
|
Returns
|
|
@@ -2008,12 +2121,12 @@ def normalize_multichannel(multichannel_frame, percentiles=None,
|
|
|
2008
2121
|
Raises
|
|
2009
2122
|
------
|
|
2010
2123
|
AssertionError
|
|
2011
|
-
If the input `multichannel_frame` does not have 3 dimensions, or if the length of `values` does not match the
|
|
2124
|
+
If the input `multichannel_frame` does not have 3 dimensions, or if the length of `values` does not match the
|
|
2012
2125
|
number of channels in `multichannel_frame`.
|
|
2013
2126
|
|
|
2014
2127
|
Notes
|
|
2015
2128
|
-----
|
|
2016
|
-
- This function provides flexibility in normalization by allowing the use of percentile ranges, direct value ranges,
|
|
2129
|
+
- This function provides flexibility in normalization by allowing the use of percentile ranges, direct value ranges,
|
|
2017
2130
|
or amplification factors.
|
|
2018
2131
|
- The function makes a copy of the input frame to avoid altering the original data.
|
|
2019
2132
|
- When both `percentiles` and `values` are provided, `values` takes precedence for normalization.
|
|
@@ -2023,21 +2136,22 @@ def normalize_multichannel(multichannel_frame, percentiles=None,
|
|
|
2023
2136
|
>>> multichannel_frame = np.random.rand(100, 100, 3) # Example multichannel frame
|
|
2024
2137
|
>>> normalized_frame = normalize_multichannel(multichannel_frame, percentiles=((1, 99), (2, 98), (0, 100)))
|
|
2025
2138
|
# Normalizes each channel of the frame using specified percentile ranges.
|
|
2026
|
-
|
|
2139
|
+
|
|
2027
2140
|
"""
|
|
2028
2141
|
|
|
2029
2142
|
|
|
2030
2143
|
|
|
2031
2144
|
mf = multichannel_frame.copy().astype(float)
|
|
2032
|
-
assert mf.ndim==3,f'Wrong shape for the multichannel frame: {mf.shape}.'
|
|
2145
|
+
assert mf.ndim == 3, f'Wrong shape for the multichannel frame: {mf.shape}.'
|
|
2033
2146
|
if percentiles is None:
|
|
2034
|
-
percentiles = [(0.,99.99)]*mf.shape[-1]
|
|
2035
|
-
elif isinstance(percentiles,tuple):
|
|
2036
|
-
percentiles = [percentiles]*mf.shape[-1]
|
|
2147
|
+
percentiles = [(0., 99.99)] * mf.shape[-1]
|
|
2148
|
+
elif isinstance(percentiles, tuple):
|
|
2149
|
+
percentiles = [percentiles] * mf.shape[-1]
|
|
2037
2150
|
if values is not None:
|
|
2038
2151
|
if isinstance(values, tuple):
|
|
2039
|
-
values = [values]*mf.shape[-1]
|
|
2040
|
-
assert len(values)==mf.shape[
|
|
2152
|
+
values = [values] * mf.shape[-1]
|
|
2153
|
+
assert len(values) == mf.shape[
|
|
2154
|
+
-1], 'Mismatch between the normalization values provided and the number of channels.'
|
|
2041
2155
|
|
|
2042
2156
|
mf_new = []
|
|
2043
2157
|
for c in range(mf.shape[-1]):
|
|
@@ -2066,9 +2180,9 @@ def load_frames(img_nums, stack_path, scale=None, normalize_input=True, dtype=fl
|
|
|
2066
2180
|
"""
|
|
2067
2181
|
Loads and optionally normalizes and rescales specified frames from a stack located at a given path.
|
|
2068
2182
|
|
|
2069
|
-
This function reads specified frames from a stack file, applying systematic adjustments to ensure
|
|
2070
|
-
the channel axis is last. It supports optional normalization of the input frames and rescaling. An
|
|
2071
|
-
artificial pixel modification is applied to frames with uniform values to prevent errors during
|
|
2183
|
+
This function reads specified frames from a stack file, applying systematic adjustments to ensure
|
|
2184
|
+
the channel axis is last. It supports optional normalization of the input frames and rescaling. An
|
|
2185
|
+
artificial pixel modification is applied to frames with uniform values to prevent errors during
|
|
2072
2186
|
normalization.
|
|
2073
2187
|
|
|
2074
2188
|
Parameters
|
|
@@ -2080,7 +2194,7 @@ def load_frames(img_nums, stack_path, scale=None, normalize_input=True, dtype=fl
|
|
|
2080
2194
|
scale : float, optional
|
|
2081
2195
|
The scaling factor to apply to the frames. If None, no scaling is applied (default is None).
|
|
2082
2196
|
normalize_input : bool, optional
|
|
2083
|
-
Whether to normalize the loaded frames. If True, normalization is applied according to
|
|
2197
|
+
Whether to normalize the loaded frames. If True, normalization is applied according to
|
|
2084
2198
|
`normalize_kwargs` (default is True).
|
|
2085
2199
|
dtype : data-type, optional
|
|
2086
2200
|
The desired data-type for the output frames (default is float).
|
|
@@ -2096,38 +2210,39 @@ def load_frames(img_nums, stack_path, scale=None, normalize_input=True, dtype=fl
|
|
|
2096
2210
|
Raises
|
|
2097
2211
|
------
|
|
2098
2212
|
Exception
|
|
2099
|
-
Prints an error message if the specified frames cannot be loaded or if there is a mismatch between
|
|
2213
|
+
Prints an error message if the specified frames cannot be loaded or if there is a mismatch between
|
|
2100
2214
|
the provided experiment channel information and the stack format.
|
|
2101
2215
|
|
|
2102
2216
|
Notes
|
|
2103
2217
|
-----
|
|
2104
2218
|
- The function uses scikit-image for reading frames and supports multi-frame TIFF stacks.
|
|
2105
2219
|
- Normalization and scaling are optional and can be customized through function parameters.
|
|
2106
|
-
- A workaround is implemented for frames with uniform pixel values to prevent normalization errors by
|
|
2220
|
+
- A workaround is implemented for frames with uniform pixel values to prevent normalization errors by
|
|
2107
2221
|
adding a 'fake' pixel.
|
|
2108
2222
|
|
|
2109
2223
|
Examples
|
|
2110
2224
|
--------
|
|
2111
2225
|
>>> frames = load_frames([0, 1, 2], '/path/to/stack.tif', scale=0.5, normalize_input=True, dtype=np.uint8)
|
|
2112
|
-
# Loads the first three frames from '/path/to/stack.tif', normalizes them, rescales by a factor of 0.5,
|
|
2226
|
+
# Loads the first three frames from '/path/to/stack.tif', normalizes them, rescales by a factor of 0.5,
|
|
2113
2227
|
# and converts them to uint8 data type.
|
|
2114
|
-
|
|
2228
|
+
|
|
2115
2229
|
"""
|
|
2116
2230
|
|
|
2117
2231
|
try:
|
|
2118
2232
|
frames = skio.imread(stack_path, key=img_nums, plugin="tifffile")
|
|
2119
2233
|
except Exception as e:
|
|
2120
|
-
print(
|
|
2234
|
+
print(
|
|
2235
|
+
f'Error in loading the frame {img_nums} {e}. Please check that the experiment channel information is consistent with the movie being read.')
|
|
2121
2236
|
return None
|
|
2122
2237
|
|
|
2123
|
-
if frames.ndim==3:
|
|
2238
|
+
if frames.ndim == 3:
|
|
2124
2239
|
# Systematically move channel axis to the end
|
|
2125
2240
|
channel_axis = np.argmin(frames.shape)
|
|
2126
2241
|
frames = np.moveaxis(frames, channel_axis, -1)
|
|
2127
2242
|
|
|
2128
2243
|
if frames.ndim==2:
|
|
2129
2244
|
frames = frames[:,:,np.newaxis].astype(float)
|
|
2130
|
-
|
|
2245
|
+
|
|
2131
2246
|
if normalize_input:
|
|
2132
2247
|
frames = normalize_multichannel(frames, **normalize_kwargs)
|
|
2133
2248
|
|
|
@@ -2138,9 +2253,9 @@ def load_frames(img_nums, stack_path, scale=None, normalize_input=True, dtype=fl
|
|
|
2138
2253
|
# add a fake pixel to prevent auto normalization errors on images that are uniform
|
|
2139
2254
|
# to revisit
|
|
2140
2255
|
for k in range(frames.shape[2]):
|
|
2141
|
-
unique_values = np.unique(frames[
|
|
2142
|
-
if len(unique_values)==1:
|
|
2143
|
-
frames[0,0,k] += 1
|
|
2256
|
+
unique_values = np.unique(frames[:, :, k])
|
|
2257
|
+
if len(unique_values) == 1:
|
|
2258
|
+
frames[0, 0, k] += 1
|
|
2144
2259
|
|
|
2145
2260
|
return frames.astype(dtype)
|
|
2146
2261
|
|
|
@@ -2150,9 +2265,9 @@ def get_stack_normalization_values(stack, percentiles=None, ignore_gray_value=0.
|
|
|
2150
2265
|
"""
|
|
2151
2266
|
Computes the normalization value ranges (minimum and maximum) for each channel in a 4D stack based on specified percentiles.
|
|
2152
2267
|
|
|
2153
|
-
This function calculates the value ranges for normalizing each channel within a 4-dimensional stack, with dimensions
|
|
2154
|
-
expected to be in the order of Time (T), Y (height), X (width), and Channels (C). The normalization values are determined
|
|
2155
|
-
by the specified percentiles for each channel. An option to ignore a specific gray value during computation is provided,
|
|
2268
|
+
This function calculates the value ranges for normalizing each channel within a 4-dimensional stack, with dimensions
|
|
2269
|
+
expected to be in the order of Time (T), Y (height), X (width), and Channels (C). The normalization values are determined
|
|
2270
|
+
by the specified percentiles for each channel. An option to ignore a specific gray value during computation is provided,
|
|
2156
2271
|
though its effect is not implemented in this snippet.
|
|
2157
2272
|
|
|
2158
2273
|
Parameters
|
|
@@ -2160,30 +2275,30 @@ def get_stack_normalization_values(stack, percentiles=None, ignore_gray_value=0.
|
|
|
2160
2275
|
stack : ndarray
|
|
2161
2276
|
The input 4D stack with dimensions TYXC from which to calculate normalization values.
|
|
2162
2277
|
percentiles : tuple, list of tuples, optional
|
|
2163
|
-
The percentile values (low, high) used to calculate the normalization ranges for each channel. If a single tuple
|
|
2164
|
-
is provided, it is applied to all channels. If a list of tuples is provided, each tuple is applied to the
|
|
2278
|
+
The percentile values (low, high) used to calculate the normalization ranges for each channel. If a single tuple
|
|
2279
|
+
is provided, it is applied to all channels. If a list of tuples is provided, each tuple is applied to the
|
|
2165
2280
|
corresponding channel. If None, defaults to (0., 99.99) for each channel.
|
|
2166
2281
|
ignore_gray_value : float, optional
|
|
2167
|
-
A gray value to potentially ignore during the calculation. This parameter is provided for interface consistency
|
|
2282
|
+
A gray value to potentially ignore during the calculation. This parameter is provided for interface consistency
|
|
2168
2283
|
but is not utilized in the current implementation (default is 0.).
|
|
2169
2284
|
|
|
2170
2285
|
Returns
|
|
2171
2286
|
-------
|
|
2172
2287
|
list of tuples
|
|
2173
|
-
A list where each tuple contains the (minimum, maximum) values for normalizing each channel based on the specified
|
|
2288
|
+
A list where each tuple contains the (minimum, maximum) values for normalizing each channel based on the specified
|
|
2174
2289
|
percentiles.
|
|
2175
2290
|
|
|
2176
2291
|
Raises
|
|
2177
2292
|
------
|
|
2178
2293
|
AssertionError
|
|
2179
|
-
If the input stack does not have 4 dimensions, or if the length of the `percentiles` list does not match the number
|
|
2294
|
+
If the input stack does not have 4 dimensions, or if the length of the `percentiles` list does not match the number
|
|
2180
2295
|
of channels in the stack.
|
|
2181
2296
|
|
|
2182
2297
|
Notes
|
|
2183
2298
|
-----
|
|
2184
|
-
- The function assumes the input stack is in TYXC format, where T is the time dimension, Y and X are spatial dimensions,
|
|
2299
|
+
- The function assumes the input stack is in TYXC format, where T is the time dimension, Y and X are spatial dimensions,
|
|
2185
2300
|
and C is the channel dimension.
|
|
2186
|
-
- Memory management via `gc.collect()` is employed after calculating normalization values for each channel to mitigate
|
|
2301
|
+
- Memory management via `gc.collect()` is employed after calculating normalization values for each channel to mitigate
|
|
2187
2302
|
potential memory issues with large datasets.
|
|
2188
2303
|
|
|
2189
2304
|
Examples
|
|
@@ -2191,23 +2306,24 @@ def get_stack_normalization_values(stack, percentiles=None, ignore_gray_value=0.
|
|
|
2191
2306
|
>>> stack = np.random.rand(5, 100, 100, 3) # Example 4D stack with 3 channels
|
|
2192
2307
|
>>> normalization_values = get_stack_normalization_values(stack, percentiles=((1, 99), (2, 98), (0, 100)))
|
|
2193
2308
|
# Calculates normalization ranges for each channel using the specified percentiles.
|
|
2194
|
-
|
|
2309
|
+
|
|
2195
2310
|
"""
|
|
2196
2311
|
|
|
2197
|
-
assert stack.ndim==4,f'Wrong number of dimensions for the stack, expect TYXC (4) got {stack.ndim}.'
|
|
2312
|
+
assert stack.ndim == 4, f'Wrong number of dimensions for the stack, expect TYXC (4) got {stack.ndim}.'
|
|
2198
2313
|
if percentiles is None:
|
|
2199
|
-
percentiles = [(0.,99.99)]*stack.shape[-1]
|
|
2200
|
-
elif isinstance(percentiles,tuple):
|
|
2201
|
-
percentiles = [percentiles]*stack.shape[-1]
|
|
2202
|
-
elif isinstance(percentiles,list):
|
|
2203
|
-
assert len(percentiles)==
|
|
2314
|
+
percentiles = [(0., 99.99)] * stack.shape[-1]
|
|
2315
|
+
elif isinstance(percentiles, tuple):
|
|
2316
|
+
percentiles = [percentiles] * stack.shape[-1]
|
|
2317
|
+
elif isinstance(percentiles, list):
|
|
2318
|
+
assert len(percentiles) == stack.shape[
|
|
2319
|
+
-1], f'Mismatch between the provided percentiles and the number of channels {stack.shape[-1]}. If you meant to apply the same percentiles to all channels, please provide a single tuple.'
|
|
2204
2320
|
|
|
2205
2321
|
values = []
|
|
2206
2322
|
for c in range(stack.shape[-1]):
|
|
2207
2323
|
perc = percentiles[c]
|
|
2208
|
-
mi = np.nanpercentile(stack[
|
|
2209
|
-
ma = np.nanpercentile(stack[
|
|
2210
|
-
values.append(tuple((mi,ma)))
|
|
2324
|
+
mi = np.nanpercentile(stack[:, :, :, c].flatten(), perc[0], keepdims=True)[0]
|
|
2325
|
+
ma = np.nanpercentile(stack[:, :, :, c].flatten(), perc[1], keepdims=True)[0]
|
|
2326
|
+
values.append(tuple((mi, ma)))
|
|
2211
2327
|
gc.collect()
|
|
2212
2328
|
|
|
2213
2329
|
return values
|
|
@@ -2216,15 +2332,15 @@ def get_stack_normalization_values(stack, percentiles=None, ignore_gray_value=0.
|
|
|
2216
2332
|
def get_positions_in_well(well):
|
|
2217
2333
|
|
|
2218
2334
|
"""
|
|
2219
|
-
Retrieves the list of position directories within a specified well directory,
|
|
2335
|
+
Retrieves the list of position directories within a specified well directory,
|
|
2220
2336
|
formatted as a NumPy array of strings.
|
|
2221
2337
|
|
|
2222
|
-
This function identifies position directories based on their naming convention,
|
|
2223
|
-
which must include a numeric identifier following the well's name. The well's name
|
|
2224
|
-
is expected to start with 'W' (e.g., 'W1'), followed by a numeric identifier. Position
|
|
2225
|
-
directories are assumed to be named with this numeric identifier directly after the well
|
|
2226
|
-
identifier, without the 'W'. For example, positions within well 'W1' might be named
|
|
2227
|
-
'101', '102', etc. This function will glob these directories and return their full
|
|
2338
|
+
This function identifies position directories based on their naming convention,
|
|
2339
|
+
which must include a numeric identifier following the well's name. The well's name
|
|
2340
|
+
is expected to start with 'W' (e.g., 'W1'), followed by a numeric identifier. Position
|
|
2341
|
+
directories are assumed to be named with this numeric identifier directly after the well
|
|
2342
|
+
identifier, without the 'W'. For example, positions within well 'W1' might be named
|
|
2343
|
+
'101', '102', etc. This function will glob these directories and return their full
|
|
2228
2344
|
paths as a NumPy array.
|
|
2229
2345
|
|
|
2230
2346
|
Parameters
|
|
@@ -2235,13 +2351,13 @@ def get_positions_in_well(well):
|
|
|
2235
2351
|
Returns
|
|
2236
2352
|
-------
|
|
2237
2353
|
np.ndarray
|
|
2238
|
-
An array of strings, each representing the full path to a position directory within
|
|
2354
|
+
An array of strings, each representing the full path to a position directory within
|
|
2239
2355
|
the specified well. The array is empty if no position directories are found.
|
|
2240
2356
|
|
|
2241
2357
|
Notes
|
|
2242
2358
|
-----
|
|
2243
2359
|
- This function relies on a specific naming convention for wells and positions. It assumes
|
|
2244
|
-
that each well directory is prefixed with 'W' followed by a numeric identifier, and
|
|
2360
|
+
that each well directory is prefixed with 'W' followed by a numeric identifier, and
|
|
2245
2361
|
position directories are named starting with this numeric identifier directly.
|
|
2246
2362
|
|
|
2247
2363
|
Examples
|
|
@@ -2249,54 +2365,54 @@ def get_positions_in_well(well):
|
|
|
2249
2365
|
>>> get_positions_in_well('/path/to/experiment/W1')
|
|
2250
2366
|
# This might return an array like array(['/path/to/experiment/W1/101', '/path/to/experiment/W1/102'])
|
|
2251
2367
|
if position directories '101' and '102' exist within the well 'W1' directory.
|
|
2252
|
-
|
|
2368
|
+
|
|
2253
2369
|
"""
|
|
2254
2370
|
|
|
2255
2371
|
if well.endswith(os.sep):
|
|
2256
2372
|
well = well[:-1]
|
|
2257
2373
|
|
|
2258
|
-
w_numeric = os.path.split(well)[-1].replace('W','')
|
|
2259
|
-
positions = natsorted(glob(os.sep.join([well,f'{w_numeric}*{os.sep}'])))
|
|
2374
|
+
w_numeric = os.path.split(well)[-1].replace('W', '')
|
|
2375
|
+
positions = natsorted(glob(os.sep.join([well, f'{w_numeric}*{os.sep}'])))
|
|
2260
2376
|
|
|
2261
|
-
return np.array(positions,dtype=str)
|
|
2377
|
+
return np.array(positions, dtype=str)
|
|
2262
2378
|
|
|
2263
2379
|
|
|
2264
2380
|
def extract_experiment_folder_output(experiment_folder, destination_folder):
|
|
2265
2381
|
|
|
2266
2382
|
"""
|
|
2267
|
-
Copies the output subfolder and associated tables from an experiment folder to a new location,
|
|
2383
|
+
Copies the output subfolder and associated tables from an experiment folder to a new location,
|
|
2268
2384
|
making the experiment folder much lighter by only keeping essential data.
|
|
2269
|
-
|
|
2270
|
-
This function takes the path to an experiment folder and a destination folder as input.
|
|
2271
|
-
It creates a copy of the experiment folder at the destination, but only includes the output subfolders
|
|
2272
|
-
and their associated tables for each well and position within the experiment.
|
|
2273
|
-
This operation significantly reduces the size of the experiment data by excluding non-essential files.
|
|
2274
|
-
|
|
2275
|
-
The structure of the copied experiment folder is preserved, including the configuration file,
|
|
2276
|
-
well directories, and position directories within each well.
|
|
2385
|
+
|
|
2386
|
+
This function takes the path to an experiment folder and a destination folder as input.
|
|
2387
|
+
It creates a copy of the experiment folder at the destination, but only includes the output subfolders
|
|
2388
|
+
and their associated tables for each well and position within the experiment.
|
|
2389
|
+
This operation significantly reduces the size of the experiment data by excluding non-essential files.
|
|
2390
|
+
|
|
2391
|
+
The structure of the copied experiment folder is preserved, including the configuration file,
|
|
2392
|
+
well directories, and position directories within each well.
|
|
2277
2393
|
Only the 'output' subfolder and its 'tables' subdirectory are copied for each position.
|
|
2278
|
-
|
|
2394
|
+
|
|
2279
2395
|
Parameters
|
|
2280
2396
|
----------
|
|
2281
2397
|
experiment_folder : str
|
|
2282
|
-
The path to the source experiment folder from which to extract data.
|
|
2398
|
+
The path to the source experiment folder from which to extract data.
|
|
2283
2399
|
destination_folder : str
|
|
2284
|
-
The path to the destination folder where the reduced copy of the experiment
|
|
2400
|
+
The path to the destination folder where the reduced copy of the experiment
|
|
2285
2401
|
will be created.
|
|
2286
|
-
|
|
2402
|
+
|
|
2287
2403
|
Notes
|
|
2288
2404
|
-----
|
|
2289
|
-
- This function assumes that the structure of the experiment folder is consistent,
|
|
2290
|
-
with wells organized in subdirectories and each containing a position subdirectory.
|
|
2405
|
+
- This function assumes that the structure of the experiment folder is consistent,
|
|
2406
|
+
with wells organized in subdirectories and each containing a position subdirectory.
|
|
2291
2407
|
Each position subdirectory should have an 'output' folder and a 'tables' subfolder within it.
|
|
2292
|
-
|
|
2293
|
-
- The function also assumes the existence of a configuration file in the root of the
|
|
2408
|
+
|
|
2409
|
+
- The function also assumes the existence of a configuration file in the root of the
|
|
2294
2410
|
experiment folder, which is copied to the root of the destination experiment folder.
|
|
2295
|
-
|
|
2411
|
+
|
|
2296
2412
|
Examples
|
|
2297
2413
|
--------
|
|
2298
2414
|
>>> extract_experiment_folder_output('/path/to/experiment_folder', '/path/to/destination_folder')
|
|
2299
|
-
# This will copy the 'experiment_folder' to 'destination_folder', including only
|
|
2415
|
+
# This will copy the 'experiment_folder' to 'destination_folder', including only
|
|
2300
2416
|
# the output subfolders and their tables for each well and position.
|
|
2301
2417
|
|
|
2302
2418
|
"""
|
|
@@ -2306,21 +2422,21 @@ def extract_experiment_folder_output(experiment_folder, destination_folder):
|
|
|
2306
2422
|
experiment_folder = experiment_folder[:-1]
|
|
2307
2423
|
if destination_folder.endswith(os.sep):
|
|
2308
2424
|
destination_folder = destination_folder[:-1]
|
|
2309
|
-
|
|
2425
|
+
|
|
2310
2426
|
exp_name = experiment_folder.split(os.sep)[-1]
|
|
2311
2427
|
output_path = os.sep.join([destination_folder, exp_name])
|
|
2312
2428
|
if not os.path.exists(output_path):
|
|
2313
2429
|
os.mkdir(output_path)
|
|
2314
2430
|
|
|
2315
2431
|
config = get_config(experiment_folder)
|
|
2316
|
-
copyfile(config,os.sep.join([output_path,os.path.split(config)[-1]]))
|
|
2317
|
-
|
|
2432
|
+
copyfile(config, os.sep.join([output_path, os.path.split(config)[-1]]))
|
|
2433
|
+
|
|
2318
2434
|
wells_src = get_experiment_wells(experiment_folder)
|
|
2319
2435
|
wells = [w.split(os.sep)[-2] for w in wells_src]
|
|
2320
2436
|
|
|
2321
|
-
for k,w in enumerate(wells):
|
|
2322
|
-
|
|
2323
|
-
well_output_path = os.sep.join([output_path,w])
|
|
2437
|
+
for k, w in enumerate(wells):
|
|
2438
|
+
|
|
2439
|
+
well_output_path = os.sep.join([output_path, w])
|
|
2324
2440
|
if not os.path.exists(well_output_path):
|
|
2325
2441
|
os.mkdir(well_output_path)
|
|
2326
2442
|
|
|
@@ -2336,16 +2452,16 @@ def extract_experiment_folder_output(experiment_folder, destination_folder):
|
|
|
2336
2452
|
|
|
2337
2453
|
if not os.path.exists(output_folder):
|
|
2338
2454
|
os.mkdir(output_folder)
|
|
2339
|
-
|
|
2455
|
+
|
|
2340
2456
|
if not os.path.exists(output_tables_folder):
|
|
2341
|
-
os.mkdir(output_tables_folder)
|
|
2457
|
+
os.mkdir(output_tables_folder)
|
|
2342
2458
|
|
|
2343
|
-
tab_path = glob(pos+os.sep.join(['output','tables',f'*']))
|
|
2344
|
-
|
|
2345
|
-
for t in tab_path:
|
|
2346
|
-
copyfile(t,os.sep.join([output_tables_folder,os.path.split(t)[-1]]))
|
|
2459
|
+
tab_path = glob(pos + os.sep.join(['output', 'tables', f'*']))
|
|
2347
2460
|
|
|
2461
|
+
for t in tab_path:
|
|
2462
|
+
copyfile(t, os.sep.join([output_tables_folder, os.path.split(t)[-1]]))
|
|
2348
2463
|
|
|
2349
2464
|
|
|
2350
2465
|
if __name__ == '__main__':
|
|
2351
|
-
control_segmentation_napari("/home/limozin/Documents/Experiments/MinimumJan/W4/401/", prefix='Aligned',
|
|
2466
|
+
control_segmentation_napari("/home/limozin/Documents/Experiments/MinimumJan/W4/401/", prefix='Aligned',
|
|
2467
|
+
population="target", flush_memory=False)
|