celldetective 1.4.0__py3-none-any.whl → 1.4.1__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/exceptions.py +11 -0
- celldetective/filters.py +7 -1
- celldetective/gui/InitWindow.py +4 -1
- celldetective/gui/__init__.py +2 -9
- celldetective/gui/about.py +2 -2
- celldetective/gui/base_annotator.py +786 -0
- celldetective/gui/classifier_widget.py +18 -13
- celldetective/gui/configure_new_exp.py +51 -30
- celldetective/gui/control_panel.py +10 -7
- celldetective/gui/{signal_annotator.py → event_annotator.py} +473 -1437
- celldetective/gui/generic_signal_plot.py +2 -1
- celldetective/gui/gui_utils.py +5 -2
- celldetective/gui/help/neighborhood.json +2 -2
- celldetective/gui/layouts.py +21 -11
- celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +3 -1
- celldetective/gui/process_block.py +129 -91
- celldetective/gui/processes/downloader.py +37 -34
- celldetective/gui/processes/measure_cells.py +14 -8
- celldetective/gui/processes/segment_cells.py +21 -6
- celldetective/gui/processes/track_cells.py +12 -13
- celldetective/gui/settings/__init__.py +7 -0
- celldetective/gui/settings/_settings_base.py +70 -0
- celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +35 -91
- celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +28 -81
- celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +1 -1
- celldetective/gui/settings/_settings_segmentation.py +49 -0
- celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +33 -79
- celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +73 -95
- celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +64 -87
- celldetective/gui/styles.py +2 -1
- celldetective/gui/survival_ui.py +1 -1
- celldetective/gui/tableUI.py +25 -0
- celldetective/gui/table_ops/__init__.py +0 -0
- celldetective/gui/table_ops/merge_groups.py +118 -0
- celldetective/gui/viewers.py +3 -5
- celldetective/gui/workers.py +0 -2
- celldetective/io.py +98 -55
- celldetective/links/zenodo.json +145 -144
- celldetective/measure.py +31 -26
- celldetective/preprocessing.py +34 -21
- celldetective/regionprops/_regionprops.py +16 -5
- celldetective/scripts/measure_cells.py +5 -5
- celldetective/scripts/measure_relative.py +16 -11
- celldetective/scripts/segment_cells.py +4 -4
- celldetective/scripts/segment_cells_thresholds.py +3 -3
- celldetective/scripts/track_cells.py +7 -7
- celldetective/scripts/train_segmentation_model.py +10 -1
- celldetective/tracking.py +10 -4
- celldetective/utils.py +59 -58
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/METADATA +1 -1
- celldetective-1.4.1.dist-info/RECORD +123 -0
- tests/gui/__init__.py +0 -0
- tests/gui/test_new_project.py +228 -0
- tests/{test_qt.py → gui/test_project.py} +22 -26
- tests/test_preprocessing.py +2 -2
- celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
- celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
- celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
- celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
- celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
- celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
- celldetective/models/signal_detection/NucCond/config_input.json +0 -1
- celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
- celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
- celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
- celldetective/models/signal_detection/NucCond/scores.npy +0 -0
- celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
- celldetective-1.4.0.dist-info/RECORD +0 -131
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/WHEEL +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -504,7 +504,8 @@ class GenericSignalPlotWidget(CelldetectiveWidget):
|
|
|
504
504
|
# Spines
|
|
505
505
|
self.ax.spines['top'].set_visible(False)
|
|
506
506
|
self.ax.spines['right'].set_visible(False)
|
|
507
|
-
|
|
507
|
+
self.ax.grid(which='major', color='black', linestyle='-', linewidth=0.8, alpha=0.2)
|
|
508
|
+
self.ax.grid(which='minor', color='lightgray', linestyle='--', linewidth=0.5, alpha=0.1)
|
|
508
509
|
# Lims
|
|
509
510
|
safe_df = self.df.dropna(subset=self.feature_selected)
|
|
510
511
|
values = safe_df[self.feature_selected].values
|
celldetective/gui/gui_utils.py
CHANGED
|
@@ -653,6 +653,7 @@ class FilterChoice(CelldetectiveWidget):
|
|
|
653
653
|
'log_filter': {'blob_size': 30},
|
|
654
654
|
'tophat_filter': {'size': 4, 'connectivity': 4},
|
|
655
655
|
'otsu_filter': None,
|
|
656
|
+
'multiotsu_filter': {'classes': 3},
|
|
656
657
|
'local_filter': {'block_size': 73, 'method': 'mean', 'offset': 0},
|
|
657
658
|
'niblack_filter': {'window_size': 15, 'k': 0.2},
|
|
658
659
|
# 'sauvola_filter': {'window_size': 15, 'k': 0.2}
|
|
@@ -1210,7 +1211,7 @@ def color_from_state(state, recently_modified=False):
|
|
|
1210
1211
|
"""
|
|
1211
1212
|
|
|
1212
1213
|
unique_values = np.unique(state)
|
|
1213
|
-
color_map={}
|
|
1214
|
+
color_map = {}
|
|
1214
1215
|
for value in unique_values:
|
|
1215
1216
|
|
|
1216
1217
|
if np.isnan(value):
|
|
@@ -1220,8 +1221,10 @@ def color_from_state(state, recently_modified=False):
|
|
|
1220
1221
|
color_map[value] = 'tab:blue'
|
|
1221
1222
|
elif value==1:
|
|
1222
1223
|
color_map[value] = 'tab:red'
|
|
1224
|
+
elif value==99:
|
|
1225
|
+
color_map[value] = 'k'
|
|
1223
1226
|
else:
|
|
1224
|
-
color_map[value] = plt.cm.
|
|
1227
|
+
color_map[value] = plt.cm.tab20(value/20.0)
|
|
1225
1228
|
|
|
1226
1229
|
return color_map
|
|
1227
1230
|
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"yes": {
|
|
4
4
|
"Do you have the complete shape information for both populations?": {
|
|
5
5
|
"yes": "You can calculate a mask-contact neighborhood. Define the reference and neighbor populations. Adjust the tolerance parameter to control how sensitive the analysis is to contact between reference and neighbor cells.",
|
|
6
|
-
"no": "You can use an isotropic distance threshold. Define the reference and neighbor populations. Set a radius
|
|
6
|
+
"no": "You can use an isotropic distance threshold. Define the reference and neighbor populations. Set a radius ( r > (R_{\text{ref}} + 0.5 \times R_{\text{neigh}}) ), where ( R_{\text{ref}} ) is the average radius of reference cells and ( R_{\text{neigh}} ) is the average radius of neighbor cells."
|
|
7
7
|
}
|
|
8
8
|
},
|
|
9
9
|
"no": {
|
|
10
10
|
"Do you have the complete shape information for the population of interest?": {
|
|
11
11
|
"yes": "You can calculate a mask-contact neighborhood. Use the same population as both the reference and neighbor. Adjust the tolerance parameter to control sensitivity to cell-cell contact.",
|
|
12
|
-
"no": "You can use an isotropic distance threshold. Use the same population as both the reference and neighbor. Set a radius
|
|
12
|
+
"no": "You can use an isotropic distance threshold. Use the same population as both the reference and neighbor. Set a radius ( r > 1.5 \times R ), where ( R ) is the average cell radius."
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
}
|
celldetective/gui/layouts.py
CHANGED
|
@@ -882,7 +882,7 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
|
|
|
882
882
|
else:
|
|
883
883
|
clip = False
|
|
884
884
|
|
|
885
|
-
corrected_stack = correct_background_model(self.attr_parent.exp_dir,
|
|
885
|
+
corrected_stack = correct_background_model(self.attr_parent.exp_dir,
|
|
886
886
|
well_option=self.attr_parent.well_list.getSelectedIndices(), #+1 ??
|
|
887
887
|
position_option=self.attr_parent.position_list.getSelectedIndices(), #+1??
|
|
888
888
|
target_channel=self.channels_cb.currentText(),
|
|
@@ -897,14 +897,18 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
|
|
|
897
897
|
show_progress_per_pos = False,
|
|
898
898
|
)
|
|
899
899
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
900
|
+
if corrected_stack:
|
|
901
|
+
self.viewer = StackVisualizer(
|
|
902
|
+
stack=corrected_stack[0],
|
|
903
|
+
window_title='Corrected channel',
|
|
904
|
+
target_channel=self.channels_cb.currentIndex(),
|
|
905
|
+
frame_slider = True,
|
|
906
|
+
contrast_slider = True
|
|
907
|
+
)
|
|
908
|
+
self.viewer.show()
|
|
909
|
+
else:
|
|
910
|
+
print("Corrected stack could not be generated... No stack available...")
|
|
911
|
+
|
|
908
912
|
|
|
909
913
|
|
|
910
914
|
class LocalCorrectionLayout(BackgroundFitCorrectionLayout):
|
|
@@ -1330,6 +1334,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1330
1334
|
for c in self.coef_widgets:
|
|
1331
1335
|
c.setEnabled(False)
|
|
1332
1336
|
|
|
1337
|
+
self.interpolate_check = QCheckBox("interpolate NaNs")
|
|
1338
|
+
|
|
1333
1339
|
def add_to_layout(self):
|
|
1334
1340
|
|
|
1335
1341
|
channel_layout = QHBoxLayout()
|
|
@@ -1394,10 +1400,12 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1394
1400
|
self.operation_layout = OperationLayout()
|
|
1395
1401
|
self.addLayout(self.operation_layout, 9, 0, 1, 3)
|
|
1396
1402
|
|
|
1403
|
+
self.addWidget(self.interpolate_check, 10, 0, 1, 1)
|
|
1404
|
+
|
|
1397
1405
|
correction_layout = QHBoxLayout()
|
|
1398
1406
|
correction_layout.addWidget(self.add_correction_btn, 95)
|
|
1399
1407
|
correction_layout.addWidget(self.corrected_stack_viewer_btn, 5)
|
|
1400
|
-
self.addLayout(correction_layout,
|
|
1408
|
+
self.addLayout(correction_layout, 11, 0, 1, 3)
|
|
1401
1409
|
|
|
1402
1410
|
# verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
|
1403
1411
|
# self.addItem(verticalSpacer, 5, 0, 1, 3)
|
|
@@ -1456,7 +1464,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1456
1464
|
"opt_coef_nbr": opt_coef_nbr,
|
|
1457
1465
|
"operation": operation,
|
|
1458
1466
|
"clip": clip,
|
|
1459
|
-
"offset": offset
|
|
1467
|
+
"offset": offset,
|
|
1468
|
+
"fix_nan": self.interpolate_check.isChecked(),
|
|
1460
1469
|
}
|
|
1461
1470
|
|
|
1462
1471
|
def set_target_channel(self):
|
|
@@ -1532,6 +1541,7 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1532
1541
|
clip = clip,
|
|
1533
1542
|
export= False,
|
|
1534
1543
|
return_stacks=True,
|
|
1544
|
+
fix_nan=self.interpolate_check.isChecked(),
|
|
1535
1545
|
show_progress_per_well = True,
|
|
1536
1546
|
show_progress_per_pos = False,
|
|
1537
1547
|
)
|
|
@@ -26,7 +26,7 @@ from sklearn.preprocessing import MinMaxScaler
|
|
|
26
26
|
from functools import partial
|
|
27
27
|
from pandas.api.types import is_numeric_dtype
|
|
28
28
|
|
|
29
|
-
class
|
|
29
|
+
class PairEventAnnotator(CelldetectiveMainWindow):
|
|
30
30
|
|
|
31
31
|
"""
|
|
32
32
|
UI to set tracking parameters for bTrack.
|
|
@@ -167,8 +167,10 @@ class SignalAnnotator2(CelldetectiveMainWindow):
|
|
|
167
167
|
self.left_panel.addLayout(neigh_hbox)
|
|
168
168
|
|
|
169
169
|
self.reference_cell_info = QLabel('')
|
|
170
|
+
self.reference_cell_info.setStyleSheet("color: #1f77b4;")
|
|
170
171
|
self.pair_info = QLabel('')
|
|
171
172
|
self.neighbor_cell_info= QLabel('')
|
|
173
|
+
self.neighbor_cell_info.setStyleSheet("color: #d62728;")
|
|
172
174
|
|
|
173
175
|
class_hbox = QHBoxLayout()
|
|
174
176
|
class_hbox.addWidget(QLabel('interaction event: '), 25)
|
|
@@ -7,18 +7,20 @@ import gc
|
|
|
7
7
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator
|
|
8
8
|
|
|
9
9
|
from celldetective.gui.processes.compute_neighborhood import NeighborhoodProcess
|
|
10
|
-
from celldetective.gui.
|
|
11
|
-
from celldetective.gui.signal_annotator2 import SignalAnnotator2
|
|
10
|
+
from celldetective.gui.event_annotator import MeasureAnnotator
|
|
12
11
|
from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
|
|
13
12
|
control_tracks, load_experiment_tables, get_pair_signal_models_list
|
|
14
13
|
from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, locate_signal_model
|
|
15
|
-
from celldetective.gui import SegmentationModelLoader, ClassifierWidget,
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
from celldetective.gui import SegmentationModelLoader, ClassifierWidget, \
|
|
15
|
+
EventAnnotator, TableUI, CelldetectiveWidget, PairEventAnnotator
|
|
16
|
+
|
|
17
|
+
from celldetective.gui.settings import SettingsSegmentation, SettingsMeasurements, SettingsTracking, \
|
|
18
|
+
SettingsSignalAnnotator, SettingsNeighborhood, SettingsSegmentationModelTraining, SettingsEventDetectionModelTraining
|
|
19
|
+
|
|
18
20
|
from celldetective.gui.gui_utils import QHSeperationLine
|
|
19
21
|
from celldetective.relative_measurements import rel_measure_at_position
|
|
20
22
|
from celldetective.signals import analyze_signals_at_position, analyze_pair_signals_at_position
|
|
21
|
-
from celldetective.utils import extract_experiment_channels
|
|
23
|
+
from celldetective.utils import extract_experiment_channels, remove_file_if_exists
|
|
22
24
|
import numpy as np
|
|
23
25
|
from glob import glob
|
|
24
26
|
from natsort import natsorted
|
|
@@ -81,13 +83,13 @@ class ProcessPanel(QFrame, Styles):
|
|
|
81
83
|
title_hbox = QHBoxLayout()
|
|
82
84
|
self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
|
|
83
85
|
|
|
84
|
-
self.help_pop_btn = QPushButton()
|
|
85
|
-
self.help_pop_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
|
|
86
|
-
self.help_pop_btn.setIconSize(QSize(20, 20))
|
|
87
|
-
self.help_pop_btn.clicked.connect(self.help_population)
|
|
88
|
-
self.help_pop_btn.setStyleSheet(self.button_select_all)
|
|
89
|
-
self.help_pop_btn.setToolTip("Help.")
|
|
90
|
-
self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
|
|
86
|
+
# self.help_pop_btn = QPushButton()
|
|
87
|
+
# self.help_pop_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
|
|
88
|
+
# self.help_pop_btn.setIconSize(QSize(20, 20))
|
|
89
|
+
# self.help_pop_btn.clicked.connect(self.help_population)
|
|
90
|
+
# self.help_pop_btn.setStyleSheet(self.button_select_all)
|
|
91
|
+
# self.help_pop_btn.setToolTip("Help.")
|
|
92
|
+
# self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
|
|
91
93
|
|
|
92
94
|
# self.select_all_btn = QPushButton()
|
|
93
95
|
# self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
|
|
@@ -106,7 +108,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
106
108
|
|
|
107
109
|
title_hbox.addWidget(QLabel(), 5) #self.select_all_btn
|
|
108
110
|
title_hbox.addWidget(QLabel(), 85, alignment=Qt.AlignCenter)
|
|
109
|
-
title_hbox.addWidget(self.help_pop_btn, 5)
|
|
111
|
+
# title_hbox.addWidget(self.help_pop_btn, 5)
|
|
110
112
|
title_hbox.addWidget(self.collapse_btn, 5)
|
|
111
113
|
|
|
112
114
|
self.grid.addLayout(title_hbox, 0, 0, 1, 4)
|
|
@@ -136,29 +138,29 @@ class ProcessPanel(QFrame, Styles):
|
|
|
136
138
|
self.parent_window.scroll.setMinimumHeight(min(int(930), int(0.9*self.parent_window.screen_height)))
|
|
137
139
|
|
|
138
140
|
|
|
139
|
-
def help_population(self):
|
|
141
|
+
# def help_population(self):
|
|
140
142
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
# """
|
|
144
|
+
# Helper to choose a proper cell population structure.
|
|
145
|
+
# """
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
# dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','cell-populations.json'])
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
+
# with open(dict_path) as f:
|
|
150
|
+
# d = json.load(f)
|
|
149
151
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
# suggestion = help_generic(d)
|
|
153
|
+
# if isinstance(suggestion, str):
|
|
154
|
+
# print(f"{suggestion=}")
|
|
155
|
+
# msgBox = QMessageBox()
|
|
156
|
+
# msgBox.setIcon(QMessageBox.Information)
|
|
157
|
+
# msgBox.setTextFormat(Qt.RichText)
|
|
158
|
+
# msgBox.setText(suggestion)
|
|
159
|
+
# msgBox.setWindowTitle("Info")
|
|
160
|
+
# msgBox.setStandardButtons(QMessageBox.Ok)
|
|
161
|
+
# returnValue = msgBox.exec()
|
|
162
|
+
# if returnValue == QMessageBox.Ok:
|
|
163
|
+
# return None
|
|
162
164
|
|
|
163
165
|
def populate_contents(self):
|
|
164
166
|
self.ContentsFrame = QFrame()
|
|
@@ -284,9 +286,16 @@ class ProcessPanel(QFrame, Styles):
|
|
|
284
286
|
self.grid_contents.addLayout(signal_layout,6,0,1,4)
|
|
285
287
|
|
|
286
288
|
def refresh_signal_models(self):
|
|
287
|
-
signal_models = get_signal_models_list()
|
|
289
|
+
self.signal_models = get_signal_models_list()
|
|
288
290
|
self.signal_models_list.clear()
|
|
289
|
-
|
|
291
|
+
|
|
292
|
+
thresh = 35
|
|
293
|
+
models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.signal_models]
|
|
294
|
+
|
|
295
|
+
self.signal_models_list.addItems(models_truncated)
|
|
296
|
+
for i in range(len(self.signal_models)):
|
|
297
|
+
self.signal_models_list.setItemData(i, self.signal_models[i], Qt.ToolTipRole)
|
|
298
|
+
|
|
290
299
|
|
|
291
300
|
def generate_tracking_options(self):
|
|
292
301
|
|
|
@@ -347,14 +356,10 @@ class ProcessPanel(QFrame, Styles):
|
|
|
347
356
|
if returnValue == QMessageBox.No:
|
|
348
357
|
return None
|
|
349
358
|
elif returnValue == QMessageBox.Yes:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy'])):
|
|
355
|
-
os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy']))
|
|
356
|
-
if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv'])):
|
|
357
|
-
os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv']))
|
|
359
|
+
remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv']))
|
|
360
|
+
remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl']))
|
|
361
|
+
remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy']))
|
|
362
|
+
remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv']))
|
|
358
363
|
self.parent_window.update_position_options()
|
|
359
364
|
else:
|
|
360
365
|
return None
|
|
@@ -373,13 +378,21 @@ class ProcessPanel(QFrame, Styles):
|
|
|
373
378
|
#self.to_disable.append(self.segment_action)
|
|
374
379
|
grid_segment.addWidget(self.segment_action, 90)
|
|
375
380
|
|
|
376
|
-
self.flip_segment_btn = QPushButton()
|
|
377
|
-
self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color="black"))
|
|
378
|
-
self.flip_segment_btn.setIconSize(QSize(20, 20))
|
|
379
|
-
self.flip_segment_btn.clicked.connect(self.flip_segmentation)
|
|
380
|
-
self.flip_segment_btn.setStyleSheet(self.button_select_all)
|
|
381
|
-
self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
|
|
382
|
-
grid_segment.addWidget(self.flip_segment_btn, 5)
|
|
381
|
+
# self.flip_segment_btn = QPushButton()
|
|
382
|
+
# self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color="black"))
|
|
383
|
+
# self.flip_segment_btn.setIconSize(QSize(20, 20))
|
|
384
|
+
# self.flip_segment_btn.clicked.connect(self.flip_segmentation)
|
|
385
|
+
# self.flip_segment_btn.setStyleSheet(self.button_select_all)
|
|
386
|
+
# self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
|
|
387
|
+
# grid_segment.addWidget(self.flip_segment_btn, 5)
|
|
388
|
+
|
|
389
|
+
self.segmentation_config_btn = QPushButton()
|
|
390
|
+
self.segmentation_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
|
|
391
|
+
self.segmentation_config_btn.setIconSize(QSize(20, 20))
|
|
392
|
+
self.segmentation_config_btn.setToolTip("Configure segmentation.")
|
|
393
|
+
self.segmentation_config_btn.setStyleSheet(self.button_select_all)
|
|
394
|
+
self.segmentation_config_btn.clicked.connect(self.open_segmentation_configuration_ui)
|
|
395
|
+
grid_segment.addWidget(self.segmentation_config_btn, 5)
|
|
383
396
|
|
|
384
397
|
|
|
385
398
|
self.check_seg_btn = QPushButton()
|
|
@@ -572,6 +585,14 @@ class ProcessPanel(QFrame, Styles):
|
|
|
572
585
|
print(f"Loading images and labels into napari...")
|
|
573
586
|
try:
|
|
574
587
|
control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
|
|
588
|
+
except FileNotFoundError as e:
|
|
589
|
+
msgBox = QMessageBox()
|
|
590
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
591
|
+
msgBox.setText(str(e))
|
|
592
|
+
msgBox.setWindowTitle("Warning")
|
|
593
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
594
|
+
_ = msgBox.exec()
|
|
595
|
+
return
|
|
575
596
|
except Exception as e:
|
|
576
597
|
print(f'Task unsuccessful... Exception {e}...')
|
|
577
598
|
msgBox = QMessageBox()
|
|
@@ -604,15 +625,15 @@ class ProcessPanel(QFrame, Styles):
|
|
|
604
625
|
|
|
605
626
|
test = self.parent_window.locate_selected_position()
|
|
606
627
|
if test:
|
|
607
|
-
self.
|
|
608
|
-
self.
|
|
628
|
+
self.event_annotator = EventAnnotator(self)
|
|
629
|
+
self.event_annotator.show()
|
|
609
630
|
|
|
610
631
|
def check_measurements(self):
|
|
611
632
|
|
|
612
633
|
test = self.parent_window.locate_selected_position()
|
|
613
634
|
if test:
|
|
614
|
-
self.
|
|
615
|
-
self.
|
|
635
|
+
self.measure_annotator = MeasureAnnotator(self)
|
|
636
|
+
self.measure_annotator.show()
|
|
616
637
|
|
|
617
638
|
def enable_segmentation_model_list(self):
|
|
618
639
|
if self.segment_action.isChecked():
|
|
@@ -630,22 +651,22 @@ class ProcessPanel(QFrame, Styles):
|
|
|
630
651
|
|
|
631
652
|
self.seg_model_list.clear()
|
|
632
653
|
self.seg_models_specific = get_segmentation_models_list(mode=self.mode, return_path=False)
|
|
633
|
-
self.seg_models = self.seg_models_specific.copy()
|
|
634
|
-
|
|
635
|
-
self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
|
|
636
|
-
#self.seg_model_list.addItems(models_truncated)
|
|
654
|
+
self.seg_models = self.seg_models_specific.copy()
|
|
655
|
+
self.n_specific_seg_models = len(self.seg_models)
|
|
637
656
|
|
|
638
657
|
self.seg_models_generic = get_segmentation_models_list(mode="generic", return_path=False)
|
|
639
658
|
self.seg_models.append('Threshold')
|
|
640
659
|
self.seg_models.extend(self.seg_models_generic)
|
|
641
660
|
|
|
642
|
-
|
|
643
|
-
self.
|
|
661
|
+
thresh = 35
|
|
662
|
+
self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
|
|
663
|
+
|
|
664
|
+
self.seg_model_list.addItems(self.models_truncated)
|
|
644
665
|
|
|
645
666
|
for i in range(len(self.seg_models)):
|
|
646
667
|
self.seg_model_list.setItemData(i, self.seg_models[i], Qt.ToolTipRole)
|
|
647
668
|
|
|
648
|
-
self.seg_model_list.insertSeparator(
|
|
669
|
+
self.seg_model_list.insertSeparator(self.n_specific_seg_models)
|
|
649
670
|
|
|
650
671
|
# def tick_all_actions(self):
|
|
651
672
|
# self.switch_all_ticks_option()
|
|
@@ -671,23 +692,28 @@ class ProcessPanel(QFrame, Styles):
|
|
|
671
692
|
|
|
672
693
|
def open_tracking_configuration_ui(self):
|
|
673
694
|
print('Set the tracking parameters...')
|
|
674
|
-
self.
|
|
675
|
-
self.
|
|
695
|
+
self.settings_tracking = SettingsTracking(self)
|
|
696
|
+
self.settings_tracking.show()
|
|
676
697
|
|
|
677
698
|
def open_signal_model_config_ui(self):
|
|
678
699
|
print('Set the training parameters for new signal models...')
|
|
679
|
-
self.
|
|
680
|
-
self.
|
|
700
|
+
self.settings_event_detection_training = SettingsEventDetectionModelTraining(self)
|
|
701
|
+
self.settings_event_detection_training.show()
|
|
681
702
|
|
|
682
703
|
def open_segmentation_model_config_ui(self):
|
|
683
704
|
print('Set the training parameters for a new segmentation model...')
|
|
684
|
-
self.
|
|
685
|
-
self.
|
|
705
|
+
self.settings_segmentation_training = SettingsSegmentationModelTraining(self)
|
|
706
|
+
self.settings_segmentation_training.show()
|
|
686
707
|
|
|
687
708
|
def open_measurement_configuration_ui(self):
|
|
688
709
|
print('Set the measurements to be performed...')
|
|
689
|
-
self.
|
|
690
|
-
self.
|
|
710
|
+
self.settings_measurements = SettingsMeasurements(self)
|
|
711
|
+
self.settings_measurements.show()
|
|
712
|
+
|
|
713
|
+
def open_segmentation_configuration_ui(self):
|
|
714
|
+
print('Set the segmentation settings to be performed...')
|
|
715
|
+
self.settings_segmentation = SettingsSegmentation(self)
|
|
716
|
+
self.settings_segmentation.show()
|
|
691
717
|
|
|
692
718
|
def open_classifier_ui(self):
|
|
693
719
|
|
|
@@ -709,8 +735,8 @@ class ProcessPanel(QFrame, Styles):
|
|
|
709
735
|
self.ClassifierWidget.show()
|
|
710
736
|
|
|
711
737
|
def open_signal_annotator_configuration_ui(self):
|
|
712
|
-
self.
|
|
713
|
-
self.
|
|
738
|
+
self.settings_signal_annotator = SettingsSignalAnnotator(self)
|
|
739
|
+
self.settings_signal_annotator.show()
|
|
714
740
|
|
|
715
741
|
def reset_generalist_setup(self, index):
|
|
716
742
|
self.cellpose_calibrated = False
|
|
@@ -766,10 +792,11 @@ class ProcessPanel(QFrame, Styles):
|
|
|
766
792
|
#tabs += [pos+os.sep.join(['output', 'tables', f'trajectories_pairs.csv']) for pos in self.df_pos_info['pos_path'].unique()]
|
|
767
793
|
tabs += [pos+os.sep.join(['output', 'tables', f'napari_{self.mode}_trajectories.npy']) for pos in self.df_pos_info['pos_path'].unique()]
|
|
768
794
|
for t in tabs:
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
795
|
+
remove_file_if_exists(t.replace('.csv','.pkl'))
|
|
796
|
+
try:
|
|
797
|
+
os.remove(t)
|
|
798
|
+
except:
|
|
799
|
+
pass
|
|
773
800
|
loop_iter=0
|
|
774
801
|
|
|
775
802
|
if self.parent_window.position_list.isMultipleSelection():
|
|
@@ -782,7 +809,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
782
809
|
if returnValue == QMessageBox.No:
|
|
783
810
|
return None
|
|
784
811
|
|
|
785
|
-
if self.seg_model_list.currentIndex() >
|
|
812
|
+
if self.seg_model_list.currentIndex() > self.n_specific_seg_models:
|
|
786
813
|
self.model_name = self.seg_models[self.seg_model_list.currentIndex()-1]
|
|
787
814
|
else:
|
|
788
815
|
self.model_name = self.seg_models[self.seg_model_list.currentIndex()]
|
|
@@ -806,7 +833,8 @@ class ProcessPanel(QFrame, Styles):
|
|
|
806
833
|
return None
|
|
807
834
|
|
|
808
835
|
if self.signal_analysis_action.isChecked() and not self.signalChannelsSet:
|
|
809
|
-
self.
|
|
836
|
+
self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
|
|
837
|
+
self.signalChannelWidget = SignalModelParamsWidget(self, model_name = self.signal_model_name)
|
|
810
838
|
self.signalChannelWidget.show()
|
|
811
839
|
return None
|
|
812
840
|
|
|
@@ -856,7 +884,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
856
884
|
return None
|
|
857
885
|
else:
|
|
858
886
|
print(f"Segmentation from threshold config: {self.threshold_config}")
|
|
859
|
-
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu
|
|
887
|
+
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu}
|
|
860
888
|
self.job = ProgressWindow(SegmentCellThresholdProcess, parent_window=self, title="Segment", process_args = process_args)
|
|
861
889
|
result = self.job.exec_()
|
|
862
890
|
if result == QDialog.Accepted:
|
|
@@ -871,7 +899,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
871
899
|
# process = {"output_dir": self.output_dir, "file": self.model_name}
|
|
872
900
|
# self.download_model_job = ProgressWindow(DownloadProcess, parent_window=self, title="Download", process_args = args)
|
|
873
901
|
|
|
874
|
-
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu
|
|
902
|
+
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu}
|
|
875
903
|
self.job = ProgressWindow(SegmentCellDLProcess, parent_window=self, title="Segment", process_args = process_args)
|
|
876
904
|
result = self.job.exec_()
|
|
877
905
|
if result == QDialog.Accepted:
|
|
@@ -926,7 +954,8 @@ class ProcessPanel(QFrame, Styles):
|
|
|
926
954
|
returnValue = msgBox.exec()
|
|
927
955
|
if returnValue == QMessageBox.No:
|
|
928
956
|
return None
|
|
929
|
-
|
|
957
|
+
self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
|
|
958
|
+
analyze_signals_at_position(self.pos, self.signal_model_name, self.mode)
|
|
930
959
|
|
|
931
960
|
|
|
932
961
|
# self.stack = None
|
|
@@ -940,7 +969,16 @@ class ProcessPanel(QFrame, Styles):
|
|
|
940
969
|
|
|
941
970
|
def open_napari_tracking(self):
|
|
942
971
|
print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
|
|
943
|
-
|
|
972
|
+
try:
|
|
973
|
+
control_tracks(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
|
|
974
|
+
except FileNotFoundError as e:
|
|
975
|
+
msgBox = QMessageBox()
|
|
976
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
977
|
+
msgBox.setText(str(e))
|
|
978
|
+
msgBox.setWindowTitle("Warning")
|
|
979
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
980
|
+
_ = msgBox.exec()
|
|
981
|
+
return
|
|
944
982
|
|
|
945
983
|
def view_table_ui(self):
|
|
946
984
|
|
|
@@ -1045,8 +1083,8 @@ class ProcessPanel(QFrame, Styles):
|
|
|
1045
1083
|
self.process_population()
|
|
1046
1084
|
|
|
1047
1085
|
def set_selected_signals_for_event_detection(self):
|
|
1048
|
-
|
|
1049
|
-
model_complete_path = locate_signal_model(self.
|
|
1086
|
+
self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
|
|
1087
|
+
model_complete_path = locate_signal_model(self.signal_model_name)
|
|
1050
1088
|
input_config_path = model_complete_path+"config_input.json"
|
|
1051
1089
|
new_channels = [self.signalChannelWidget.channel_cbs[i].currentText() for i in range(len(self.signalChannelWidget.channel_cbs))]
|
|
1052
1090
|
with open(input_config_path) as config_file:
|
|
@@ -1441,12 +1479,12 @@ class NeighPanel(QFrame, Styles):
|
|
|
1441
1479
|
|
|
1442
1480
|
def open_signal_annotator_configuration_ui(self):
|
|
1443
1481
|
self.mode = 'pairs'
|
|
1444
|
-
self.
|
|
1445
|
-
self.
|
|
1482
|
+
self.config_signal_annotator = SettingsSignalAnnotator(self)
|
|
1483
|
+
self.config_signal_annotator.show()
|
|
1446
1484
|
|
|
1447
1485
|
def open_signal_model_config_ui(self):
|
|
1448
|
-
self.
|
|
1449
|
-
self.
|
|
1486
|
+
self.settings_pair_event_detection_training = SettingsEventDetectionModelTraining(self, signal_mode='pairs')
|
|
1487
|
+
self.settings_pair_event_detection_training.show()
|
|
1450
1488
|
|
|
1451
1489
|
def remove_protocol_from_list(self):
|
|
1452
1490
|
|
|
@@ -1457,7 +1495,7 @@ class NeighPanel(QFrame, Styles):
|
|
|
1457
1495
|
|
|
1458
1496
|
def open_config_distance_threshold_neighborhood(self):
|
|
1459
1497
|
|
|
1460
|
-
self.ConfigNeigh =
|
|
1498
|
+
self.ConfigNeigh = SettingsNeighborhood(parent_window=self,
|
|
1461
1499
|
neighborhood_type='distance_threshold',
|
|
1462
1500
|
neighborhood_parameter_name='threshold distance',
|
|
1463
1501
|
)
|
|
@@ -1465,7 +1503,7 @@ class NeighPanel(QFrame, Styles):
|
|
|
1465
1503
|
|
|
1466
1504
|
def open_config_contact_neighborhood(self):
|
|
1467
1505
|
|
|
1468
|
-
self.ConfigNeigh =
|
|
1506
|
+
self.ConfigNeigh = SettingsNeighborhood(parent_window=self,
|
|
1469
1507
|
neighborhood_type='mask_contact',
|
|
1470
1508
|
neighborhood_parameter_name='tolerance contact distance',
|
|
1471
1509
|
)
|
|
@@ -1548,8 +1586,8 @@ class NeighPanel(QFrame, Styles):
|
|
|
1548
1586
|
|
|
1549
1587
|
test = self.parent_window.locate_selected_position()
|
|
1550
1588
|
if test:
|
|
1551
|
-
self.
|
|
1552
|
-
self.
|
|
1589
|
+
self.pair_event_annotator = PairEventAnnotator(self)
|
|
1590
|
+
self.pair_event_annotator.show()
|
|
1553
1591
|
|
|
1554
1592
|
|
|
1555
1593
|
class PreprocessingPanel(QFrame, Styles):
|
|
@@ -43,41 +43,44 @@ class DownloadProcess(Process):
|
|
|
43
43
|
self.t0 = time.time()
|
|
44
44
|
|
|
45
45
|
def download_url_to_file(self, url, dst):
|
|
46
|
-
|
|
47
|
-
file_size = None
|
|
48
|
-
ssl._create_default_https_context = ssl._create_unverified_context
|
|
49
|
-
u = urlopen(url)
|
|
50
|
-
meta = u.info()
|
|
51
|
-
if hasattr(meta, 'getheaders'):
|
|
52
|
-
content_length = meta.getheaders("Content-Length")
|
|
53
|
-
else:
|
|
54
|
-
content_length = meta.get_all("Content-Length")
|
|
55
|
-
if content_length is not None and len(content_length) > 0:
|
|
56
|
-
file_size = int(content_length[0])
|
|
57
|
-
# We deliberately save it in a temp file and move it after
|
|
58
|
-
dst = os.path.expanduser(dst)
|
|
59
|
-
dst_dir = os.path.dirname(dst)
|
|
60
|
-
f = tempfile.NamedTemporaryFile(delete=False, dir=dst_dir)
|
|
61
|
-
|
|
62
46
|
try:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
47
|
+
file_size = None
|
|
48
|
+
ssl._create_default_https_context = ssl._create_unverified_context
|
|
49
|
+
u = urlopen(url)
|
|
50
|
+
meta = u.info()
|
|
51
|
+
if hasattr(meta, 'getheaders'):
|
|
52
|
+
content_length = meta.getheaders("Content-Length")
|
|
53
|
+
else:
|
|
54
|
+
content_length = meta.get_all("Content-Length")
|
|
55
|
+
if content_length is not None and len(content_length) > 0:
|
|
56
|
+
file_size = int(content_length[0])
|
|
57
|
+
# We deliberately save it in a temp file and move it after
|
|
58
|
+
dst = os.path.expanduser(dst)
|
|
59
|
+
dst_dir = os.path.dirname(dst)
|
|
60
|
+
f = tempfile.NamedTemporaryFile(delete=False, dir=dst_dir)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
with tqdm(total=file_size, disable=not self.progress,
|
|
64
|
+
unit='B', unit_scale=True, unit_divisor=1024) as pbar:
|
|
65
|
+
while True:
|
|
66
|
+
buffer = u.read(8192) #8192
|
|
67
|
+
if len(buffer) == 0:
|
|
68
|
+
break
|
|
69
|
+
f.write(buffer)
|
|
70
|
+
pbar.update(len(buffer))
|
|
71
|
+
self.sum_done+=len(buffer) / file_size * 100
|
|
72
|
+
mean_exec_per_step = (time.time() - self.t0) / (self.sum_done*file_size / 100 + 1)
|
|
73
|
+
pred_time = (file_size - (self.sum_done*file_size / 100 + 1)) * mean_exec_per_step
|
|
74
|
+
self.queue.put([self.sum_done, pred_time])
|
|
75
|
+
f.close()
|
|
76
|
+
shutil.move(f.name, dst)
|
|
77
|
+
finally:
|
|
78
|
+
f.close()
|
|
79
|
+
if os.path.exists(f.name):
|
|
80
|
+
os.remove(f.name)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print("No internet connection: ", e)
|
|
83
|
+
return None
|
|
81
84
|
|
|
82
85
|
def run(self):
|
|
83
86
|
|