celldetective 1.5.0b0__py3-none-any.whl → 1.5.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/_version.py +1 -1
- celldetective/gui/InitWindow.py +26 -4
- celldetective/gui/base/components.py +20 -1
- celldetective/gui/base_annotator.py +11 -7
- celldetective/gui/event_annotator.py +51 -1060
- celldetective/gui/interactions_block.py +55 -25
- celldetective/gui/interactive_timeseries_viewer.py +11 -1
- celldetective/gui/measure_annotator.py +968 -0
- celldetective/gui/process_block.py +88 -34
- celldetective/gui/viewers/base_viewer.py +134 -3
- celldetective/gui/viewers/contour_viewer.py +4 -4
- celldetective/gui/workers.py +124 -10
- celldetective/measure.py +3 -0
- celldetective/napari/utils.py +29 -19
- celldetective/processes/downloader.py +122 -96
- celldetective/processes/load_table.py +55 -0
- celldetective/processes/measure_cells.py +107 -81
- celldetective/processes/track_cells.py +39 -39
- celldetective/segmentation.py +1 -1
- celldetective/tracking.py +9 -0
- celldetective/utils/data_loaders.py +21 -1
- celldetective/utils/downloaders.py +38 -0
- celldetective/utils/maths.py +14 -1
- {celldetective-1.5.0b0.dist-info → celldetective-1.5.0b2.dist-info}/METADATA +1 -1
- {celldetective-1.5.0b0.dist-info → celldetective-1.5.0b2.dist-info}/RECORD +29 -27
- {celldetective-1.5.0b0.dist-info → celldetective-1.5.0b2.dist-info}/WHEEL +0 -0
- {celldetective-1.5.0b0.dist-info → celldetective-1.5.0b2.dist-info}/entry_points.txt +0 -0
- {celldetective-1.5.0b0.dist-info → celldetective-1.5.0b2.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.5.0b0.dist-info → celldetective-1.5.0b2.dist-info}/top_level.txt +0 -0
celldetective/napari/utils.py
CHANGED
|
@@ -27,6 +27,7 @@ from celldetective.utils.experiment import (
|
|
|
27
27
|
)
|
|
28
28
|
from celldetective.utils.parsing import config_section_to_dict
|
|
29
29
|
from celldetective import get_logger
|
|
30
|
+
from celldetective.gui.base.styles import Styles
|
|
30
31
|
|
|
31
32
|
logger = get_logger()
|
|
32
33
|
|
|
@@ -87,10 +88,16 @@ def control_tracks(
|
|
|
87
88
|
position += os.sep
|
|
88
89
|
|
|
89
90
|
position = position.replace("\\", "/")
|
|
91
|
+
if progress_callback:
|
|
92
|
+
progress_callback(0)
|
|
93
|
+
|
|
90
94
|
stack, labels = locate_stack_and_labels(
|
|
91
95
|
position, prefix=prefix, population=population
|
|
92
96
|
)
|
|
93
97
|
|
|
98
|
+
if progress_callback:
|
|
99
|
+
progress_callback(25)
|
|
100
|
+
|
|
94
101
|
return view_tracks_in_napari(
|
|
95
102
|
position,
|
|
96
103
|
population,
|
|
@@ -130,7 +137,13 @@ def view_tracks_in_napari(
|
|
|
130
137
|
Updated
|
|
131
138
|
"""
|
|
132
139
|
|
|
140
|
+
print(f"DEBUG: view_tracks_in_napari called with pos={position}, pop={population}")
|
|
133
141
|
df, df_path = get_position_table(position, population=population, return_path=True)
|
|
142
|
+
print(f"DEBUG: get_position_table returned df={df is not None}")
|
|
143
|
+
|
|
144
|
+
if progress_callback:
|
|
145
|
+
progress_callback(50)
|
|
146
|
+
|
|
134
147
|
if df is None:
|
|
135
148
|
print("Please compute trajectories first... Abort...")
|
|
136
149
|
return None
|
|
@@ -144,12 +157,18 @@ def view_tracks_in_napari(
|
|
|
144
157
|
|
|
145
158
|
if (labels is not None) * relabel:
|
|
146
159
|
print("Replacing the cell mask labels with the track ID...")
|
|
160
|
+
|
|
161
|
+
def wrapped_callback(p):
|
|
162
|
+
if progress_callback:
|
|
163
|
+
return progress_callback(50 + int(p * 0.5))
|
|
164
|
+
return True
|
|
165
|
+
|
|
147
166
|
labels = relabel_segmentation(
|
|
148
167
|
labels,
|
|
149
168
|
df,
|
|
150
169
|
exclude_nans=True,
|
|
151
170
|
threads=threads,
|
|
152
|
-
progress_callback=
|
|
171
|
+
progress_callback=wrapped_callback,
|
|
153
172
|
)
|
|
154
173
|
if labels is None:
|
|
155
174
|
return None
|
|
@@ -186,9 +205,12 @@ def launch_napari_viewer(
|
|
|
186
205
|
shared_data,
|
|
187
206
|
contrast_limits,
|
|
188
207
|
flush_memory=True,
|
|
208
|
+
block=True,
|
|
209
|
+
progress_callback=None,
|
|
189
210
|
):
|
|
190
211
|
|
|
191
212
|
viewer = napari.Viewer()
|
|
213
|
+
|
|
192
214
|
if stack is not None:
|
|
193
215
|
viewer.add_image(
|
|
194
216
|
stack,
|
|
@@ -196,6 +218,7 @@ def launch_napari_viewer(
|
|
|
196
218
|
colormap=["gray"] * stack.shape[-1],
|
|
197
219
|
contrast_limits=contrast_limits,
|
|
198
220
|
)
|
|
221
|
+
|
|
199
222
|
if labels is not None:
|
|
200
223
|
labels_layer = viewer.add_labels(
|
|
201
224
|
labels.astype(int), name="segmentation", opacity=0.4
|
|
@@ -274,8 +297,6 @@ def launch_napari_viewer(
|
|
|
274
297
|
def export_table_widget():
|
|
275
298
|
return export_modifications()
|
|
276
299
|
|
|
277
|
-
from celldetective.gui.base.styles import Styles
|
|
278
|
-
|
|
279
300
|
export_table_widget.native.setStyleSheet(Styles().button_style_sheet)
|
|
280
301
|
|
|
281
302
|
def label_changed(event):
|
|
@@ -426,9 +447,9 @@ def launch_napari_viewer(
|
|
|
426
447
|
|
|
427
448
|
shared_data["df"] = df
|
|
428
449
|
|
|
429
|
-
viewer.show(block=
|
|
450
|
+
viewer.show(block=block)
|
|
430
451
|
|
|
431
|
-
if flush_memory:
|
|
452
|
+
if flush_memory and block:
|
|
432
453
|
|
|
433
454
|
# temporary fix for slight napari memory leak
|
|
434
455
|
for i in range(10000):
|
|
@@ -781,8 +802,6 @@ def control_segmentation_napari(
|
|
|
781
802
|
def export_widget():
|
|
782
803
|
return export_annotation()
|
|
783
804
|
|
|
784
|
-
from celldetective.gui.base.styles import Styles
|
|
785
|
-
|
|
786
805
|
stack, labels = locate_stack_and_labels(
|
|
787
806
|
position, prefix=prefix, population=population
|
|
788
807
|
)
|
|
@@ -889,22 +908,13 @@ def correct_annotation(filename):
|
|
|
889
908
|
stack,
|
|
890
909
|
channel_axis=-1,
|
|
891
910
|
colormap=["gray"] * stack.shape[-1],
|
|
892
|
-
|
|
911
|
+
contrast_limits=contrast_limits,
|
|
893
912
|
)
|
|
894
913
|
viewer.add_labels(labels, name="segmentation", opacity=0.4)
|
|
895
914
|
viewer.window.add_dock_widget(save_widget, area="right")
|
|
896
|
-
|
|
915
|
+
save_widget.native.setStyleSheet(Styles().button_style_sheet)
|
|
897
916
|
|
|
898
|
-
|
|
899
|
-
for i in range(100):
|
|
900
|
-
try:
|
|
901
|
-
viewer.layers.pop()
|
|
902
|
-
except:
|
|
903
|
-
pass
|
|
904
|
-
del viewer
|
|
905
|
-
del stack
|
|
906
|
-
del labels
|
|
907
|
-
gc.collect()
|
|
917
|
+
viewer.show(block=False)
|
|
908
918
|
|
|
909
919
|
|
|
910
920
|
def _view_on_napari(tracks=None, stack=None, labels=None):
|
|
@@ -11,101 +11,127 @@ import time
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
import json
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
class DownloadProcess(Process):
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
17
|
+
def __init__(self, queue=None, process_args=None, *args, **kwargs):
|
|
18
|
+
|
|
19
|
+
super().__init__(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
if process_args is not None:
|
|
22
|
+
for key, value in process_args.items():
|
|
23
|
+
setattr(self, key, value)
|
|
24
|
+
|
|
25
|
+
self.queue = queue
|
|
26
|
+
self.progress = True
|
|
27
|
+
|
|
28
|
+
# Get celldetective package root
|
|
29
|
+
current_dir = os.path.dirname(os.path.realpath(__file__))
|
|
30
|
+
package_root = os.path.dirname(current_dir)
|
|
31
|
+
zenodo_json = os.path.join(package_root, "links", "zenodo.json")
|
|
32
|
+
# print(f"{zenodo_json=}")
|
|
33
|
+
|
|
34
|
+
with open(zenodo_json, "r") as f:
|
|
35
|
+
zenodo_json = json.load(f)
|
|
36
|
+
all_files = list(zenodo_json["files"]["entries"].keys())
|
|
37
|
+
all_files_short = [f.replace(".zip", "") for f in all_files]
|
|
38
|
+
zenodo_url = zenodo_json["links"]["files"].replace("api/", "")
|
|
39
|
+
full_links = ["/".join([zenodo_url, f]) for f in all_files]
|
|
40
|
+
index = all_files_short.index(self.file)
|
|
41
|
+
|
|
42
|
+
self.zip_url = full_links[index]
|
|
43
|
+
self.path_to_zip_file = os.sep.join([self.output_dir, "temp.zip"])
|
|
44
|
+
|
|
45
|
+
self.sum_done = 0
|
|
46
|
+
self.t0 = time.time()
|
|
47
|
+
|
|
48
|
+
def download_url_to_file(self, url, dst):
|
|
49
|
+
try:
|
|
50
|
+
file_size = None
|
|
51
|
+
ssl._create_default_https_context = ssl._create_unverified_context
|
|
52
|
+
u = urlopen(url)
|
|
53
|
+
meta = u.info()
|
|
54
|
+
if hasattr(meta, "getheaders"):
|
|
55
|
+
content_length = meta.getheaders("Content-Length")
|
|
56
|
+
else:
|
|
57
|
+
content_length = meta.get_all("Content-Length")
|
|
58
|
+
if content_length is not None and len(content_length) > 0:
|
|
59
|
+
file_size = int(content_length[0])
|
|
60
|
+
# We deliberately save it in a temp file and move it after
|
|
61
|
+
dst = os.path.expanduser(dst)
|
|
62
|
+
dst_dir = os.path.dirname(dst)
|
|
63
|
+
f = tempfile.NamedTemporaryFile(delete=False, dir=dst_dir)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
with tqdm(
|
|
67
|
+
total=file_size,
|
|
68
|
+
disable=not self.progress,
|
|
69
|
+
unit="B",
|
|
70
|
+
unit_scale=True,
|
|
71
|
+
unit_divisor=1024,
|
|
72
|
+
) as pbar:
|
|
73
|
+
while True:
|
|
74
|
+
buffer = u.read(8192) # 8192
|
|
75
|
+
if len(buffer) == 0:
|
|
76
|
+
break
|
|
77
|
+
f.write(buffer)
|
|
78
|
+
pbar.update(len(buffer))
|
|
79
|
+
self.sum_done += len(buffer) / file_size * 100
|
|
80
|
+
mean_exec_per_step = (time.time() - self.t0) / (
|
|
81
|
+
self.sum_done * file_size / 100 + 1
|
|
82
|
+
)
|
|
83
|
+
pred_time = (
|
|
84
|
+
file_size - (self.sum_done * file_size / 100 + 1)
|
|
85
|
+
) * mean_exec_per_step
|
|
86
|
+
self.queue.put([self.sum_done, pred_time])
|
|
87
|
+
f.close()
|
|
88
|
+
shutil.move(f.name, dst)
|
|
89
|
+
finally:
|
|
90
|
+
f.close()
|
|
91
|
+
if os.path.exists(f.name):
|
|
92
|
+
os.remove(f.name)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print("No internet connection: ", e)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def run(self):
|
|
98
|
+
|
|
99
|
+
self.download_url_to_file(rf"{self.zip_url}", self.path_to_zip_file)
|
|
100
|
+
with zipfile.ZipFile(self.path_to_zip_file, "r") as zip_ref:
|
|
101
|
+
zip_ref.extractall(self.output_dir)
|
|
102
|
+
|
|
103
|
+
file_to_rename = glob(
|
|
104
|
+
os.sep.join(
|
|
105
|
+
[
|
|
106
|
+
self.output_dir,
|
|
107
|
+
self.file,
|
|
108
|
+
"*[!.json][!.png][!.h5][!.csv][!.npy][!.tif][!.ini]",
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
if (
|
|
113
|
+
len(file_to_rename) > 0
|
|
114
|
+
and not file_to_rename[0].endswith(os.sep)
|
|
115
|
+
and not self.file.startswith("demo")
|
|
116
|
+
):
|
|
117
|
+
os.rename(
|
|
118
|
+
file_to_rename[0], os.sep.join([self.output_dir, self.file, self.file])
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
os.remove(self.path_to_zip_file)
|
|
122
|
+
self.queue.put([100, 0])
|
|
123
|
+
time.sleep(0.5)
|
|
124
|
+
|
|
125
|
+
# Send end signal
|
|
126
|
+
self.queue.put("finished")
|
|
127
|
+
self.queue.close()
|
|
128
|
+
|
|
129
|
+
def end_process(self):
|
|
130
|
+
|
|
131
|
+
self.terminate()
|
|
132
|
+
self.queue.put("finished")
|
|
133
|
+
|
|
134
|
+
def abort_process(self):
|
|
135
|
+
|
|
136
|
+
self.terminate()
|
|
137
|
+
self.queue.put("error")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from multiprocessing import Process
|
|
3
|
+
from celldetective.utils.data_loaders import load_experiment_tables
|
|
4
|
+
from celldetective import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TableLoaderProcess(Process):
|
|
10
|
+
|
|
11
|
+
def __init__(self, queue=None, process_args=None, *args, **kwargs):
|
|
12
|
+
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
14
|
+
|
|
15
|
+
if process_args is not None:
|
|
16
|
+
for key, value in process_args.items():
|
|
17
|
+
setattr(self, key, value)
|
|
18
|
+
|
|
19
|
+
self.queue = queue
|
|
20
|
+
|
|
21
|
+
def run(self):
|
|
22
|
+
|
|
23
|
+
def progress(well_progress, pos_progress):
|
|
24
|
+
# Check for cancellation if needed?
|
|
25
|
+
# The runner checks queue for instructions? No, runner closes queue.
|
|
26
|
+
# But here we can just push updates.
|
|
27
|
+
self.queue.put(
|
|
28
|
+
{
|
|
29
|
+
"well_progress": well_progress,
|
|
30
|
+
"pos_progress": pos_progress,
|
|
31
|
+
"status": f"Loading tables... Well {well_progress}%, Position {pos_progress}%",
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
return True # continue
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
self.queue.put({"status": "Started loading..."})
|
|
38
|
+
|
|
39
|
+
df = load_experiment_tables(
|
|
40
|
+
experiment=self.experiment,
|
|
41
|
+
population=self.population,
|
|
42
|
+
well_option=self.well_option,
|
|
43
|
+
position_option=self.position_option,
|
|
44
|
+
return_pos_info=False,
|
|
45
|
+
progress_callback=progress,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self.queue.put({"status": "finished", "result": df})
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error(f"Table loading failed: {e}")
|
|
52
|
+
self.queue.put({"status": "error", "message": str(e)})
|
|
53
|
+
|
|
54
|
+
def end_process(self):
|
|
55
|
+
self.terminate()
|
|
@@ -310,6 +310,9 @@ class MeasurementProcess(Process):
|
|
|
310
310
|
|
|
311
311
|
for t in tqdm(indices, desc="frame"):
|
|
312
312
|
|
|
313
|
+
measurements_at_t = None
|
|
314
|
+
perform_measurement = True
|
|
315
|
+
|
|
313
316
|
if self.file is not None:
|
|
314
317
|
img = load_frames(
|
|
315
318
|
self.img_num_channels[:, t],
|
|
@@ -321,92 +324,115 @@ class MeasurementProcess(Process):
|
|
|
321
324
|
if self.label_path is not None:
|
|
322
325
|
lbl = locate_labels(self.pos, population=self.mode, frames=t)
|
|
323
326
|
if lbl is None:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if
|
|
327
|
-
|
|
328
|
-
if self.
|
|
329
|
-
#
|
|
330
|
-
if
|
|
331
|
-
|
|
332
|
-
|
|
327
|
+
perform_measurement = False
|
|
328
|
+
|
|
329
|
+
if perform_measurement:
|
|
330
|
+
|
|
331
|
+
if self.trajectories is not None:
|
|
332
|
+
# Optimized access
|
|
333
|
+
if self.frame_slices is not None:
|
|
334
|
+
# Check if frame t is in our precomputed slices
|
|
335
|
+
if t in self.frame_slices:
|
|
336
|
+
start, end = self.frame_slices[t]
|
|
337
|
+
positions_at_t = self.trajectories.iloc[start:end].copy()
|
|
338
|
+
else:
|
|
339
|
+
# Empty frame for trajectories
|
|
340
|
+
positions_at_t = pd.DataFrame(
|
|
341
|
+
columns=self.trajectories.columns
|
|
342
|
+
)
|
|
333
343
|
else:
|
|
334
|
-
#
|
|
335
|
-
positions_at_t =
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
344
|
+
# Fallback or original method (should not be reached if optimized)
|
|
345
|
+
positions_at_t = self.trajectories.loc[
|
|
346
|
+
self.trajectories[self.column_labels["time"]] == t
|
|
347
|
+
].copy()
|
|
348
|
+
|
|
349
|
+
if self.do_features:
|
|
350
|
+
feature_table = measure_features(
|
|
351
|
+
img,
|
|
352
|
+
lbl,
|
|
353
|
+
features=self.features,
|
|
354
|
+
border_dist=self.border_distances,
|
|
355
|
+
channels=self.channel_names,
|
|
356
|
+
haralick_options=self.haralick_options,
|
|
357
|
+
verbose=False,
|
|
358
|
+
normalisation_list=self.background_correction,
|
|
359
|
+
spot_detection=self.spot_detection,
|
|
360
|
+
)
|
|
361
|
+
if self.trajectories is None:
|
|
362
|
+
positions_at_t = _extract_coordinates_from_features(
|
|
363
|
+
feature_table, timepoint=t
|
|
364
|
+
)
|
|
365
|
+
column_labels = {
|
|
366
|
+
"track": "ID",
|
|
367
|
+
"time": self.column_labels["time"],
|
|
368
|
+
"x": self.column_labels["x"],
|
|
369
|
+
"y": self.column_labels["y"],
|
|
370
|
+
}
|
|
371
|
+
feature_table.rename(
|
|
372
|
+
columns={
|
|
373
|
+
"centroid-1": "POSITION_X",
|
|
374
|
+
"centroid-0": "POSITION_Y",
|
|
375
|
+
},
|
|
376
|
+
inplace=True,
|
|
357
377
|
)
|
|
358
|
-
column_labels = {
|
|
359
|
-
"track": "ID",
|
|
360
|
-
"time": self.column_labels["time"],
|
|
361
|
-
"x": self.column_labels["x"],
|
|
362
|
-
"y": self.column_labels["y"],
|
|
363
|
-
}
|
|
364
|
-
feature_table.rename(
|
|
365
|
-
columns={"centroid-1": "POSITION_X", "centroid-0": "POSITION_Y"},
|
|
366
|
-
inplace=True,
|
|
367
|
-
)
|
|
368
378
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
+
if self.do_iso_intensities and not self.trajectories is None:
|
|
380
|
+
iso_table = measure_isotropic_intensity(
|
|
381
|
+
positions_at_t,
|
|
382
|
+
img,
|
|
383
|
+
channels=self.channel_names,
|
|
384
|
+
intensity_measurement_radii=self.intensity_measurement_radii,
|
|
385
|
+
column_labels=self.column_labels,
|
|
386
|
+
operations=self.isotropic_operations,
|
|
387
|
+
verbose=False,
|
|
388
|
+
)
|
|
379
389
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
390
|
+
if (
|
|
391
|
+
self.do_iso_intensities
|
|
392
|
+
and self.do_features
|
|
393
|
+
and not self.trajectories is None
|
|
394
|
+
):
|
|
395
|
+
measurements_at_t = iso_table.merge(
|
|
396
|
+
feature_table,
|
|
397
|
+
how="outer",
|
|
398
|
+
on="class_id",
|
|
399
|
+
suffixes=("_delme", ""),
|
|
400
|
+
)
|
|
401
|
+
measurements_at_t = measurements_at_t[
|
|
402
|
+
[
|
|
403
|
+
c
|
|
404
|
+
for c in measurements_at_t.columns
|
|
405
|
+
if not c.endswith("_delme")
|
|
406
|
+
]
|
|
407
|
+
]
|
|
408
|
+
elif (
|
|
409
|
+
self.do_iso_intensities
|
|
410
|
+
* (not self.do_features)
|
|
411
|
+
* (not self.trajectories is None)
|
|
412
|
+
):
|
|
413
|
+
measurements_at_t = iso_table
|
|
414
|
+
elif self.do_features:
|
|
415
|
+
measurements_at_t = positions_at_t.merge(
|
|
416
|
+
feature_table,
|
|
417
|
+
how="outer",
|
|
418
|
+
on="class_id",
|
|
419
|
+
suffixes=("_delme", ""),
|
|
420
|
+
)
|
|
421
|
+
measurements_at_t = measurements_at_t[
|
|
422
|
+
[
|
|
423
|
+
c
|
|
424
|
+
for c in measurements_at_t.columns
|
|
425
|
+
if not c.endswith("_delme")
|
|
426
|
+
]
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
measurements_at_t = center_of_mass_to_abs_coordinates(measurements_at_t)
|
|
430
|
+
|
|
431
|
+
measurements_at_t = measure_radial_distance_to_center(
|
|
432
|
+
measurements_at_t,
|
|
433
|
+
volume=img.shape,
|
|
434
|
+
column_labels=self.column_labels,
|
|
400
435
|
)
|
|
401
|
-
measurements_at_t = measurements_at_t[
|
|
402
|
-
[c for c in measurements_at_t.columns if not c.endswith("_delme")]
|
|
403
|
-
]
|
|
404
|
-
|
|
405
|
-
measurements_at_t = center_of_mass_to_abs_coordinates(measurements_at_t)
|
|
406
|
-
|
|
407
|
-
measurements_at_t = measure_radial_distance_to_center(
|
|
408
|
-
measurements_at_t, volume=img.shape, column_labels=self.column_labels
|
|
409
|
-
)
|
|
410
436
|
|
|
411
437
|
self.sum_done += 1
|
|
412
438
|
data = {}
|