celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__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 +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +403 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/downloader.py +137 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +235 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
- celldetective-1.5.0b1.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/downloader.py +0 -111
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
|
@@ -5,10 +5,34 @@ Copright © 2022 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
|
5
5
|
import argparse
|
|
6
6
|
import os
|
|
7
7
|
import json
|
|
8
|
-
from celldetective.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
from celldetective.utils.image_loaders import (
|
|
9
|
+
locate_labels,
|
|
10
|
+
auto_load_number_of_frames,
|
|
11
|
+
load_frames,
|
|
12
|
+
fix_missing_labels,
|
|
13
|
+
_get_img_num_per_channel,
|
|
14
|
+
)
|
|
15
|
+
from celldetective.utils.experiment import (
|
|
16
|
+
extract_position_name,
|
|
17
|
+
extract_experiment_channels,
|
|
18
|
+
)
|
|
19
|
+
from celldetective import (
|
|
20
|
+
extract_experiment_channels,
|
|
21
|
+
)
|
|
22
|
+
from celldetective.utils.parsing import config_section_to_dict
|
|
23
|
+
from celldetective.utils.data_cleaning import (
|
|
24
|
+
_remove_invalid_cols,
|
|
25
|
+
_extract_coordinates_from_features,
|
|
26
|
+
remove_redundant_features,
|
|
27
|
+
remove_trajectory_measurements,
|
|
28
|
+
)
|
|
29
|
+
from celldetective.measure import (
|
|
30
|
+
drop_tonal_features,
|
|
31
|
+
measure_features,
|
|
32
|
+
measure_isotropic_intensity,
|
|
33
|
+
center_of_mass_to_abs_coordinates,
|
|
34
|
+
measure_radial_distance_to_center,
|
|
35
|
+
)
|
|
12
36
|
from pathlib import Path, PurePath
|
|
13
37
|
from glob import glob
|
|
14
38
|
from tqdm import tqdm
|
|
@@ -20,38 +44,54 @@ import datetime
|
|
|
20
44
|
|
|
21
45
|
tprint("Measure")
|
|
22
46
|
|
|
23
|
-
parser = argparse.ArgumentParser(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
parser = argparse.ArgumentParser(
|
|
48
|
+
description="Measure features and intensities in a multichannel timeseries.",
|
|
49
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument("-p", "--position", required=True, help="Path to the position")
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--mode",
|
|
54
|
+
default="target",
|
|
55
|
+
choices=["target", "effector", "targets", "effectors"],
|
|
56
|
+
help="Cell population of interest",
|
|
57
|
+
)
|
|
27
58
|
parser.add_argument("--threads", default="1", help="Number of parallel threads")
|
|
28
59
|
|
|
29
60
|
args = parser.parse_args()
|
|
30
61
|
process_arguments = vars(args)
|
|
31
|
-
pos = str(process_arguments[
|
|
32
|
-
mode = str(process_arguments[
|
|
33
|
-
n_threads = int(process_arguments[
|
|
34
|
-
|
|
35
|
-
column_labels = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
pos = str(process_arguments["position"])
|
|
63
|
+
mode = str(process_arguments["mode"])
|
|
64
|
+
n_threads = int(process_arguments["threads"])
|
|
65
|
+
|
|
66
|
+
column_labels = {
|
|
67
|
+
"track": "TRACK_ID",
|
|
68
|
+
"time": "FRAME",
|
|
69
|
+
"x": "POSITION_X",
|
|
70
|
+
"y": "POSITION_Y",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if mode.lower() == "target" or mode.lower() == "targets":
|
|
74
|
+
label_folder = "labels_targets"
|
|
75
|
+
table_name = "trajectories_targets.csv"
|
|
76
|
+
instruction_file = os.sep.join(["configs", "measurement_instructions_targets.json"])
|
|
77
|
+
|
|
78
|
+
elif mode.lower() == "effector" or mode.lower() == "effectors":
|
|
79
|
+
label_folder = "labels_effectors"
|
|
80
|
+
table_name = "trajectories_effectors.csv"
|
|
81
|
+
instruction_file = os.sep.join(
|
|
82
|
+
["configs", "measurement_instructions_effectors.json"]
|
|
83
|
+
)
|
|
46
84
|
|
|
47
85
|
# Locate experiment config
|
|
48
86
|
parent1 = Path(pos).parent
|
|
49
87
|
expfolder = parent1.parent
|
|
50
|
-
config = PurePath(expfolder,Path("config.ini"))
|
|
51
|
-
assert os.path.exists(
|
|
88
|
+
config = PurePath(expfolder, Path("config.ini"))
|
|
89
|
+
assert os.path.exists(
|
|
90
|
+
config
|
|
91
|
+
), "The configuration file for the experiment could not be located. Abort."
|
|
52
92
|
|
|
53
93
|
print(f"Position: {extract_position_name(pos)}...")
|
|
54
|
-
print("Configuration file: ",config)
|
|
94
|
+
print("Configuration file: ", config)
|
|
55
95
|
print(f"Population: {mode}...")
|
|
56
96
|
|
|
57
97
|
# from exp config fetch spatial calib, channel names
|
|
@@ -63,139 +103,147 @@ channel_names, channel_indices = extract_experiment_channels(expfolder)
|
|
|
63
103
|
nbr_channels = len(channel_names)
|
|
64
104
|
|
|
65
105
|
# from tracking instructions, fetch btrack config, features, haralick, clean_traj, idea: fetch custom timeline?
|
|
66
|
-
instr_path = PurePath(expfolder,Path(f"{instruction_file}"))
|
|
67
|
-
print(
|
|
106
|
+
instr_path = PurePath(expfolder, Path(f"{instruction_file}"))
|
|
107
|
+
print("Looking for measurement instruction file...")
|
|
68
108
|
|
|
69
109
|
if os.path.exists(instr_path):
|
|
70
110
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
with open(instr_path, "r") as f:
|
|
112
|
+
instructions = json.load(f)
|
|
113
|
+
print(f"Measurement instruction file successfully loaded...")
|
|
114
|
+
print(f"Instructions: {instructions}...")
|
|
115
|
+
|
|
116
|
+
if "background_correction" in instructions:
|
|
117
|
+
background_correction = instructions["background_correction"]
|
|
118
|
+
else:
|
|
119
|
+
background_correction = None
|
|
120
|
+
|
|
121
|
+
if "features" in instructions:
|
|
122
|
+
features = instructions["features"]
|
|
123
|
+
else:
|
|
124
|
+
features = None
|
|
125
|
+
|
|
126
|
+
if "border_distances" in instructions:
|
|
127
|
+
border_distances = instructions["border_distances"]
|
|
128
|
+
else:
|
|
129
|
+
border_distances = None
|
|
130
|
+
|
|
131
|
+
if "spot_detection" in instructions:
|
|
132
|
+
spot_detection = instructions["spot_detection"]
|
|
133
|
+
else:
|
|
134
|
+
spot_detection = None
|
|
135
|
+
|
|
136
|
+
if "haralick_options" in instructions:
|
|
137
|
+
haralick_options = instructions["haralick_options"]
|
|
138
|
+
else:
|
|
139
|
+
haralick_options = None
|
|
140
|
+
|
|
141
|
+
if "intensity_measurement_radii" in instructions:
|
|
142
|
+
intensity_measurement_radii = instructions["intensity_measurement_radii"]
|
|
143
|
+
else:
|
|
144
|
+
intensity_measurement_radii = None
|
|
145
|
+
|
|
146
|
+
if "isotropic_operations" in instructions:
|
|
147
|
+
isotropic_operations = instructions["isotropic_operations"]
|
|
148
|
+
else:
|
|
149
|
+
isotropic_operations = None
|
|
150
|
+
|
|
151
|
+
if "clear_previous" in instructions:
|
|
152
|
+
clear_previous = instructions["clear_previous"]
|
|
153
|
+
else:
|
|
154
|
+
clear_previous = True
|
|
115
155
|
|
|
116
156
|
else:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
157
|
+
print("No measurement instructions found. Use default measurements.")
|
|
158
|
+
features = ["area", "intensity_mean"]
|
|
159
|
+
border_distances = None
|
|
160
|
+
haralick_options = None
|
|
161
|
+
clear_previous = False
|
|
162
|
+
background_correction = None
|
|
163
|
+
spot_detection = None
|
|
164
|
+
intensity_measurement_radii = 10
|
|
165
|
+
isotropic_operations = ["mean"]
|
|
126
166
|
|
|
127
167
|
if features is None:
|
|
128
|
-
|
|
168
|
+
features = []
|
|
129
169
|
|
|
130
170
|
# from pos fetch labels
|
|
131
|
-
label_path = natsorted(glob(os.sep.join([pos, label_folder,
|
|
132
|
-
if len(label_path)>0:
|
|
133
|
-
|
|
171
|
+
label_path = natsorted(glob(os.sep.join([pos, label_folder, "*.tif"])))
|
|
172
|
+
if len(label_path) > 0:
|
|
173
|
+
print(f"Found {len(label_path)} segmented frames...")
|
|
134
174
|
else:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
175
|
+
print(
|
|
176
|
+
f"No segmented frames have been found. Please run segmentation first, skipping... Features cannot be computed."
|
|
177
|
+
)
|
|
178
|
+
features = None
|
|
179
|
+
haralick_options = None
|
|
180
|
+
border_distances = None
|
|
181
|
+
label_path = None
|
|
140
182
|
|
|
141
183
|
# Do this if features or Haralick is not None, else don't need stack
|
|
142
184
|
try:
|
|
143
|
-
|
|
185
|
+
file = glob(pos + os.sep.join(["movie", f"{movie_prefix}*.tif"]))[0]
|
|
144
186
|
except IndexError:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
187
|
+
print(
|
|
188
|
+
"Movie could not be found. Check the prefix. If you intended to measure texture or tone, this will not be performed."
|
|
189
|
+
)
|
|
190
|
+
file = None
|
|
191
|
+
haralick_option = None
|
|
192
|
+
features = drop_tonal_features(features)
|
|
149
193
|
|
|
150
194
|
# Load trajectories, add centroid if not in trajectory
|
|
151
|
-
trajectories = pos+os.sep.join([
|
|
195
|
+
trajectories = pos + os.sep.join(["output", "tables", table_name])
|
|
152
196
|
if os.path.exists(trajectories):
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
197
|
+
print("A trajectory table was found...")
|
|
198
|
+
trajectories = pd.read_csv(trajectories)
|
|
199
|
+
if "TRACK_ID" not in list(trajectories.columns):
|
|
200
|
+
do_iso_intensities = False
|
|
201
|
+
intensity_measurement_radii = None
|
|
202
|
+
if clear_previous:
|
|
203
|
+
print("No TRACK_ID... Clear previous measurements...")
|
|
204
|
+
trajectories = (
|
|
205
|
+
None # remove_trajectory_measurements(trajectories, column_labels)
|
|
206
|
+
)
|
|
207
|
+
do_features = True
|
|
208
|
+
features += ["centroid"]
|
|
209
|
+
else:
|
|
210
|
+
if clear_previous:
|
|
211
|
+
print("TRACK_ID found... Clear previous measurements...")
|
|
212
|
+
trajectories = remove_trajectory_measurements(trajectories, column_labels)
|
|
167
213
|
else:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
214
|
+
trajectories = None
|
|
215
|
+
do_features = True
|
|
216
|
+
features += ["centroid"]
|
|
217
|
+
do_iso_intensities = False
|
|
172
218
|
|
|
173
219
|
|
|
174
220
|
len_movie_auto = auto_load_number_of_frames(file)
|
|
175
221
|
if len_movie_auto is not None:
|
|
176
|
-
|
|
222
|
+
len_movie = len_movie_auto
|
|
177
223
|
|
|
178
224
|
if label_path is not None and file is not None:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
225
|
+
test = len(label_path) == len_movie
|
|
226
|
+
if not test:
|
|
227
|
+
fix_missing_labels(pos, population=mode, prefix=movie_prefix)
|
|
228
|
+
label_path = natsorted(glob(os.sep.join([pos, label_folder, "*.tif"])))
|
|
183
229
|
|
|
184
230
|
img_num_channels = _get_img_num_per_channel(channel_indices, len_movie, nbr_channels)
|
|
185
231
|
|
|
186
232
|
|
|
187
233
|
# Test what to do
|
|
188
234
|
if (file is None) or (intensity_measurement_radii is None):
|
|
189
|
-
|
|
190
|
-
|
|
235
|
+
do_iso_intensities = False
|
|
236
|
+
print(
|
|
237
|
+
"Either no image, no positions or no radii were provided... Isotropic intensities will not be computed..."
|
|
238
|
+
)
|
|
191
239
|
else:
|
|
192
|
-
|
|
240
|
+
do_iso_intensities = True
|
|
193
241
|
|
|
194
242
|
if label_path is None:
|
|
195
|
-
|
|
196
|
-
|
|
243
|
+
do_features = False
|
|
244
|
+
print("No labels were provided... Features will not be computed...")
|
|
197
245
|
else:
|
|
198
|
-
|
|
246
|
+
do_features = True
|
|
199
247
|
|
|
200
248
|
|
|
201
249
|
#######################################
|
|
@@ -204,76 +252,127 @@ else:
|
|
|
204
252
|
|
|
205
253
|
timestep_dataframes = []
|
|
206
254
|
if trajectories is None:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
255
|
+
print("Use features as a substitute for the trajectory table.")
|
|
256
|
+
if "label" not in features:
|
|
257
|
+
features.append("label")
|
|
210
258
|
|
|
211
259
|
if label_path is not None:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
features_log=f
|
|
216
|
-
border_distances_log=f
|
|
217
|
-
haralick_options_log=f
|
|
218
|
-
background_correction_log=f
|
|
219
|
-
spot_detection_log=f
|
|
220
|
-
intensity_measurement_radii_log=
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
260
|
+
label_names = [os.path.split(lbl)[-1] for lbl in label_path]
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
features_log = f"features: {features}"
|
|
264
|
+
border_distances_log = f"border_distances: {border_distances}"
|
|
265
|
+
haralick_options_log = f"haralick_options: {haralick_options}"
|
|
266
|
+
background_correction_log = f"background_correction: {background_correction}"
|
|
267
|
+
spot_detection_log = f"spot_detection: {spot_detection}"
|
|
268
|
+
intensity_measurement_radii_log = (
|
|
269
|
+
f"intensity_measurement_radii: {intensity_measurement_radii}"
|
|
270
|
+
)
|
|
271
|
+
isotropic_options_log = f"isotropic_operations: {isotropic_operations} \n"
|
|
272
|
+
log = "\n".join(
|
|
273
|
+
[
|
|
274
|
+
features_log,
|
|
275
|
+
border_distances_log,
|
|
276
|
+
haralick_options_log,
|
|
277
|
+
background_correction_log,
|
|
278
|
+
spot_detection_log,
|
|
279
|
+
intensity_measurement_radii_log,
|
|
280
|
+
isotropic_options_log,
|
|
281
|
+
]
|
|
282
|
+
)
|
|
283
|
+
with open(pos + f"log_{mode}.json", "a") as f:
|
|
284
|
+
f.write(f"{datetime.datetime.now()} MEASURE \n")
|
|
285
|
+
f.write(log + "\n")
|
|
226
286
|
|
|
227
287
|
|
|
228
288
|
def measure_index(indices):
|
|
229
289
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
290
|
+
# global column_labels
|
|
291
|
+
|
|
292
|
+
for t in tqdm(indices, desc="frame"):
|
|
293
|
+
|
|
294
|
+
if file is not None:
|
|
295
|
+
img = load_frames(
|
|
296
|
+
img_num_channels[:, t], file, scale=None, normalize_input=False
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if label_path is not None:
|
|
300
|
+
|
|
301
|
+
lbl = locate_labels(pos, population=mode, frames=t)
|
|
302
|
+
if lbl is None:
|
|
303
|
+
continue
|
|
304
|
+
|
|
305
|
+
if trajectories is not None:
|
|
306
|
+
|
|
307
|
+
positions_at_t = trajectories.loc[
|
|
308
|
+
trajectories[column_labels["time"]] == t
|
|
309
|
+
].copy()
|
|
310
|
+
|
|
311
|
+
if do_features:
|
|
312
|
+
feature_table = measure_features(
|
|
313
|
+
img,
|
|
314
|
+
lbl,
|
|
315
|
+
features=features,
|
|
316
|
+
border_dist=border_distances,
|
|
317
|
+
channels=channel_names,
|
|
318
|
+
haralick_options=haralick_options,
|
|
319
|
+
verbose=False,
|
|
320
|
+
normalisation_list=background_correction,
|
|
321
|
+
spot_detection=spot_detection,
|
|
322
|
+
)
|
|
323
|
+
if trajectories is None:
|
|
324
|
+
positions_at_t = _extract_coordinates_from_features(
|
|
325
|
+
feature_table, timepoint=t
|
|
326
|
+
)
|
|
327
|
+
column_labels = {
|
|
328
|
+
"track": "ID",
|
|
329
|
+
"time": column_labels["time"],
|
|
330
|
+
"x": column_labels["x"],
|
|
331
|
+
"y": column_labels["y"],
|
|
332
|
+
}
|
|
333
|
+
feature_table.rename(
|
|
334
|
+
columns={"centroid-1": "POSITION_X", "centroid-0": "POSITION_Y"},
|
|
335
|
+
inplace=True,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
if do_iso_intensities and not trajectories is None:
|
|
339
|
+
iso_table = measure_isotropic_intensity(
|
|
340
|
+
positions_at_t,
|
|
341
|
+
img,
|
|
342
|
+
channels=channel_names,
|
|
343
|
+
intensity_measurement_radii=intensity_measurement_radii,
|
|
344
|
+
column_labels=column_labels,
|
|
345
|
+
operations=isotropic_operations,
|
|
346
|
+
verbose=False,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
if do_iso_intensities and do_features and not trajectories is None:
|
|
350
|
+
measurements_at_t = iso_table.merge(
|
|
351
|
+
feature_table, how="outer", on="class_id", suffixes=("_delme", "")
|
|
352
|
+
)
|
|
353
|
+
measurements_at_t = measurements_at_t[
|
|
354
|
+
[c for c in measurements_at_t.columns if not c.endswith("_delme")]
|
|
355
|
+
]
|
|
356
|
+
elif do_iso_intensities * (not do_features) * (not trajectories is None):
|
|
357
|
+
measurements_at_t = iso_table
|
|
358
|
+
elif do_features:
|
|
359
|
+
measurements_at_t = positions_at_t.merge(
|
|
360
|
+
feature_table, how="outer", on="class_id", suffixes=("_delme", "")
|
|
361
|
+
)
|
|
362
|
+
measurements_at_t = measurements_at_t[
|
|
363
|
+
[c for c in measurements_at_t.columns if not c.endswith("_delme")]
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
measurements_at_t = center_of_mass_to_abs_coordinates(measurements_at_t)
|
|
367
|
+
measurements_at_t = measure_radial_distance_to_center(
|
|
368
|
+
measurements_at_t, volume=img.shape, column_labels=column_labels
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
if measurements_at_t is not None:
|
|
372
|
+
measurements_at_t[column_labels["time"]] = t
|
|
373
|
+
timestep_dataframes.append(measurements_at_t)
|
|
374
|
+
|
|
375
|
+
return
|
|
277
376
|
|
|
278
377
|
|
|
279
378
|
print(f"Starting the measurements with {n_threads} thread(s)...")
|
|
@@ -285,33 +384,35 @@ indices = list(range(img_num_channels.shape[1]))
|
|
|
285
384
|
chunks = np.array_split(indices, n_threads)
|
|
286
385
|
|
|
287
386
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
387
|
+
results = executor.map(measure_index, chunks)
|
|
388
|
+
try:
|
|
389
|
+
for i, return_value in enumerate(results):
|
|
390
|
+
print(f"Thread {i} output check: ", return_value)
|
|
391
|
+
except Exception as e:
|
|
392
|
+
print("Exception: ", e)
|
|
393
|
+
|
|
394
|
+
print("Done.")
|
|
294
395
|
|
|
295
|
-
print('Done.')
|
|
296
396
|
|
|
397
|
+
if len(timestep_dataframes) > 0:
|
|
297
398
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
df = pd.concat(timestep_dataframes)
|
|
399
|
+
df = pd.concat(timestep_dataframes)
|
|
301
400
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
401
|
+
if trajectories is not None:
|
|
402
|
+
df = df.sort_values(by=[column_labels["track"], column_labels["time"]])
|
|
403
|
+
df = df.dropna(subset=[column_labels["track"]])
|
|
404
|
+
else:
|
|
405
|
+
df["ID"] = np.arange(len(df))
|
|
406
|
+
df = df.sort_values(by=[column_labels["time"], "ID"])
|
|
308
407
|
|
|
309
|
-
|
|
310
|
-
|
|
408
|
+
df = df.reset_index(drop=True)
|
|
409
|
+
df = _remove_invalid_cols(df)
|
|
311
410
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
411
|
+
df.to_csv(pos + os.sep.join(["output", "tables", table_name]), index=False)
|
|
412
|
+
print(
|
|
413
|
+
f'Measurement table successfully exported in {os.sep.join(["output", "tables"])}...'
|
|
414
|
+
)
|
|
415
|
+
print("Done.")
|
|
315
416
|
else:
|
|
316
|
-
|
|
317
|
-
|
|
417
|
+
print("No measurement could be performed. Check your inputs.")
|
|
418
|
+
print("Done.")
|