celldetective 1.0.2.post1__py3-none-any.whl → 1.1.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/__main__.py +2 -2
- celldetective/events.py +2 -44
- celldetective/filters.py +4 -5
- celldetective/gui/__init__.py +1 -1
- celldetective/gui/analyze_block.py +37 -10
- celldetective/gui/btrack_options.py +24 -23
- celldetective/gui/classifier_widget.py +62 -19
- celldetective/gui/configure_new_exp.py +32 -35
- celldetective/gui/control_panel.py +115 -81
- celldetective/gui/gui_utils.py +674 -396
- celldetective/gui/json_readers.py +7 -6
- celldetective/gui/layouts.py +755 -0
- celldetective/gui/measurement_options.py +168 -487
- celldetective/gui/neighborhood_options.py +322 -270
- celldetective/gui/plot_measurements.py +1114 -0
- celldetective/gui/plot_signals_ui.py +20 -20
- celldetective/gui/process_block.py +449 -169
- celldetective/gui/retrain_segmentation_model_options.py +27 -26
- celldetective/gui/retrain_signal_model_options.py +25 -24
- celldetective/gui/seg_model_loader.py +31 -27
- celldetective/gui/signal_annotator.py +2326 -2295
- celldetective/gui/signal_annotator_options.py +18 -16
- celldetective/gui/styles.py +16 -1
- celldetective/gui/survival_ui.py +61 -39
- celldetective/gui/tableUI.py +60 -23
- celldetective/gui/thresholds_gui.py +68 -66
- celldetective/gui/viewers.py +596 -0
- celldetective/io.py +234 -23
- celldetective/measure.py +37 -32
- celldetective/neighborhood.py +495 -27
- celldetective/preprocessing.py +683 -0
- celldetective/scripts/analyze_signals.py +7 -0
- celldetective/scripts/measure_cells.py +12 -0
- celldetective/scripts/segment_cells.py +5 -0
- celldetective/scripts/track_cells.py +11 -0
- celldetective/signals.py +221 -98
- celldetective/tracking.py +0 -1
- celldetective/utils.py +178 -36
- celldetective-1.1.0.dist-info/METADATA +305 -0
- celldetective-1.1.0.dist-info/RECORD +80 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_events.py +28 -0
- tests/test_filters.py +24 -0
- tests/test_io.py +70 -0
- tests/test_measure.py +141 -0
- tests/test_neighborhood.py +70 -0
- tests/test_segmentation.py +93 -0
- tests/test_signals.py +135 -0
- tests/test_tracking.py +164 -0
- tests/test_utils.py +71 -0
- celldetective-1.0.2.post1.dist-info/METADATA +0 -221
- celldetective-1.0.2.post1.dist-info/RECORD +0 -66
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/LICENSE +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/WHEEL +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
|
|
2
|
-
QMessageBox, QWidget, QLineEdit, QScrollArea
|
|
1
|
+
from PyQt5.QtWidgets import QFrame, QGridLayout, QRadioButton, QButtonGroup, QGroupBox, QComboBox,QTabWidget,QSizePolicy,QListWidget, QDialog, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
|
|
2
|
+
QMessageBox, QWidget, QLineEdit, QScrollArea, QSpacerItem, QLayout, QSizePolicy
|
|
3
3
|
from PyQt5.QtCore import Qt, QSize
|
|
4
4
|
from superqt.fonticon import icon
|
|
5
5
|
from fonticon_mdi6 import MDI6
|
|
6
6
|
import gc
|
|
7
|
+
from PyQt5.QtGui import QIcon, QDoubleValidator, QIntValidator
|
|
7
8
|
|
|
8
9
|
from celldetective.gui.signal_annotator import MeasureAnnotator
|
|
9
10
|
from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, control_tracking_btrack, load_experiment_tables
|
|
10
|
-
from celldetective.io import locate_segmentation_model
|
|
11
|
+
from celldetective.io import locate_segmentation_model, auto_load_number_of_frames, load_frames
|
|
11
12
|
from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
|
|
12
13
|
from celldetective.gui.gui_utils import QHSeperationLine
|
|
13
14
|
from celldetective.segmentation import segment_at_position, segment_from_threshold_at_position
|
|
14
15
|
from celldetective.tracking import track_at_position
|
|
15
16
|
from celldetective.measure import measure_at_position
|
|
16
17
|
from celldetective.signals import analyze_signals_at_position
|
|
18
|
+
from celldetective.utils import extract_experiment_channels
|
|
17
19
|
import numpy as np
|
|
18
20
|
from glob import glob
|
|
19
21
|
from natsort import natsorted
|
|
20
|
-
from superqt import QLabeledDoubleSlider
|
|
22
|
+
from superqt import QLabeledDoubleSlider, QLabeledSlider, QLabeledRangeSlider, QLabeledSlider, QLabeledDoubleRangeSlider
|
|
21
23
|
import os
|
|
22
24
|
import pandas as pd
|
|
23
25
|
from tqdm import tqdm
|
|
@@ -25,26 +27,37 @@ from celldetective.gui.gui_utils import center_window
|
|
|
25
27
|
from tifffile import imwrite
|
|
26
28
|
import json
|
|
27
29
|
import psutil
|
|
28
|
-
from celldetective.neighborhood import compute_neighborhood_at_position
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
from celldetective.neighborhood import compute_neighborhood_at_position, compute_contact_neighborhood_at_position
|
|
31
|
+
from celldetective.gui.gui_utils import FigureCanvas
|
|
32
|
+
import matplotlib.pyplot as plt
|
|
33
|
+
from celldetective.filters import std_filter, median_filter, gauss_filter
|
|
34
|
+
from stardist import fill_label_holes
|
|
35
|
+
from celldetective.preprocessing import correct_background_model_free, estimate_background_per_condition, correct_background_model
|
|
36
|
+
from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
|
|
37
|
+
from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout
|
|
38
|
+
from celldetective.gui.viewers import StackVisualizer, CellSizeViewer, ThresholdedStackVisualizer
|
|
39
|
+
from celldetective.gui.layouts import BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, OperationLayout
|
|
40
|
+
from celldetective.gui import Styles
|
|
41
|
+
|
|
42
|
+
class ProcessPanel(QFrame, Styles):
|
|
43
|
+
def __init__(self, parent_window, mode):
|
|
32
44
|
|
|
33
45
|
super().__init__()
|
|
34
|
-
self.
|
|
46
|
+
self.parent_window = parent_window
|
|
35
47
|
self.mode = mode
|
|
36
|
-
self.exp_channels = self.
|
|
37
|
-
self.exp_dir = self.
|
|
48
|
+
self.exp_channels = self.parent_window.exp_channels
|
|
49
|
+
self.exp_dir = self.parent_window.exp_dir
|
|
50
|
+
self.exp_config = self.parent_window.exp_config
|
|
51
|
+
self.movie_prefix = self.parent_window.movie_prefix
|
|
38
52
|
self.threshold_config_targets = None
|
|
39
53
|
self.threshold_config_effectors = None
|
|
40
|
-
self.wells = np.array(self.
|
|
54
|
+
self.wells = np.array(self.parent_window.wells,dtype=str)
|
|
41
55
|
self.cellpose_calibrated = False
|
|
42
56
|
self.stardist_calibrated = False
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
58
|
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
47
59
|
self.grid = QGridLayout(self)
|
|
60
|
+
self.grid.setContentsMargins(5,5,5,5)
|
|
48
61
|
self.generate_header()
|
|
49
62
|
|
|
50
63
|
def generate_header(self):
|
|
@@ -67,14 +80,14 @@ class ProcessPanel(QFrame):
|
|
|
67
80
|
self.select_all_btn.setIconSize(QSize(20, 20))
|
|
68
81
|
self.all_ticked = False
|
|
69
82
|
self.select_all_btn.clicked.connect(self.tick_all_actions)
|
|
70
|
-
self.select_all_btn.setStyleSheet(self.
|
|
83
|
+
self.select_all_btn.setStyleSheet(self.button_select_all)
|
|
71
84
|
self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
|
|
72
85
|
#self.to_disable.append(self.all_tc_actions)
|
|
73
86
|
|
|
74
87
|
self.collapse_btn = QPushButton()
|
|
75
88
|
self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
76
89
|
self.collapse_btn.setIconSize(QSize(25, 25))
|
|
77
|
-
self.collapse_btn.setStyleSheet(self.
|
|
90
|
+
self.collapse_btn.setStyleSheet(self.button_select_all)
|
|
78
91
|
self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
|
|
79
92
|
|
|
80
93
|
self.populate_contents()
|
|
@@ -88,22 +101,23 @@ class ProcessPanel(QFrame):
|
|
|
88
101
|
if self.ContentsFrame.isHidden():
|
|
89
102
|
self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
90
103
|
self.collapse_btn.setIconSize(QSize(20, 20))
|
|
91
|
-
self.
|
|
104
|
+
self.parent_window.scroll.setMinimumHeight(int(550))
|
|
92
105
|
#self.parent.w.adjustSize()
|
|
93
|
-
self.
|
|
106
|
+
self.parent_window.adjustSize()
|
|
94
107
|
#self.parent.scroll.adjustSize()
|
|
95
108
|
else:
|
|
96
109
|
self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
97
110
|
self.collapse_btn.setIconSize(QSize(20, 20))
|
|
98
111
|
#self.parent.w.adjustSize()
|
|
99
112
|
#self.parent.adjustSize()
|
|
100
|
-
self.
|
|
101
|
-
self.
|
|
113
|
+
self.parent_window.scroll.setMinimumHeight(min(int(880), int(0.8*self.parent_window.screen_height)))
|
|
114
|
+
self.parent_window.scroll.setMinimumWidth(425)
|
|
102
115
|
|
|
103
116
|
#self.parent.scroll.adjustSize()
|
|
104
117
|
|
|
105
118
|
def populate_contents(self):
|
|
106
119
|
self.ContentsFrame = QFrame()
|
|
120
|
+
self.ContentsFrame.setContentsMargins(5,5,5,5)
|
|
107
121
|
self.grid_contents = QGridLayout(self.ContentsFrame)
|
|
108
122
|
self.grid_contents.setContentsMargins(0,0,0,0)
|
|
109
123
|
self.generate_segmentation_options()
|
|
@@ -113,7 +127,7 @@ class ProcessPanel(QFrame):
|
|
|
113
127
|
|
|
114
128
|
self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
|
|
115
129
|
self.view_tab_btn = QPushButton("View table")
|
|
116
|
-
self.view_tab_btn.setStyleSheet(self.
|
|
130
|
+
self.view_tab_btn.setStyleSheet(self.button_style_sheet_2)
|
|
117
131
|
self.view_tab_btn.clicked.connect(self.view_table_ui)
|
|
118
132
|
self.view_tab_btn.setToolTip('View table')
|
|
119
133
|
self.view_tab_btn.setIcon(icon(MDI6.table,color="#1565c0"))
|
|
@@ -123,7 +137,7 @@ class ProcessPanel(QFrame):
|
|
|
123
137
|
|
|
124
138
|
self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
|
|
125
139
|
self.submit_btn = QPushButton("Submit")
|
|
126
|
-
self.submit_btn.setStyleSheet(self.
|
|
140
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet_2)
|
|
127
141
|
self.submit_btn.clicked.connect(self.process_population)
|
|
128
142
|
self.grid_contents.addWidget(self.submit_btn, 11, 0, 1, 4)
|
|
129
143
|
|
|
@@ -147,7 +161,7 @@ class ProcessPanel(QFrame):
|
|
|
147
161
|
self.classify_btn.setIcon(icon(MDI6.scatter_plot, color="black"))
|
|
148
162
|
self.classify_btn.setIconSize(QSize(20, 20))
|
|
149
163
|
self.classify_btn.setToolTip("Classify data.")
|
|
150
|
-
self.classify_btn.setStyleSheet(self.
|
|
164
|
+
self.classify_btn.setStyleSheet(self.button_select_all)
|
|
151
165
|
self.classify_btn.clicked.connect(self.open_classifier_ui)
|
|
152
166
|
measure_layout.addWidget(self.classify_btn, 5) #4,2,1,1, alignment=Qt.AlignRight
|
|
153
167
|
|
|
@@ -155,7 +169,7 @@ class ProcessPanel(QFrame):
|
|
|
155
169
|
self.check_measurements_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
|
|
156
170
|
self.check_measurements_btn.setIconSize(QSize(20, 20))
|
|
157
171
|
self.check_measurements_btn.setToolTip("Explore measurements in-situ.")
|
|
158
|
-
self.check_measurements_btn.setStyleSheet(self.
|
|
172
|
+
self.check_measurements_btn.setStyleSheet(self.button_select_all)
|
|
159
173
|
self.check_measurements_btn.clicked.connect(self.check_measurements)
|
|
160
174
|
measure_layout.addWidget(self.check_measurements_btn, 5)
|
|
161
175
|
|
|
@@ -164,7 +178,7 @@ class ProcessPanel(QFrame):
|
|
|
164
178
|
self.measurements_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
|
|
165
179
|
self.measurements_config_btn.setIconSize(QSize(20, 20))
|
|
166
180
|
self.measurements_config_btn.setToolTip("Configure measurements.")
|
|
167
|
-
self.measurements_config_btn.setStyleSheet(self.
|
|
181
|
+
self.measurements_config_btn.setStyleSheet(self.button_select_all)
|
|
168
182
|
self.measurements_config_btn.clicked.connect(self.open_measurement_configuration_ui)
|
|
169
183
|
measure_layout.addWidget(self.measurements_config_btn, 5) #4,2,1,1, alignment=Qt.AlignRight
|
|
170
184
|
|
|
@@ -191,14 +205,14 @@ class ProcessPanel(QFrame):
|
|
|
191
205
|
self.check_signals_btn.setIconSize(QSize(20, 20))
|
|
192
206
|
self.check_signals_btn.clicked.connect(self.check_signals)
|
|
193
207
|
self.check_signals_btn.setToolTip("Explore signals in-situ.")
|
|
194
|
-
self.check_signals_btn.setStyleSheet(self.
|
|
208
|
+
self.check_signals_btn.setStyleSheet(self.button_select_all)
|
|
195
209
|
signal_hlayout.addWidget(self.check_signals_btn, 6)
|
|
196
210
|
|
|
197
211
|
self.config_signal_annotator_btn = QPushButton()
|
|
198
212
|
self.config_signal_annotator_btn.setIcon(icon(MDI6.cog_outline,color="black"))
|
|
199
213
|
self.config_signal_annotator_btn.setIconSize(QSize(20, 20))
|
|
200
214
|
self.config_signal_annotator_btn.setToolTip("Configure the dynamic visualizer.")
|
|
201
|
-
self.config_signal_annotator_btn.setStyleSheet(self.
|
|
215
|
+
self.config_signal_annotator_btn.setStyleSheet(self.button_select_all)
|
|
202
216
|
self.config_signal_annotator_btn.clicked.connect(self.open_signal_annotator_configuration_ui)
|
|
203
217
|
signal_hlayout.addWidget(self.config_signal_annotator_btn, 6)
|
|
204
218
|
|
|
@@ -220,7 +234,7 @@ class ProcessPanel(QFrame):
|
|
|
220
234
|
self.train_signal_model_btn.setToolTip("Open a dialog box to create a new target segmentation model.")
|
|
221
235
|
self.train_signal_model_btn.setIcon(icon(MDI6.redo_variant,color='black'))
|
|
222
236
|
self.train_signal_model_btn.setIconSize(QSize(20, 20))
|
|
223
|
-
self.train_signal_model_btn.setStyleSheet(self.
|
|
237
|
+
self.train_signal_model_btn.setStyleSheet(self.button_style_sheet_3)
|
|
224
238
|
model_zoo_layout.addWidget(self.train_signal_model_btn, 5)
|
|
225
239
|
self.train_signal_model_btn.clicked.connect(self.open_signal_model_config_ui)
|
|
226
240
|
|
|
@@ -255,7 +269,7 @@ class ProcessPanel(QFrame):
|
|
|
255
269
|
# self.show_track_table_btn.setIcon(icon(MDI6.table,color="black"))
|
|
256
270
|
# self.show_track_table_btn.setIconSize(QSize(20, 20))
|
|
257
271
|
# self.show_track_table_btn.setToolTip("Show trajectories table.")
|
|
258
|
-
# self.show_track_table_btn.setStyleSheet(self.
|
|
272
|
+
# self.show_track_table_btn.setStyleSheet(self.button_select_all)
|
|
259
273
|
# #self.show_track_table_btn.clicked.connect(self.display_trajectory_table)
|
|
260
274
|
# self.show_track_table_btn.setEnabled(False)
|
|
261
275
|
# grid_track.addWidget(self.show_track_table_btn, 6) #4,3,1,1, alignment=Qt.AlignLeft
|
|
@@ -264,7 +278,7 @@ class ProcessPanel(QFrame):
|
|
|
264
278
|
self.check_tracking_result_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
|
|
265
279
|
self.check_tracking_result_btn.setIconSize(QSize(20, 20))
|
|
266
280
|
self.check_tracking_result_btn.setToolTip("View raw bTrack output in napari.")
|
|
267
|
-
self.check_tracking_result_btn.setStyleSheet(self.
|
|
281
|
+
self.check_tracking_result_btn.setStyleSheet(self.button_select_all)
|
|
268
282
|
self.check_tracking_result_btn.clicked.connect(self.open_napari_tracking)
|
|
269
283
|
self.check_tracking_result_btn.setEnabled(False)
|
|
270
284
|
grid_track.addWidget(self.check_tracking_result_btn, 6) #4,3,1,1, alignment=Qt.AlignLeft
|
|
@@ -273,7 +287,7 @@ class ProcessPanel(QFrame):
|
|
|
273
287
|
self.track_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
|
|
274
288
|
self.track_config_btn.setIconSize(QSize(20, 20))
|
|
275
289
|
self.track_config_btn.setToolTip("Configure tracking.")
|
|
276
|
-
self.track_config_btn.setStyleSheet(self.
|
|
290
|
+
self.track_config_btn.setStyleSheet(self.button_select_all)
|
|
277
291
|
self.track_config_btn.clicked.connect(self.open_tracking_configuration_ui)
|
|
278
292
|
grid_track.addWidget(self.track_config_btn, 6) #4,2,1,1, alignment=Qt.AlignRight
|
|
279
293
|
|
|
@@ -301,7 +315,7 @@ class ProcessPanel(QFrame):
|
|
|
301
315
|
self.check_seg_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
|
|
302
316
|
self.check_seg_btn.setIconSize(QSize(20, 20))
|
|
303
317
|
self.check_seg_btn.clicked.connect(self.check_segmentation)
|
|
304
|
-
self.check_seg_btn.setStyleSheet(self.
|
|
318
|
+
self.check_seg_btn.setStyleSheet(self.button_select_all)
|
|
305
319
|
self.check_seg_btn.setToolTip("View segmentation output in napari.")
|
|
306
320
|
#self.check_seg_btn.setEnabled(False)
|
|
307
321
|
#self.to_disable.append(self.control_target_seg)
|
|
@@ -321,7 +335,7 @@ class ProcessPanel(QFrame):
|
|
|
321
335
|
self.upload_model_btn = QPushButton("UPLOAD")
|
|
322
336
|
self.upload_model_btn.setIcon(icon(MDI6.upload,color="black"))
|
|
323
337
|
self.upload_model_btn.setIconSize(QSize(20, 20))
|
|
324
|
-
self.upload_model_btn.setStyleSheet(self.
|
|
338
|
+
self.upload_model_btn.setStyleSheet(self.button_style_sheet_3)
|
|
325
339
|
self.upload_model_btn.setToolTip("Upload a new segmentation model\n(Deep learning or threshold-based).")
|
|
326
340
|
model_zoo_layout.addWidget(self.upload_model_btn, 5)
|
|
327
341
|
self.upload_model_btn.clicked.connect(self.upload_segmentation_model)
|
|
@@ -331,7 +345,7 @@ class ProcessPanel(QFrame):
|
|
|
331
345
|
self.train_btn.setToolTip("Train or retrain a segmentation model\non newly annotated data.")
|
|
332
346
|
self.train_btn.setIcon(icon(MDI6.redo_variant,color='black'))
|
|
333
347
|
self.train_btn.setIconSize(QSize(20, 20))
|
|
334
|
-
self.train_btn.setStyleSheet(self.
|
|
348
|
+
self.train_btn.setStyleSheet(self.button_style_sheet_3)
|
|
335
349
|
self.train_btn.clicked.connect(self.open_segmentation_model_config_ui)
|
|
336
350
|
model_zoo_layout.addWidget(self.train_btn, 5)
|
|
337
351
|
# self.train_button_tc.clicked.connect(self.train_stardist_model_tc)
|
|
@@ -344,7 +358,7 @@ class ProcessPanel(QFrame):
|
|
|
344
358
|
|
|
345
359
|
def check_segmentation(self):
|
|
346
360
|
|
|
347
|
-
if not os.path.exists(os.sep.join([self.
|
|
361
|
+
if not os.path.exists(os.sep.join([self.parent_window.pos,f'labels_{self.mode}', os.sep])):
|
|
348
362
|
msgBox = QMessageBox()
|
|
349
363
|
msgBox.setIcon(QMessageBox.Question)
|
|
350
364
|
msgBox.setText("No labels can be found for this position. Do you want to annotate from scratch?")
|
|
@@ -354,18 +368,18 @@ class ProcessPanel(QFrame):
|
|
|
354
368
|
if returnValue == QMessageBox.No:
|
|
355
369
|
return None
|
|
356
370
|
else:
|
|
357
|
-
os.mkdir(os.sep.join([self.
|
|
358
|
-
lbl = np.zeros((self.
|
|
359
|
-
for i in range(self.
|
|
360
|
-
imwrite(os.sep.join([self.
|
|
371
|
+
os.mkdir(os.sep.join([self.parent_window.pos,f'labels_{self.mode}']))
|
|
372
|
+
lbl = np.zeros((self.parent_window.shape_x, self.parent_window.shape_y), dtype=int)
|
|
373
|
+
for i in range(self.parent_window.len_movie):
|
|
374
|
+
imwrite(os.sep.join([self.parent_window.pos,f'labels_{self.mode}', str(i).zfill(4)+'.tif']), lbl)
|
|
361
375
|
|
|
362
376
|
#self.freeze()
|
|
363
377
|
#QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
364
|
-
test = self.
|
|
378
|
+
test = self.parent_window.locate_selected_position()
|
|
365
379
|
if test:
|
|
366
380
|
print('Memory use: ', dict(psutil.virtual_memory()._asdict()))
|
|
367
381
|
try:
|
|
368
|
-
control_segmentation_napari(self.
|
|
382
|
+
control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
|
|
369
383
|
except Exception as e:
|
|
370
384
|
msgBox = QMessageBox()
|
|
371
385
|
msgBox.setIcon(QMessageBox.Warning)
|
|
@@ -377,14 +391,14 @@ class ProcessPanel(QFrame):
|
|
|
377
391
|
|
|
378
392
|
def check_signals(self):
|
|
379
393
|
|
|
380
|
-
test = self.
|
|
394
|
+
test = self.parent_window.locate_selected_position()
|
|
381
395
|
if test:
|
|
382
396
|
self.SignalAnnotator = SignalAnnotator(self)
|
|
383
397
|
self.SignalAnnotator.show()
|
|
384
398
|
|
|
385
399
|
def check_measurements(self):
|
|
386
400
|
|
|
387
|
-
test = self.
|
|
401
|
+
test = self.parent_window.locate_selected_position()
|
|
388
402
|
if test:
|
|
389
403
|
self.MeasureAnnotator = MeasureAnnotator(self)
|
|
390
404
|
self.MeasureAnnotator.show()
|
|
@@ -507,11 +521,11 @@ class ProcessPanel(QFrame):
|
|
|
507
521
|
|
|
508
522
|
def process_population(self):
|
|
509
523
|
|
|
510
|
-
if self.
|
|
524
|
+
if self.parent_window.well_list.currentText()=="*":
|
|
511
525
|
self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
|
|
512
526
|
else:
|
|
513
|
-
self.well_index = [self.
|
|
514
|
-
print(f"Processing well {self.
|
|
527
|
+
self.well_index = [self.parent_window.well_list.currentIndex()]
|
|
528
|
+
print(f"Processing well {self.parent_window.well_list.currentText()}...")
|
|
515
529
|
|
|
516
530
|
# self.freeze()
|
|
517
531
|
# QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
@@ -523,7 +537,7 @@ class ProcessPanel(QFrame):
|
|
|
523
537
|
|
|
524
538
|
loop_iter=0
|
|
525
539
|
|
|
526
|
-
if self.
|
|
540
|
+
if self.parent_window.position_list.currentText()=="*":
|
|
527
541
|
msgBox = QMessageBox()
|
|
528
542
|
msgBox.setIcon(QMessageBox.Question)
|
|
529
543
|
msgBox.setText("If you continue, all positions will be processed.\nDo you want to proceed?")
|
|
@@ -546,7 +560,15 @@ class ProcessPanel(QFrame):
|
|
|
546
560
|
|
|
547
561
|
layout = QVBoxLayout()
|
|
548
562
|
self.diamWidget.setLayout(layout)
|
|
549
|
-
|
|
563
|
+
|
|
564
|
+
self.view_diameter_btn = QPushButton()
|
|
565
|
+
self.view_diameter_btn.setStyleSheet(self.button_select_all)
|
|
566
|
+
self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
|
|
567
|
+
self.view_diameter_btn.setToolTip("View stack.")
|
|
568
|
+
self.view_diameter_btn.setIconSize(QSize(20, 20))
|
|
569
|
+
self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
|
|
570
|
+
|
|
571
|
+
self.diameter_le = ThresholdLineEdit(init_value=40, connected_buttons=[self.view_diameter_btn],placeholder='cell diameter in pixels', value_type='float')
|
|
550
572
|
|
|
551
573
|
self.cellpose_channel_cb = [QComboBox() for i in range(2)]
|
|
552
574
|
self.cellpose_channel_template = ['brightfield_channel', 'live_nuclei_channel']
|
|
@@ -563,12 +585,21 @@ class ProcessPanel(QFrame):
|
|
|
563
585
|
else:
|
|
564
586
|
self.cellpose_channel_cb[k].addItems(list(self.exp_channels))
|
|
565
587
|
idx = self.cellpose_channel_cb[k].findText(self.cellpose_channel_template[k])
|
|
566
|
-
|
|
588
|
+
if idx>0:
|
|
589
|
+
self.cellpose_channel_cb[k].setCurrentIndex(idx)
|
|
590
|
+
else:
|
|
591
|
+
self.cellpose_channel_cb[k].setCurrentIndex(0)
|
|
592
|
+
|
|
593
|
+
if k==1:
|
|
594
|
+
idx = self.cellpose_channel_cb[k].findText('None')
|
|
595
|
+
self.cellpose_channel_cb[k].setCurrentIndex(idx)
|
|
596
|
+
|
|
567
597
|
layout.addLayout(hbox_channel)
|
|
568
598
|
|
|
569
599
|
hbox = QHBoxLayout()
|
|
570
600
|
hbox.addWidget(QLabel('diameter [px]: '), 33)
|
|
571
|
-
hbox.addWidget(self.diameter_le,
|
|
601
|
+
hbox.addWidget(self.diameter_le, 61)
|
|
602
|
+
hbox.addWidget(self.view_diameter_btn)
|
|
572
603
|
layout.addLayout(hbox)
|
|
573
604
|
|
|
574
605
|
self.flow_slider = QLabeledDoubleSlider()
|
|
@@ -625,7 +656,11 @@ class ProcessPanel(QFrame):
|
|
|
625
656
|
else:
|
|
626
657
|
self.stardist_channel_cb[k].addItems(list(self.exp_channels))
|
|
627
658
|
idx = self.stardist_channel_cb[k].findText(self.stardist_channel_template[k])
|
|
628
|
-
|
|
659
|
+
if idx>0:
|
|
660
|
+
self.stardist_channel_cb[k].setCurrentIndex(idx)
|
|
661
|
+
else:
|
|
662
|
+
self.stardist_channel_cb[k].setCurrentIndex(0)
|
|
663
|
+
|
|
629
664
|
layout.addLayout(hbox_channel)
|
|
630
665
|
|
|
631
666
|
self.set_stardist_scale_btn = QPushButton('set')
|
|
@@ -639,15 +674,15 @@ class ProcessPanel(QFrame):
|
|
|
639
674
|
|
|
640
675
|
for w_idx in self.well_index:
|
|
641
676
|
|
|
642
|
-
pos = self.
|
|
643
|
-
if self.
|
|
677
|
+
pos = self.parent_window.positions[w_idx]
|
|
678
|
+
if self.parent_window.position_list.currentText()=="*":
|
|
644
679
|
pos_indices = np.linspace(0,len(pos)-1,len(pos),dtype=int)
|
|
645
680
|
print("Processing all positions...")
|
|
646
681
|
else:
|
|
647
|
-
pos_indices = natsorted([pos.index(self.
|
|
648
|
-
print(f"Processing position {self.
|
|
682
|
+
pos_indices = natsorted([pos.index(self.parent_window.position_list.currentText())])
|
|
683
|
+
print(f"Processing position {self.parent_window.position_list.currentText()}...")
|
|
649
684
|
|
|
650
|
-
well = self.
|
|
685
|
+
well = self.parent_window.wells[w_idx]
|
|
651
686
|
|
|
652
687
|
for pos_idx in pos_indices:
|
|
653
688
|
|
|
@@ -661,7 +696,7 @@ class ProcessPanel(QFrame):
|
|
|
661
696
|
|
|
662
697
|
if self.segment_action.isChecked():
|
|
663
698
|
|
|
664
|
-
if len(glob(os.sep.join([self.pos, f'labels_{self.mode}','*.tif'])))>0 and self.
|
|
699
|
+
if len(glob(os.sep.join([self.pos, f'labels_{self.mode}','*.tif'])))>0 and self.parent_window.position_list.currentText()!="*":
|
|
665
700
|
msgBox = QMessageBox()
|
|
666
701
|
msgBox.setIcon(QMessageBox.Question)
|
|
667
702
|
msgBox.setText("Labels have already been produced for this position. Do you want to segment again?")
|
|
@@ -683,12 +718,12 @@ class ProcessPanel(QFrame):
|
|
|
683
718
|
return None
|
|
684
719
|
else:
|
|
685
720
|
print(f"Segmentation from threshold config: {self.threshold_config}")
|
|
686
|
-
segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.
|
|
721
|
+
segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
|
|
687
722
|
else:
|
|
688
|
-
segment_at_position(self.pos, self.mode, self.model_name, stack_prefix=self.
|
|
723
|
+
segment_at_position(self.pos, self.mode, self.model_name, stack_prefix=self.parent_window.movie_prefix, use_gpu=self.parent_window.parent_window.use_gpu, threads=self.parent_window.parent_window.n_threads)
|
|
689
724
|
|
|
690
725
|
if self.track_action.isChecked():
|
|
691
|
-
if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and self.
|
|
726
|
+
if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and self.parent_window.position_list.currentText()!="*":
|
|
692
727
|
msgBox = QMessageBox()
|
|
693
728
|
msgBox.setIcon(QMessageBox.Question)
|
|
694
729
|
msgBox.setText("A trajectory set already exists. Previously annotated data for\nthis position will be lost. Do you want to proceed?")
|
|
@@ -697,10 +732,10 @@ class ProcessPanel(QFrame):
|
|
|
697
732
|
returnValue = msgBox.exec()
|
|
698
733
|
if returnValue == QMessageBox.No:
|
|
699
734
|
return None
|
|
700
|
-
track_at_position(self.pos, self.mode, threads=self.
|
|
735
|
+
track_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
|
|
701
736
|
|
|
702
737
|
if self.measure_action.isChecked():
|
|
703
|
-
measure_at_position(self.pos, self.mode, threads=self.
|
|
738
|
+
measure_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
|
|
704
739
|
|
|
705
740
|
table = os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])
|
|
706
741
|
if self.signal_analysis_action.isChecked() and os.path.exists(table):
|
|
@@ -712,7 +747,7 @@ class ProcessPanel(QFrame):
|
|
|
712
747
|
print(cols, 'class_color in cols')
|
|
713
748
|
colors = list(table['class_color'].to_numpy())
|
|
714
749
|
if 'tab:orange' in colors or 'tab:cyan' in colors:
|
|
715
|
-
if self.
|
|
750
|
+
if self.parent_window.position_list.currentText()!="*":
|
|
716
751
|
msgBox = QMessageBox()
|
|
717
752
|
msgBox.setIcon(QMessageBox.Question)
|
|
718
753
|
msgBox.setText("The signals of the cells in the position appear to have been annotated... Do you want to proceed?")
|
|
@@ -725,15 +760,35 @@ class ProcessPanel(QFrame):
|
|
|
725
760
|
|
|
726
761
|
|
|
727
762
|
# self.stack = None
|
|
728
|
-
self.
|
|
763
|
+
self.parent_window.update_position_options()
|
|
729
764
|
if self.segment_action.isChecked():
|
|
730
765
|
self.segment_action.setChecked(False)
|
|
731
766
|
|
|
732
767
|
# QApplication.restoreOverrideCursor()
|
|
733
768
|
# self.unfreeze()
|
|
734
769
|
|
|
770
|
+
def view_current_stack_with_scale_bar(self):
|
|
771
|
+
|
|
772
|
+
self.parent_window.locate_image()
|
|
773
|
+
if self.parent_window.current_stack is not None:
|
|
774
|
+
self.viewer = CellSizeViewer(
|
|
775
|
+
initial_diameter = float(self.diameter_le.text().replace(',','.')),
|
|
776
|
+
parent_le = self.diameter_le,
|
|
777
|
+
stack_path=self.parent_window.current_stack,
|
|
778
|
+
window_title=f'Position {self.parent_window.position_list.currentText()}',
|
|
779
|
+
frame_slider = True,
|
|
780
|
+
contrast_slider = True,
|
|
781
|
+
channel_cb = True,
|
|
782
|
+
channel_names = self.parent_window.exp_channels,
|
|
783
|
+
n_channels = self.parent_window.nbr_channels,
|
|
784
|
+
PxToUm = 1,
|
|
785
|
+
)
|
|
786
|
+
self.viewer.show()
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
|
|
735
790
|
def open_napari_tracking(self):
|
|
736
|
-
control_tracking_btrack(self.
|
|
791
|
+
control_tracking_btrack(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
|
|
737
792
|
|
|
738
793
|
def view_table_ui(self):
|
|
739
794
|
|
|
@@ -744,7 +799,7 @@ class ProcessPanel(QFrame):
|
|
|
744
799
|
plot_mode = 'plot_track_signals'
|
|
745
800
|
if 'TRACK_ID' not in list(self.df.columns):
|
|
746
801
|
plot_mode = 'static'
|
|
747
|
-
self.tab_ui = TableUI(self.df, f"Well {self.
|
|
802
|
+
self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population=self.mode, plot_mode=plot_mode)
|
|
748
803
|
self.tab_ui.show()
|
|
749
804
|
else:
|
|
750
805
|
print('Table could not be loaded...')
|
|
@@ -782,12 +837,12 @@ class ProcessPanel(QFrame):
|
|
|
782
837
|
|
|
783
838
|
"""
|
|
784
839
|
|
|
785
|
-
self.well_option = self.
|
|
840
|
+
self.well_option = self.parent_window.well_list.currentIndex()
|
|
786
841
|
if self.well_option==len(self.wells):
|
|
787
842
|
wo = '*'
|
|
788
843
|
else:
|
|
789
844
|
wo = self.well_option
|
|
790
|
-
self.position_option = self.
|
|
845
|
+
self.position_option = self.parent_window.position_list.currentIndex()
|
|
791
846
|
if self.position_option==0:
|
|
792
847
|
po = '*'
|
|
793
848
|
else:
|
|
@@ -799,9 +854,9 @@ class ProcessPanel(QFrame):
|
|
|
799
854
|
|
|
800
855
|
def set_cellpose_scale(self):
|
|
801
856
|
|
|
802
|
-
scale = self.
|
|
857
|
+
scale = self.parent_window.PxToUm * float(self.diameter_le.get_threshold()) / 30.0
|
|
803
858
|
if self.model_name=="CP_nuclei":
|
|
804
|
-
scale = self.
|
|
859
|
+
scale = self.parent_window.PxToUm * float(self.diameter_le.get_threshold()) / 17.0
|
|
805
860
|
flow_thresh = self.flow_slider.value()
|
|
806
861
|
cellprob_thresh = self.cellprob_slider.value()
|
|
807
862
|
model_complete_path = locate_segmentation_model(self.model_name)
|
|
@@ -848,14 +903,15 @@ class ProcessPanel(QFrame):
|
|
|
848
903
|
self.process_population()
|
|
849
904
|
|
|
850
905
|
|
|
851
|
-
class NeighPanel(QFrame):
|
|
852
|
-
def __init__(self,
|
|
906
|
+
class NeighPanel(QFrame, Styles):
|
|
907
|
+
def __init__(self, parent_window):
|
|
853
908
|
|
|
854
909
|
super().__init__()
|
|
855
|
-
self.
|
|
856
|
-
self.exp_channels = self.
|
|
857
|
-
self.exp_dir = self.
|
|
858
|
-
self.wells = np.array(self.
|
|
910
|
+
self.parent_window = parent_window
|
|
911
|
+
self.exp_channels = self.parent_window.exp_channels
|
|
912
|
+
self.exp_dir = self.parent_window.exp_dir
|
|
913
|
+
self.wells = np.array(self.parent_window.wells,dtype=str)
|
|
914
|
+
self.protocols = []
|
|
859
915
|
|
|
860
916
|
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
861
917
|
self.grid = QGridLayout(self)
|
|
@@ -869,26 +925,21 @@ class NeighPanel(QFrame):
|
|
|
869
925
|
"""
|
|
870
926
|
|
|
871
927
|
panel_title = QLabel(f"NEIGHBORHOOD")
|
|
872
|
-
panel_title.setStyleSheet(
|
|
873
|
-
font-weight: bold;
|
|
874
|
-
padding: 0px;
|
|
875
|
-
""")
|
|
928
|
+
panel_title.setStyleSheet(self.block_title)
|
|
876
929
|
|
|
877
930
|
self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
|
|
878
931
|
|
|
879
|
-
self.select_all_btn = QPushButton()
|
|
880
|
-
self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
|
|
881
|
-
self.select_all_btn.setIconSize(QSize(20, 20))
|
|
882
|
-
self.all_ticked = False
|
|
883
|
-
#self.select_all_btn.
|
|
884
|
-
self.
|
|
885
|
-
self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
|
|
886
|
-
#self.to_disable.append(self.all_tc_actions)
|
|
932
|
+
# self.select_all_btn = QPushButton()
|
|
933
|
+
# self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
|
|
934
|
+
# self.select_all_btn.setIconSize(QSize(20, 20))
|
|
935
|
+
# self.all_ticked = False
|
|
936
|
+
# self.select_all_btn.setStyleSheet(self.button_select_all)
|
|
937
|
+
# self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
|
|
887
938
|
|
|
888
939
|
self.collapse_btn = QPushButton()
|
|
889
940
|
self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
890
941
|
self.collapse_btn.setIconSize(QSize(25, 25))
|
|
891
|
-
self.collapse_btn.setStyleSheet(self.
|
|
942
|
+
self.collapse_btn.setStyleSheet(self.button_select_all)
|
|
892
943
|
self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
|
|
893
944
|
|
|
894
945
|
self.populate_contents()
|
|
@@ -899,22 +950,28 @@ class NeighPanel(QFrame):
|
|
|
899
950
|
self.ContentsFrame.hide()
|
|
900
951
|
|
|
901
952
|
def collapse_advanced(self):
|
|
953
|
+
|
|
902
954
|
if self.ContentsFrame.isHidden():
|
|
903
955
|
self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
904
956
|
self.collapse_btn.setIconSize(QSize(20, 20))
|
|
905
|
-
self.
|
|
906
|
-
self.parent.adjustSize()
|
|
957
|
+
self.parent_window.scroll.setMinimumHeight(int(550))
|
|
958
|
+
#self.parent.w.adjustSize()
|
|
959
|
+
self.parent_window.adjustSize()
|
|
907
960
|
else:
|
|
908
961
|
self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
909
962
|
self.collapse_btn.setIconSize(QSize(20, 20))
|
|
910
|
-
self.parent.w.adjustSize()
|
|
911
|
-
self.parent.adjustSize()
|
|
963
|
+
#self.parent.w.adjustSize()
|
|
964
|
+
#self.parent.adjustSize()
|
|
965
|
+
self.parent_window.scroll.setMinimumHeight(min(int(750), int(0.8*self.parent_window.screen_height)))
|
|
966
|
+
self.parent_window.scroll.setMinimumWidth(425)
|
|
967
|
+
|
|
912
968
|
|
|
913
969
|
def populate_contents(self):
|
|
914
970
|
|
|
915
971
|
self.ContentsFrame = QFrame()
|
|
916
972
|
self.grid_contents = QGridLayout(self.ContentsFrame)
|
|
917
973
|
self.grid_contents.setContentsMargins(0,0,0,0)
|
|
974
|
+
self.grid_contents.setSpacing(0)
|
|
918
975
|
|
|
919
976
|
|
|
920
977
|
# DISTANCE NEIGHBORHOOD
|
|
@@ -922,78 +979,108 @@ class NeighPanel(QFrame):
|
|
|
922
979
|
dist_neigh_hbox.setContentsMargins(0,0,0,0)
|
|
923
980
|
dist_neigh_hbox.setSpacing(0)
|
|
924
981
|
|
|
925
|
-
self.dist_neigh_action =
|
|
926
|
-
self.dist_neigh_action.setStyleSheet(
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
""")
|
|
930
|
-
self.dist_neigh_action.setIcon(icon(MDI6.circle_expand, color='black'))
|
|
931
|
-
self.dist_neigh_action.setToolTip("Match cells for which the center of mass is within a threshold distance of each other.")
|
|
982
|
+
self.dist_neigh_action = QLabel("ISOTROPIC DISTANCE THRESHOLD")
|
|
983
|
+
self.dist_neigh_action.setStyleSheet(self.action_lbl_style_sheet)
|
|
984
|
+
#self.dist_neigh_action.setIcon(icon(MDI6.circle_expand, color='black'))
|
|
985
|
+
self.dist_neigh_action.setToolTip("Define an isotropic neighborhood between the center of mass\nof the cells, within a threshold distance.")
|
|
932
986
|
#self.segment_action.toggled.connect(self.enable_segmentation_model_list)
|
|
933
987
|
#self.to_disable.append(self.segment_action)
|
|
934
|
-
dist_neigh_hbox.addWidget(self.dist_neigh_action, 95)
|
|
935
988
|
|
|
936
989
|
self.config_distance_neigh_btn = QPushButton()
|
|
937
|
-
self.config_distance_neigh_btn.setIcon(icon(MDI6.
|
|
990
|
+
self.config_distance_neigh_btn.setIcon(icon(MDI6.plus,color="black"))
|
|
938
991
|
self.config_distance_neigh_btn.setIconSize(QSize(20, 20))
|
|
939
|
-
self.config_distance_neigh_btn.setToolTip("Configure
|
|
940
|
-
self.config_distance_neigh_btn.setStyleSheet(self.
|
|
941
|
-
self.config_distance_neigh_btn.clicked.connect(self.
|
|
992
|
+
self.config_distance_neigh_btn.setToolTip("Configure.")
|
|
993
|
+
self.config_distance_neigh_btn.setStyleSheet(self.button_select_all)
|
|
994
|
+
self.config_distance_neigh_btn.clicked.connect(self.open_config_distance_threshold_neighborhood)
|
|
942
995
|
dist_neigh_hbox.addWidget(self.config_distance_neigh_btn,5)
|
|
996
|
+
dist_neigh_hbox.addWidget(self.dist_neigh_action, 95)
|
|
943
997
|
|
|
944
998
|
self.grid_contents.addLayout(dist_neigh_hbox, 0,0,1,4)
|
|
945
999
|
|
|
946
|
-
#
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
#
|
|
955
|
-
# """)
|
|
956
|
-
# self.mask_neigh_action.setIcon(icon(MDI6.domino_mask, color='black'))
|
|
957
|
-
# self.mask_neigh_action.setToolTip("Match cells that are co-localizing.")
|
|
958
|
-
# #self.segment_action.toggled.connect(self.enable_segmentation_model_list)
|
|
959
|
-
# #self.to_disable.append(self.segment_action)
|
|
960
|
-
# mask_neigh_hbox.addWidget(self.mask_neigh_action, 95)
|
|
1000
|
+
# DISTANCE NEIGHBORHOOD
|
|
1001
|
+
contact_neighborhood_layout = QHBoxLayout()
|
|
1002
|
+
contact_neighborhood_layout.setContentsMargins(0,0,0,0)
|
|
1003
|
+
contact_neighborhood_layout.setSpacing(0)
|
|
1004
|
+
|
|
1005
|
+
self.contact_neigh_action = QLabel("MASK CONTACT")
|
|
1006
|
+
self.contact_neigh_action.setToolTip("Identify touching cell masks, within a threshold edge distance.")
|
|
1007
|
+
self.contact_neigh_action.setStyleSheet(self.action_lbl_style_sheet)
|
|
1008
|
+
#self.contact_neigh_action.setIcon(icon(MDI6.transition_masked, color='black'))
|
|
961
1009
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1010
|
+
self.config_contact_neigh_btn = QPushButton()
|
|
1011
|
+
self.config_contact_neigh_btn.setIcon(icon(MDI6.plus,color="black"))
|
|
1012
|
+
self.config_contact_neigh_btn.setIconSize(QSize(20, 20))
|
|
1013
|
+
self.config_contact_neigh_btn.setToolTip("Configure.")
|
|
1014
|
+
self.config_contact_neigh_btn.setStyleSheet(self.button_select_all)
|
|
1015
|
+
self.config_contact_neigh_btn.clicked.connect(self.open_config_contact_neighborhood)
|
|
1016
|
+
contact_neighborhood_layout.addWidget(self.config_contact_neigh_btn,5)
|
|
1017
|
+
contact_neighborhood_layout.addWidget(self.contact_neigh_action, 95)
|
|
969
1018
|
|
|
970
|
-
|
|
1019
|
+
self.grid_contents.addLayout(contact_neighborhood_layout, 1,0,1,4)
|
|
971
1020
|
|
|
972
1021
|
self.grid_contents.addWidget(QHSeperationLine(), 2, 0, 1, 4)
|
|
1022
|
+
|
|
1023
|
+
self.delete_protocol_btn = QPushButton('')
|
|
1024
|
+
self.delete_protocol_btn.setStyleSheet(self.button_select_all)
|
|
1025
|
+
self.delete_protocol_btn.setIcon(icon(MDI6.trash_can, color="black"))
|
|
1026
|
+
self.delete_protocol_btn.setToolTip("Remove a neighborhood computation.")
|
|
1027
|
+
self.delete_protocol_btn.setIconSize(QSize(20, 20))
|
|
1028
|
+
self.delete_protocol_btn.clicked.connect(self.remove_protocol_from_list)
|
|
1029
|
+
|
|
1030
|
+
self.protocol_list_lbl = QLabel('Neighborhoods to compute: ')
|
|
1031
|
+
self.protocol_list = QListWidget()
|
|
1032
|
+
self.protocol_list.setToolTip("Neighborhoods to compute sequentially.")
|
|
1033
|
+
|
|
1034
|
+
list_header_layout = QHBoxLayout()
|
|
1035
|
+
list_header_layout.addWidget(self.protocol_list_lbl)
|
|
1036
|
+
list_header_layout.addWidget(self.delete_protocol_btn, alignment=Qt.AlignRight)
|
|
1037
|
+
self.grid_contents.addLayout(list_header_layout, 3, 0, 1, 4)
|
|
1038
|
+
self.grid_contents.addWidget(self.protocol_list, 4, 0, 1, 4)
|
|
1039
|
+
|
|
973
1040
|
self.submit_btn = QPushButton("Submit")
|
|
974
|
-
self.submit_btn.setStyleSheet(self.
|
|
1041
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet_2)
|
|
1042
|
+
self.submit_btn.setToolTip("Compute the neighborhoods of the selected positions.")
|
|
975
1043
|
self.submit_btn.clicked.connect(self.process_neighborhood)
|
|
976
|
-
self.grid_contents.addWidget(self.submit_btn,
|
|
1044
|
+
self.grid_contents.addWidget(self.submit_btn, 5, 0, 1, 4)
|
|
1045
|
+
|
|
1046
|
+
def remove_protocol_from_list(self):
|
|
1047
|
+
|
|
1048
|
+
current_item = self.protocol_list.currentRow()
|
|
1049
|
+
if current_item > -1:
|
|
1050
|
+
del self.protocols[current_item]
|
|
1051
|
+
self.protocol_list.takeItem(current_item)
|
|
1052
|
+
|
|
1053
|
+
def open_config_distance_threshold_neighborhood(self):
|
|
1054
|
+
|
|
1055
|
+
self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
|
|
1056
|
+
neighborhood_type='distance_threshold',
|
|
1057
|
+
neighborhood_parameter_name='threshold distance',
|
|
1058
|
+
)
|
|
1059
|
+
self.ConfigNeigh.show()
|
|
977
1060
|
|
|
978
|
-
def
|
|
1061
|
+
def open_config_contact_neighborhood(self):
|
|
979
1062
|
|
|
980
|
-
self.ConfigNeigh = ConfigNeighborhoods(self
|
|
1063
|
+
self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
|
|
1064
|
+
neighborhood_type='mask_contact',
|
|
1065
|
+
neighborhood_parameter_name='tolerance contact distance',
|
|
1066
|
+
)
|
|
981
1067
|
self.ConfigNeigh.show()
|
|
982
1068
|
|
|
1069
|
+
|
|
983
1070
|
def process_neighborhood(self):
|
|
984
1071
|
|
|
985
|
-
if self.
|
|
1072
|
+
if self.parent_window.well_list.currentText()=="*":
|
|
986
1073
|
self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
|
|
987
1074
|
else:
|
|
988
|
-
self.well_index = [self.
|
|
989
|
-
print(f"Processing well {self.
|
|
1075
|
+
self.well_index = [self.parent_window.well_list.currentIndex()]
|
|
1076
|
+
print(f"Processing well {self.parent_window.well_list.currentText()}...")
|
|
990
1077
|
|
|
991
1078
|
# self.freeze()
|
|
992
1079
|
# QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
993
1080
|
|
|
994
1081
|
loop_iter=0
|
|
995
1082
|
|
|
996
|
-
if self.
|
|
1083
|
+
if self.parent_window.position_list.currentText()=="*":
|
|
997
1084
|
msgBox = QMessageBox()
|
|
998
1085
|
msgBox.setIcon(QMessageBox.Question)
|
|
999
1086
|
msgBox.setText("If you continue, all positions will be processed.\nDo you want to proceed?")
|
|
@@ -1005,15 +1092,15 @@ class NeighPanel(QFrame):
|
|
|
1005
1092
|
|
|
1006
1093
|
for w_idx in self.well_index:
|
|
1007
1094
|
|
|
1008
|
-
pos = self.
|
|
1009
|
-
if self.
|
|
1095
|
+
pos = self.parent_window.positions[w_idx]
|
|
1096
|
+
if self.parent_window.position_list.currentText()=="*":
|
|
1010
1097
|
pos_indices = np.linspace(0,len(pos)-1,len(pos),dtype=int)
|
|
1011
1098
|
print("Processing all positions...")
|
|
1012
1099
|
else:
|
|
1013
|
-
pos_indices = natsorted([pos.index(self.
|
|
1014
|
-
print(f"Processing position {self.
|
|
1100
|
+
pos_indices = natsorted([pos.index(self.parent_window.position_list.currentText())])
|
|
1101
|
+
print(f"Processing position {self.parent_window.position_list.currentText()}...")
|
|
1015
1102
|
|
|
1016
|
-
well = self.
|
|
1103
|
+
well = self.parent_window.wells[w_idx]
|
|
1017
1104
|
|
|
1018
1105
|
for pos_idx in pos_indices:
|
|
1019
1106
|
|
|
@@ -1025,31 +1112,224 @@ class NeighPanel(QFrame):
|
|
|
1025
1112
|
if not os.path.exists(self.pos + os.sep.join(['output','tables'])+os.sep):
|
|
1026
1113
|
os.mkdir(self.pos + os.sep.join(['output','tables'])+os.sep)
|
|
1027
1114
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
print('Done.')
|
|
1115
|
+
for protocol in self.protocols:
|
|
1116
|
+
|
|
1117
|
+
if protocol['neighborhood_type']=='distance_threshold':
|
|
1118
|
+
|
|
1119
|
+
compute_neighborhood_at_position(self.pos,
|
|
1120
|
+
protocol['distance'],
|
|
1121
|
+
population=protocol['population'],
|
|
1122
|
+
theta_dist=None,
|
|
1123
|
+
img_shape=(self.parent_window.shape_x,self.parent_window.shape_y),
|
|
1124
|
+
return_tables=False,
|
|
1125
|
+
clear_neigh=protocol['clear_neigh'],
|
|
1126
|
+
event_time_col=protocol['event_time_col'],
|
|
1127
|
+
neighborhood_kwargs=protocol['neighborhood_kwargs'],
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
elif protocol['neighborhood_type']=='mask_contact':
|
|
1131
|
+
|
|
1132
|
+
compute_contact_neighborhood_at_position(self.pos,
|
|
1133
|
+
protocol['distance'],
|
|
1134
|
+
population=protocol['population'],
|
|
1135
|
+
theta_dist=None,
|
|
1136
|
+
img_shape=(self.parent_window.shape_x,self.parent_window.shape_y),
|
|
1137
|
+
return_tables=False,
|
|
1138
|
+
clear_neigh=protocol['clear_neigh'],
|
|
1139
|
+
event_time_col=protocol['event_time_col'],
|
|
1140
|
+
neighborhood_kwargs=protocol['neighborhood_kwargs'],
|
|
1141
|
+
)
|
|
1142
|
+
print('Done.')
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
class PreprocessingPanel(QFrame, Styles):
|
|
1146
|
+
|
|
1147
|
+
def __init__(self, parent_window):
|
|
1148
|
+
|
|
1149
|
+
super().__init__()
|
|
1150
|
+
self.parent_window = parent_window
|
|
1151
|
+
self.exp_channels = self.parent_window.exp_channels
|
|
1152
|
+
self.exp_dir = self.parent_window.exp_dir
|
|
1153
|
+
self.wells = np.array(self.parent_window.wells,dtype=str)
|
|
1154
|
+
exp_config = self.exp_dir + "config.ini"
|
|
1155
|
+
self.channel_names, self.channels = extract_experiment_channels(exp_config)
|
|
1156
|
+
self.channel_names = np.array(self.channel_names)
|
|
1157
|
+
self.background_correction = []
|
|
1158
|
+
self.onlyFloat = QDoubleValidator()
|
|
1159
|
+
self.onlyInt = QIntValidator()
|
|
1160
|
+
|
|
1161
|
+
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
1162
|
+
self.grid = QGridLayout(self)
|
|
1163
|
+
|
|
1164
|
+
self.generate_header()
|
|
1165
|
+
|
|
1166
|
+
def generate_header(self):
|
|
1167
|
+
|
|
1168
|
+
"""
|
|
1169
|
+
Read the mode and prepare a collapsable block to process a specific cell population.
|
|
1170
|
+
|
|
1171
|
+
"""
|
|
1172
|
+
|
|
1173
|
+
panel_title = QLabel(f"PREPROCESSING")
|
|
1174
|
+
panel_title.setStyleSheet("""
|
|
1175
|
+
font-weight: bold;
|
|
1176
|
+
padding: 0px;
|
|
1177
|
+
""")
|
|
1178
|
+
|
|
1179
|
+
self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
|
|
1180
|
+
|
|
1181
|
+
self.select_all_btn = QPushButton()
|
|
1182
|
+
self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
|
|
1183
|
+
self.select_all_btn.setIconSize(QSize(20, 20))
|
|
1184
|
+
self.all_ticked = False
|
|
1185
|
+
#self.select_all_btn.clicked.connect(self.tick_all_actions)
|
|
1186
|
+
self.select_all_btn.setStyleSheet(self.button_select_all)
|
|
1187
|
+
self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
|
|
1188
|
+
#self.to_disable.append(self.all_tc_actions)
|
|
1189
|
+
|
|
1190
|
+
self.collapse_btn = QPushButton()
|
|
1191
|
+
self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
1192
|
+
self.collapse_btn.setIconSize(QSize(25, 25))
|
|
1193
|
+
self.collapse_btn.setStyleSheet(self.button_select_all)
|
|
1194
|
+
self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
|
|
1195
|
+
|
|
1196
|
+
self.populate_contents()
|
|
1197
|
+
|
|
1198
|
+
self.grid.addWidget(self.ContentsFrame, 1, 0, 1, 4, alignment=Qt.AlignTop)
|
|
1199
|
+
self.collapse_btn.clicked.connect(lambda: self.ContentsFrame.setHidden(not self.ContentsFrame.isHidden()))
|
|
1200
|
+
self.collapse_btn.clicked.connect(self.collapse_advanced)
|
|
1201
|
+
self.ContentsFrame.hide()
|
|
1202
|
+
|
|
1203
|
+
def collapse_advanced(self):
|
|
1204
|
+
|
|
1205
|
+
if self.ContentsFrame.isHidden():
|
|
1206
|
+
self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
1207
|
+
self.collapse_btn.setIconSize(QSize(20, 20))
|
|
1208
|
+
self.parent_window.scroll.setMinimumHeight(int(550))
|
|
1209
|
+
#self.parent.w.adjustSize()
|
|
1210
|
+
self.parent_window.adjustSize()
|
|
1211
|
+
else:
|
|
1212
|
+
self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
1213
|
+
self.collapse_btn.setIconSize(QSize(20, 20))
|
|
1214
|
+
#self.parent.w.adjustSize()
|
|
1215
|
+
#self.parent.adjustSize()
|
|
1216
|
+
self.parent_window.scroll.setMinimumHeight(min(int(880), int(0.8*self.parent_window.screen_height)))
|
|
1217
|
+
self.parent_window.scroll.setMinimumWidth(425)
|
|
1218
|
+
|
|
1219
|
+
def populate_contents(self):
|
|
1220
|
+
|
|
1221
|
+
self.ContentsFrame = QFrame()
|
|
1222
|
+
self.grid_contents = QGridLayout(self.ContentsFrame)
|
|
1223
|
+
|
|
1224
|
+
self.model_free_correction_layout = BackgroundModelFreeCorrectionLayout(self)
|
|
1225
|
+
self.fit_correction_layout = BackgroundFitCorrectionLayout(self)
|
|
1226
|
+
|
|
1227
|
+
self.protocol_layout = ProtocolDesignerLayout(parent_window=self,
|
|
1228
|
+
tab_layouts=[self.fit_correction_layout, self.model_free_correction_layout],
|
|
1229
|
+
tab_names=['Fit', 'Model-free'],
|
|
1230
|
+
title='BACKGROUND CORRECTION',
|
|
1231
|
+
list_title='Corrections to apply:')
|
|
1232
|
+
self.grid_contents.addLayout(self.protocol_layout,0,0,1,4)
|
|
1233
|
+
self.submit_preprocessing_btn = QPushButton("Submit")
|
|
1234
|
+
self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet_2)
|
|
1235
|
+
self.submit_preprocessing_btn.clicked.connect(self.launch_preprocessing)
|
|
1236
|
+
self.grid_contents.addWidget(self.submit_preprocessing_btn, 1,0,1,4)
|
|
1237
|
+
|
|
1238
|
+
|
|
1239
|
+
def launch_preprocessing(self):
|
|
1240
|
+
|
|
1241
|
+
msgBox1 = QMessageBox()
|
|
1242
|
+
msgBox1.setIcon(QMessageBox.Question)
|
|
1243
|
+
msgBox1.setText("Do you want to apply the preprocessing\nto all wells and positions?")
|
|
1244
|
+
msgBox1.setWindowTitle("Selection")
|
|
1245
|
+
msgBox1.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
|
|
1246
|
+
returnValue = msgBox1.exec()
|
|
1247
|
+
if returnValue == QMessageBox.Cancel:
|
|
1248
|
+
return None
|
|
1249
|
+
elif returnValue == QMessageBox.Yes:
|
|
1250
|
+
self.parent_window.well_list.setCurrentIndex(self.parent_window.well_list.count()-1)
|
|
1251
|
+
self.parent_window.position_list.setCurrentIndex(0)
|
|
1252
|
+
elif returnValue == QMessageBox.No:
|
|
1253
|
+
msgBox2 = QMessageBox()
|
|
1254
|
+
msgBox2.setIcon(QMessageBox.Question)
|
|
1255
|
+
msgBox2.setText("Do you want to apply the preprocessing\nto the positions selected at the top only?")
|
|
1256
|
+
msgBox2.setWindowTitle("Selection")
|
|
1257
|
+
msgBox2.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
|
|
1258
|
+
returnValue = msgBox2.exec()
|
|
1259
|
+
if returnValue == QMessageBox.Cancel:
|
|
1260
|
+
return None
|
|
1261
|
+
if returnValue == QMessageBox.No:
|
|
1262
|
+
return None
|
|
1263
|
+
|
|
1264
|
+
print('Proceed with correction...')
|
|
1265
|
+
|
|
1266
|
+
if self.parent_window.well_list.currentText()=='*':
|
|
1267
|
+
well_option = "*"
|
|
1268
|
+
else:
|
|
1269
|
+
well_option = self.parent_window.well_list.currentIndex()
|
|
1270
|
+
|
|
1271
|
+
if self.parent_window.position_list.currentText()=='*':
|
|
1272
|
+
pos_option = "*"
|
|
1273
|
+
else:
|
|
1274
|
+
pos_option = self.parent_window.position_list.currentIndex()-1
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
for k,correction_protocol in enumerate(self.protocol_layout.protocols):
|
|
1278
|
+
|
|
1279
|
+
movie_prefix = None
|
|
1280
|
+
export_prefix = 'Corrected'
|
|
1281
|
+
if k>0:
|
|
1282
|
+
# switch source stack to cumulate multi-channel preprocessing
|
|
1283
|
+
movie_prefix = 'Corrected'
|
|
1284
|
+
export_prefix = None
|
|
1285
|
+
|
|
1286
|
+
if correction_protocol['correction_type']=='model-free':
|
|
1287
|
+
|
|
1288
|
+
correct_background_model_free(self.exp_dir,
|
|
1289
|
+
well_option=well_option,
|
|
1290
|
+
position_option=pos_option,
|
|
1291
|
+
export= True,
|
|
1292
|
+
return_stacks=False,
|
|
1293
|
+
show_progress_per_well = True,
|
|
1294
|
+
show_progress_per_pos = True,
|
|
1295
|
+
movie_prefix = movie_prefix,
|
|
1296
|
+
export_prefix = export_prefix,
|
|
1297
|
+
**correction_protocol,
|
|
1298
|
+
)
|
|
1299
|
+
|
|
1300
|
+
elif correction_protocol['correction_type']=='fit':
|
|
1301
|
+
|
|
1302
|
+
correct_background_model(self.exp_dir,
|
|
1303
|
+
well_option=well_option,
|
|
1304
|
+
position_option=pos_option,
|
|
1305
|
+
export= True,
|
|
1306
|
+
return_stacks=False,
|
|
1307
|
+
show_progress_per_well = True,
|
|
1308
|
+
show_progress_per_pos = True,
|
|
1309
|
+
movie_prefix = movie_prefix,
|
|
1310
|
+
export_prefix = export_prefix,
|
|
1311
|
+
**correction_protocol,
|
|
1312
|
+
)
|
|
1313
|
+
print('Done.')
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
def locate_image(self):
|
|
1317
|
+
|
|
1318
|
+
"""
|
|
1319
|
+
Load the first frame of the first movie found in the experiment folder as a sample.
|
|
1320
|
+
"""
|
|
1321
|
+
|
|
1322
|
+
movies = glob(self.parent_window.pos + os.sep.join(['movie', f"{self.parent_window.movie_prefix}*.tif"]))
|
|
1323
|
+
|
|
1324
|
+
if len(movies) == 0:
|
|
1325
|
+
msgBox = QMessageBox()
|
|
1326
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
1327
|
+
msgBox.setText("Please select a position containing a movie...")
|
|
1328
|
+
msgBox.setWindowTitle("Warning")
|
|
1329
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
1330
|
+
returnValue = msgBox.exec()
|
|
1331
|
+
if returnValue == QMessageBox.Ok:
|
|
1332
|
+
self.current_stack = None
|
|
1333
|
+
return None
|
|
1334
|
+
else:
|
|
1335
|
+
self.current_stack = movies[0]
|