celldetective 1.2.0__py3-none-any.whl → 1.2.2__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 +12 -5
- celldetective/events.py +28 -2
- celldetective/gui/about.py +0 -1
- celldetective/gui/analyze_block.py +3 -18
- celldetective/gui/btrack_options.py +126 -21
- celldetective/gui/classifier_widget.py +68 -107
- celldetective/gui/configure_new_exp.py +37 -4
- celldetective/gui/control_panel.py +14 -30
- celldetective/gui/generic_signal_plot.py +793 -0
- celldetective/gui/gui_utils.py +401 -226
- celldetective/gui/json_readers.py +0 -2
- celldetective/gui/layouts.py +269 -25
- celldetective/gui/measurement_options.py +14 -23
- celldetective/gui/neighborhood_options.py +6 -16
- celldetective/gui/plot_measurements.py +10 -23
- celldetective/gui/plot_signals_ui.py +53 -687
- celldetective/gui/process_block.py +320 -186
- celldetective/gui/retrain_segmentation_model_options.py +30 -47
- celldetective/gui/retrain_signal_model_options.py +5 -14
- celldetective/gui/seg_model_loader.py +129 -113
- celldetective/gui/signal_annotator.py +93 -103
- celldetective/gui/signal_annotator2.py +9 -13
- celldetective/gui/styles.py +32 -0
- celldetective/gui/survival_ui.py +49 -712
- celldetective/gui/tableUI.py +4 -39
- celldetective/gui/thresholds_gui.py +38 -11
- celldetective/gui/viewers.py +6 -7
- celldetective/io.py +62 -84
- celldetective/measure.py +374 -15
- celldetective/models/segmentation_effectors/ricm-bimodal/config_input.json +130 -0
- celldetective/models/segmentation_effectors/ricm-bimodal/ricm-bimodal +0 -0
- celldetective/models/segmentation_effectors/ricm-bimodal/training_instructions.json +37 -0
- celldetective/neighborhood.py +3 -7
- celldetective/preprocessing.py +2 -4
- celldetective/relative_measurements.py +0 -3
- celldetective/scripts/analyze_signals.py +0 -1
- celldetective/scripts/measure_cells.py +1 -3
- celldetective/scripts/measure_relative.py +1 -2
- celldetective/scripts/segment_cells.py +16 -12
- celldetective/scripts/segment_cells_thresholds.py +17 -10
- celldetective/scripts/track_cells.py +18 -18
- celldetective/scripts/train_segmentation_model.py +1 -2
- celldetective/scripts/train_signal_model.py +0 -3
- celldetective/segmentation.py +1 -1
- celldetective/signals.py +20 -8
- celldetective/tracking.py +2 -1
- celldetective/utils.py +126 -18
- {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/METADATA +19 -12
- celldetective-1.2.2.dist-info/RECORD +92 -0
- {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/WHEEL +1 -1
- celldetective-1.2.0.dist-info/RECORD +0 -88
- {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/LICENSE +0 -0
- {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/entry_points.txt +0 -0
- {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/top_level.txt +0 -0
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QMainWindow, QApplication,QRadioButton, QMessageBox, QScrollArea, QComboBox, QFrame,
|
|
1
|
+
from PyQt5.QtWidgets import QMainWindow, QApplication,QRadioButton, QMessageBox, QScrollArea, QComboBox, QFrame, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
|
|
4
|
-
from celldetective.gui.gui_utils import center_window
|
|
4
|
+
from celldetective.gui.gui_utils import center_window
|
|
5
5
|
from celldetective.gui.layouts import ChannelNormGenerator
|
|
6
6
|
|
|
7
|
-
from superqt import
|
|
7
|
+
from superqt import QLabeledDoubleSlider,QLabeledSlider
|
|
8
8
|
from superqt.fonticon import icon
|
|
9
9
|
from fonticon_mdi6 import MDI6
|
|
10
|
-
from celldetective.utils import
|
|
11
|
-
from celldetective.io import
|
|
12
|
-
from celldetective.measure import compute_haralick_features, contour_of_instance_segmentation
|
|
10
|
+
from celldetective.utils import get_software_location
|
|
11
|
+
from celldetective.io import get_segmentation_datasets_list, locate_segmentation_dataset
|
|
13
12
|
from celldetective.segmentation import train_segmentation_model
|
|
13
|
+
from celldetective.gui.layouts import CellposeParamsWidget
|
|
14
14
|
import numpy as np
|
|
15
15
|
import json
|
|
16
|
-
from shutil import copyfile
|
|
17
16
|
import os
|
|
18
|
-
import matplotlib.pyplot as plt
|
|
19
|
-
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
20
17
|
from glob import glob
|
|
21
|
-
from natsort import natsorted
|
|
22
|
-
from tifffile import imread
|
|
23
|
-
from pathlib import Path, PurePath
|
|
24
18
|
from datetime import datetime
|
|
25
|
-
from functools import partial
|
|
26
19
|
from celldetective.gui import Styles
|
|
27
20
|
|
|
28
21
|
class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
@@ -137,14 +130,14 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
137
130
|
|
|
138
131
|
lr_layout = QHBoxLayout()
|
|
139
132
|
lr_layout.addWidget(QLabel('learning rate: '),30)
|
|
140
|
-
self.lr_le = QLineEdit('0,
|
|
133
|
+
self.lr_le = QLineEdit('0,0003')
|
|
141
134
|
self.lr_le.setValidator(self.onlyFloat)
|
|
142
135
|
lr_layout.addWidget(self.lr_le, 70)
|
|
143
136
|
layout.addLayout(lr_layout)
|
|
144
137
|
|
|
145
138
|
bs_layout = QHBoxLayout()
|
|
146
139
|
bs_layout.addWidget(QLabel('batch size: '),30)
|
|
147
|
-
self.bs_le = QLineEdit('
|
|
140
|
+
self.bs_le = QLineEdit('8')
|
|
148
141
|
self.bs_le.setValidator(self.onlyInt)
|
|
149
142
|
bs_layout.addWidget(self.bs_le, 70)
|
|
150
143
|
layout.addLayout(bs_layout)
|
|
@@ -244,7 +237,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
244
237
|
self.augmentation_slider.setTickInterval(0.01)
|
|
245
238
|
self.augmentation_slider.setOrientation(1)
|
|
246
239
|
self.augmentation_slider.setRange(1, 5)
|
|
247
|
-
self.augmentation_slider.setValue(
|
|
240
|
+
self.augmentation_slider.setValue(2.0)
|
|
248
241
|
|
|
249
242
|
augmentation_hbox.addWidget(self.augmentation_slider, 70)
|
|
250
243
|
layout.addLayout(augmentation_hbox)
|
|
@@ -256,7 +249,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
256
249
|
self.validation_slider.setTickInterval(0.01)
|
|
257
250
|
self.validation_slider.setOrientation(1)
|
|
258
251
|
self.validation_slider.setRange(0,0.9)
|
|
259
|
-
self.validation_slider.setValue(0.
|
|
252
|
+
self.validation_slider.setValue(0.2)
|
|
260
253
|
validation_split_layout.addWidget(self.validation_slider, 70)
|
|
261
254
|
layout.addLayout(validation_split_layout)
|
|
262
255
|
|
|
@@ -334,9 +327,11 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
334
327
|
|
|
335
328
|
def rescale_slider(self):
|
|
336
329
|
if self.stardist_model.isChecked():
|
|
337
|
-
self.epochs_slider.setRange(1,
|
|
330
|
+
self.epochs_slider.setRange(1,500)
|
|
331
|
+
self.lr_le.setText('0,0003')
|
|
338
332
|
else:
|
|
339
|
-
self.epochs_slider.setRange(1,
|
|
333
|
+
self.epochs_slider.setRange(1,10000)
|
|
334
|
+
self.lr_le.setText('0,01')
|
|
340
335
|
|
|
341
336
|
|
|
342
337
|
def showDialog_pretrained(self):
|
|
@@ -367,12 +362,12 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
367
362
|
subfiles = [s.replace('\\','/') for s in subfiles]
|
|
368
363
|
subfiles = [rf"{s}" for s in subfiles]
|
|
369
364
|
|
|
370
|
-
if
|
|
365
|
+
if "/".join([self.pretrained_model,"config_input.json"]) in subfiles:
|
|
371
366
|
self.load_pretrained_config()
|
|
372
367
|
self.pretrained_lbl.setText(self.pretrained_model.split("/")[-1])
|
|
373
368
|
self.cancel_pretrained.setVisible(True)
|
|
374
369
|
#self.recompile_option.setEnabled(True)
|
|
375
|
-
self.modelname_le.setText(f"{self.pretrained_model.split(
|
|
370
|
+
self.modelname_le.setText(f"{self.pretrained_model.split('/')[-1]}_{datetime.today().strftime('%Y-%m-%d')}")
|
|
376
371
|
else:
|
|
377
372
|
self.pretrained_model = None
|
|
378
373
|
self.pretrained_lbl.setText('No folder chosen')
|
|
@@ -380,38 +375,26 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
380
375
|
self.cancel_pretrained.setVisible(False)
|
|
381
376
|
return None
|
|
382
377
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
self.seg_folder = self.pretrained_model.split(os.sep)[-2]
|
|
386
|
-
self.model_name = self.pretrained_model.split(os.sep)[-1]
|
|
378
|
+
self.seg_folder = self.pretrained_model.split('/')[-2]
|
|
379
|
+
self.model_name = self.pretrained_model.split('/')[-1]
|
|
387
380
|
if self.model_name.startswith('CP') and self.seg_folder=='segmentation_generic':
|
|
388
381
|
|
|
389
|
-
self.diamWidget =
|
|
390
|
-
self.diamWidget.setWindowTitle('Estimate diameter')
|
|
391
|
-
|
|
392
|
-
layout = QVBoxLayout()
|
|
393
|
-
self.diamWidget.setLayout(layout)
|
|
394
|
-
self.diameter_le = QLineEdit('40')
|
|
395
|
-
|
|
396
|
-
hbox = QHBoxLayout()
|
|
397
|
-
hbox.addWidget(QLabel('diameter [px]: '), 33)
|
|
398
|
-
hbox.addWidget(self.diameter_le, 66)
|
|
399
|
-
layout.addLayout(hbox)
|
|
400
|
-
|
|
401
|
-
self.set_cellpose_scale_btn = QPushButton('set')
|
|
402
|
-
self.set_cellpose_scale_btn.clicked.connect(self.set_cellpose_scale)
|
|
403
|
-
layout.addWidget(self.set_cellpose_scale_btn)
|
|
404
|
-
|
|
382
|
+
self.diamWidget = CellposeParamsWidget(self, model_name=self.model_name)
|
|
405
383
|
self.diamWidget.show()
|
|
406
|
-
center_window(self.diamWidget)
|
|
407
384
|
|
|
408
385
|
def set_cellpose_scale(self):
|
|
409
386
|
|
|
410
|
-
scale = self.parent_window.parent_window.PxToUm * float(self.diameter_le.text()) / 30.0
|
|
387
|
+
scale = self.parent_window.parent_window.PxToUm * float(self.diamWidget.diameter_le.text().replace(',','.')) / 30.0
|
|
411
388
|
if self.model_name=="CP_nuclei":
|
|
412
|
-
scale = self.parent_window.parent_window.PxToUm * float(self.diameter_le.text()) / 17.0
|
|
389
|
+
scale = self.parent_window.parent_window.PxToUm * float(self.diamWidget.diameter_le.text().replace(',','.')) / 17.0
|
|
413
390
|
self.spatial_calib_le.setText(str(scale).replace('.',','))
|
|
414
|
-
|
|
391
|
+
|
|
392
|
+
for k in range(len(self.diamWidget.cellpose_channel_cb)):
|
|
393
|
+
ch = self.diamWidget.cellpose_channel_cb[k].currentText()
|
|
394
|
+
idx = self.ch_norm.channel_cbs[k].findText(ch)
|
|
395
|
+
self.ch_norm.channel_cbs[k].setCurrentIndex(idx)
|
|
396
|
+
|
|
397
|
+
self.diamWidget.close()
|
|
415
398
|
|
|
416
399
|
|
|
417
400
|
def showDialog_dataset(self):
|
|
@@ -477,8 +460,8 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
477
460
|
f = open(os.sep.join([self.pretrained_model,"config_input.json"]))
|
|
478
461
|
data = json.load(f)
|
|
479
462
|
channels = data["channels"]
|
|
480
|
-
self.seg_folder = self.pretrained_model.split(
|
|
481
|
-
self.model_name = self.pretrained_model.split(
|
|
463
|
+
self.seg_folder = self.pretrained_model.split('/')[-2]
|
|
464
|
+
self.model_name = self.pretrained_model.split('/')[-1]
|
|
482
465
|
if self.model_name.startswith('CP') and self.seg_folder=='segmentation_generic':
|
|
483
466
|
channels = ['brightfield_channel', 'live_nuclei_channel']
|
|
484
467
|
if self.model_name=="CP_nuclei":
|
|
@@ -1,28 +1,19 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout,
|
|
1
|
+
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
|
|
4
|
-
from celldetective.gui.gui_utils import center_window
|
|
4
|
+
from celldetective.gui.gui_utils import center_window
|
|
5
5
|
from celldetective.gui.layouts import ChannelNormGenerator
|
|
6
|
-
from superqt import
|
|
6
|
+
from superqt import QLabeledDoubleSlider, QLabeledSlider, QSearchableComboBox
|
|
7
7
|
from superqt.fonticon import icon
|
|
8
8
|
from fonticon_mdi6 import MDI6
|
|
9
|
-
from celldetective.utils import
|
|
10
|
-
from celldetective.io import
|
|
11
|
-
from celldetective.measure import compute_haralick_features, contour_of_instance_segmentation
|
|
9
|
+
from celldetective.utils import get_software_location
|
|
10
|
+
from celldetective.io import locate_signal_dataset, get_signal_datasets_list, load_experiment_tables
|
|
12
11
|
from celldetective.signals import train_signal_model
|
|
13
12
|
import numpy as np
|
|
14
13
|
import json
|
|
15
|
-
from shutil import copyfile
|
|
16
14
|
import os
|
|
17
|
-
import matplotlib.pyplot as plt
|
|
18
|
-
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
19
15
|
from glob import glob
|
|
20
|
-
from natsort import natsorted
|
|
21
|
-
from tifffile import imread
|
|
22
|
-
from pathlib import Path, PurePath
|
|
23
16
|
from datetime import datetime
|
|
24
|
-
import pandas as pd
|
|
25
|
-
from functools import partial
|
|
26
17
|
from celldetective.gui import Styles
|
|
27
18
|
from pandas.api.types import is_numeric_dtype
|
|
28
19
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QWidget, QGridLayout,
|
|
1
|
+
from PyQt5.QtWidgets import QWidget, QGridLayout, QVBoxLayout, QLabel, QLineEdit, QHBoxLayout, QRadioButton, QFileDialog, QPushButton, QMessageBox
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from celldetective.gui.gui_utils import center_window
|
|
4
|
+
from celldetective.gui.layouts import ChannelNormGenerator
|
|
4
5
|
from celldetective.gui import ThresholdConfigWizard
|
|
5
6
|
from PyQt5.QtGui import QDoubleValidator
|
|
6
7
|
from superqt.fonticon import icon
|
|
@@ -11,6 +12,9 @@ import os
|
|
|
11
12
|
import json
|
|
12
13
|
import shutil
|
|
13
14
|
from celldetective.gui import Styles
|
|
15
|
+
import gc
|
|
16
|
+
from cellpose.models import CellposeModel
|
|
17
|
+
|
|
14
18
|
|
|
15
19
|
class SegmentationModelLoader(QWidget, Styles):
|
|
16
20
|
|
|
@@ -29,6 +33,7 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
29
33
|
self.target_folder = "segmentation_effectors"
|
|
30
34
|
self.setWindowTitle('Upload model')
|
|
31
35
|
self.generate_content()
|
|
36
|
+
self.setWindowIcon(self.celldetective_icon)
|
|
32
37
|
center_window(self)
|
|
33
38
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
34
39
|
|
|
@@ -44,28 +49,21 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
44
49
|
# Create radio buttons
|
|
45
50
|
self.stardist_button = QRadioButton('StarDist')
|
|
46
51
|
#self.stardist_button.setIcon(QIcon(abs_path+f"/icons/star.png"))
|
|
47
|
-
self.stardist_button.setChecked(True)
|
|
48
|
-
option_layout.addWidget(self.stardist_button)
|
|
49
52
|
|
|
50
53
|
self.cellpose_button = QRadioButton('Cellpose')
|
|
51
54
|
#self.cellpose_button.setIcon(QIcon(abs_path+f"/icons/cellpose.png"))
|
|
52
|
-
option_layout.addWidget(self.cellpose_button)
|
|
53
55
|
|
|
54
56
|
self.threshold_button = QRadioButton('Threshold')
|
|
57
|
+
|
|
55
58
|
option_layout.addWidget(self.threshold_button)
|
|
59
|
+
option_layout.addWidget(self.stardist_button)
|
|
60
|
+
option_layout.addWidget(self.cellpose_button)
|
|
56
61
|
|
|
57
62
|
self.layout.addLayout(option_layout, 1,0,1,2, alignment=Qt.AlignCenter)
|
|
58
63
|
self.generate_base_block()
|
|
59
64
|
self.layout.addLayout(self.base_block, 2,0,1,2)
|
|
60
|
-
self.
|
|
61
|
-
self.
|
|
62
|
-
self.combos = [self.combo_ch1, self.combo_ch2, self.combo_ch3, self.combo_ch4]
|
|
63
|
-
|
|
64
|
-
self.normalize_checkbox = QCheckBox()
|
|
65
|
-
self.normalize_checkbox.setChecked(True)
|
|
66
|
-
self.normalize_lbl = QLabel('normalize: ')
|
|
67
|
-
self.layout.addWidget(self.normalize_lbl, 8,0,1,1)
|
|
68
|
-
self.layout.addWidget(self.normalize_checkbox,8, 1,1,2, Qt.AlignLeft)
|
|
65
|
+
# self.combos = self.channel_layout.channel_cbs
|
|
66
|
+
# self.cb_labels = self.channel_layout.channel_labels
|
|
69
67
|
|
|
70
68
|
self.generate_cellpose_options()
|
|
71
69
|
self.layout.addLayout(self.cellpose_block, 3,0,1,2)
|
|
@@ -89,28 +87,30 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
89
87
|
self.upload_button.setEnabled(False)
|
|
90
88
|
self.layout.addWidget(self.upload_button, 10, 0, 1, 1)
|
|
91
89
|
|
|
92
|
-
self.base_block_options = [self.calibration_label, self.spatial_calib_le
|
|
93
|
-
self.
|
|
94
|
-
]
|
|
90
|
+
self.base_block_options = [self.calibration_label, self.spatial_calib_le,*self.channel_layout.channel_cbs,*self.channel_layout.channel_labels,*self.channel_layout.normalization_mode_btns, *self.channel_layout.normalization_clip_btns, *self.channel_layout.normalization_min_value_lbl,
|
|
91
|
+
*self.channel_layout.normalization_min_value_le, *self.channel_layout.normalization_max_value_lbl,*self.channel_layout.normalization_max_value_le,
|
|
92
|
+
self.channel_layout.add_col_btn]
|
|
95
93
|
|
|
96
94
|
self.stardist_button.toggled.connect(self.show_seg_options)
|
|
97
95
|
self.cellpose_button.toggled.connect(self.show_seg_options)
|
|
98
96
|
self.threshold_button.toggled.connect(self.show_seg_options)
|
|
99
97
|
|
|
100
|
-
for cb in self.
|
|
98
|
+
for cb in self.channel_layout.channel_cbs:
|
|
101
99
|
cb.activated.connect(self.unlock_upload)
|
|
102
100
|
|
|
103
101
|
self.setLayout(self.layout)
|
|
102
|
+
|
|
103
|
+
self.threshold_button.setChecked(True)
|
|
104
104
|
self.show()
|
|
105
105
|
|
|
106
106
|
def unlock_upload(self):
|
|
107
107
|
if self.stardist_button.isChecked():
|
|
108
|
-
if np.any([c.currentText()!='--' for c in self.
|
|
108
|
+
if np.any([c.currentText()!='--' for c in self.channel_layout.channel_cbs]):
|
|
109
109
|
self.upload_button.setEnabled(True)
|
|
110
110
|
else:
|
|
111
111
|
self.upload_button.setEnabled(False)
|
|
112
112
|
elif self.cellpose_button.isChecked():
|
|
113
|
-
if np.any([c.currentText()!='--' for c in self.
|
|
113
|
+
if np.any([c.currentText()!='--' for c in self.channel_layout.channel_cbs]):
|
|
114
114
|
self.upload_button.setEnabled(True)
|
|
115
115
|
else:
|
|
116
116
|
self.upload_button.setEnabled(False)
|
|
@@ -121,60 +121,21 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
121
121
|
Create widgets common to StarDist and Cellpose.
|
|
122
122
|
"""
|
|
123
123
|
|
|
124
|
-
self.base_block =
|
|
124
|
+
self.base_block = QVBoxLayout()
|
|
125
125
|
|
|
126
126
|
pixel_calib_layout = QHBoxLayout()
|
|
127
|
-
self.calibration_label = QLabel("
|
|
127
|
+
self.calibration_label = QLabel("input spatial\ncalibration: ")
|
|
128
128
|
self.spatial_calib_le = QLineEdit("0,1")
|
|
129
129
|
self.qdv = QDoubleValidator(0.0, np.amax([self.parent_window.parent_window.shape_x, self.parent_window.parent_window.shape_y]), 8, notation=QDoubleValidator.StandardNotation)
|
|
130
130
|
self.spatial_calib_le.setValidator(self.qdv)
|
|
131
131
|
pixel_calib_layout.addWidget(self.calibration_label, 30)
|
|
132
132
|
pixel_calib_layout.addWidget(self.spatial_calib_le, 70)
|
|
133
|
-
self.base_block.addLayout(pixel_calib_layout
|
|
134
|
-
|
|
135
|
-
self.channel_options = ["--","live_nuclei_channel", "dead_nuclei_channel", "effector_fluo_channel", "brightfield_channel", "adhesion_channel", "fluo_channel_1", "fluo_channel_2"]
|
|
136
|
-
exp_channels = self.parent_window.parent_window.exp_channels
|
|
137
|
-
for ec in exp_channels:
|
|
138
|
-
if ec not in self.channel_options:
|
|
139
|
-
self.channel_options.append(ec)
|
|
140
|
-
self.channel_options += ['None']
|
|
141
|
-
|
|
142
|
-
channel_1_layout = QHBoxLayout()
|
|
143
|
-
self.ch_1_label = QLabel("channel 1: ")
|
|
144
|
-
self.combo_ch1 = QComboBox()
|
|
145
|
-
self.combo_ch1.addItems(self.channel_options)
|
|
146
|
-
channel_1_layout.addWidget(self.ch_1_label,30)
|
|
147
|
-
channel_1_layout.addWidget(self.combo_ch1, 70)
|
|
148
|
-
self.base_block.addLayout(channel_1_layout, 1, 0, 1, 3, alignment=Qt.AlignRight)
|
|
149
|
-
|
|
150
|
-
channel_2_layout = QHBoxLayout()
|
|
151
|
-
self.ch_2_label = QLabel("channel 2: ")
|
|
152
|
-
self.combo_ch2 = QComboBox()
|
|
153
|
-
self.combo_ch2.addItems(self.channel_options)
|
|
154
|
-
channel_2_layout.addWidget(self.ch_2_label, 30)
|
|
155
|
-
channel_2_layout.addWidget(self.combo_ch2, 70)
|
|
156
|
-
self.base_block.addLayout(channel_2_layout, 2, 0, 1, 3, alignment=Qt.AlignRight)
|
|
157
|
-
|
|
158
|
-
def generate_stardist_specific_block(self):
|
|
133
|
+
self.base_block.addLayout(pixel_calib_layout)
|
|
159
134
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
self.stardist_block = QGridLayout()
|
|
165
|
-
self.ch_3_label = QLabel("channel 3: ")
|
|
166
|
-
self.stardist_block.addWidget(self.ch_3_label, 0, 0, 1, 1, alignment=Qt.AlignLeft)
|
|
167
|
-
self.combo_ch3 = QComboBox()
|
|
168
|
-
self.combo_ch3.addItems(self.channel_options)
|
|
169
|
-
self.stardist_block.addWidget(self.combo_ch3, 0, 1, 1, 2, alignment=Qt.AlignRight)
|
|
135
|
+
self.channel_layout = ChannelNormGenerator(self, mode='channels',init_n_channels=2)
|
|
136
|
+
self.channel_layout.setContentsMargins(0,0,0,0)
|
|
137
|
+
self.base_block.addLayout(self.channel_layout)
|
|
170
138
|
|
|
171
|
-
self.ch_4_label = QLabel("channel 4: ")
|
|
172
|
-
self.stardist_block.addWidget(self.ch_4_label, 1, 0, 1, 1, alignment=Qt.AlignLeft)
|
|
173
|
-
self.combo_ch4 = QComboBox()
|
|
174
|
-
self.combo_ch4.addItems(self.channel_options)
|
|
175
|
-
self.stardist_block.addWidget(self.combo_ch4, 1, 1, 1, 2, alignment=Qt.AlignRight)
|
|
176
|
-
|
|
177
|
-
self.stardist_options = [self.ch_3_label, self.ch_4_label, self.combo_ch3, self.combo_ch4]
|
|
178
139
|
|
|
179
140
|
def generate_cellpose_options(self):
|
|
180
141
|
|
|
@@ -182,25 +143,31 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
182
143
|
Create Cellpose specific fields to use the model properly when calling it from the app.
|
|
183
144
|
"""
|
|
184
145
|
|
|
185
|
-
self.cellpose_block =
|
|
186
|
-
self.cp_diameter_label = QLabel('
|
|
146
|
+
self.cellpose_block = QVBoxLayout()
|
|
147
|
+
self.cp_diameter_label = QLabel('cell\ndiameter: ')
|
|
187
148
|
self.cp_diameter_le = QLineEdit("30,0")
|
|
188
149
|
self.cp_diameter_le.setValidator(self.qdv)
|
|
189
|
-
|
|
190
|
-
|
|
150
|
+
diam_hbox = QHBoxLayout()
|
|
151
|
+
diam_hbox.addWidget(self.cp_diameter_label, 30)
|
|
152
|
+
diam_hbox.addWidget(self.cp_diameter_le, 70)
|
|
153
|
+
self.cellpose_block.addLayout(diam_hbox)
|
|
191
154
|
|
|
192
155
|
qdv_prob = QDoubleValidator(-6, 6, 8, notation=QDoubleValidator.StandardNotation)
|
|
193
156
|
self.cp_cellprob_label = QLabel('cellprob\nthreshold: ')
|
|
194
157
|
self.cp_cellprob_le = QLineEdit('0,0')
|
|
195
158
|
self.cp_cellprob_le.setValidator(qdv_prob)
|
|
196
|
-
|
|
197
|
-
|
|
159
|
+
cellprob_hbox = QHBoxLayout()
|
|
160
|
+
cellprob_hbox.addWidget(self.cp_cellprob_label, 30)
|
|
161
|
+
cellprob_hbox.addWidget(self.cp_cellprob_le, 70)
|
|
162
|
+
self.cellpose_block.addLayout(cellprob_hbox)
|
|
198
163
|
|
|
199
164
|
self.cp_flow_label = QLabel('flow threshold: ')
|
|
200
165
|
self.cp_flow_le = QLineEdit('0,4')
|
|
201
166
|
self.cp_flow_le.setValidator(qdv_prob)
|
|
202
|
-
|
|
203
|
-
|
|
167
|
+
flow_hbox = QHBoxLayout()
|
|
168
|
+
flow_hbox.addWidget(self.cp_flow_label, 30)
|
|
169
|
+
flow_hbox.addWidget(self.cp_flow_le, 70)
|
|
170
|
+
self.cellpose_block.addLayout(flow_hbox)
|
|
204
171
|
|
|
205
172
|
self.cellpose_options = [self.cp_diameter_label,self.cp_diameter_le,self.cp_cellprob_label,
|
|
206
173
|
self.cp_cellprob_le, self.cp_flow_label, self.cp_flow_le]
|
|
@@ -275,37 +242,46 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
275
242
|
"""
|
|
276
243
|
Show the relevant widgets, mask the others.
|
|
277
244
|
"""
|
|
245
|
+
self.base_block_options = [self.calibration_label, self.spatial_calib_le,*self.channel_layout.channel_cbs,*self.channel_layout.channel_labels,*self.channel_layout.normalization_mode_btns, *self.channel_layout.normalization_clip_btns, *self.channel_layout.normalization_min_value_lbl,
|
|
246
|
+
*self.channel_layout.normalization_min_value_le, *self.channel_layout.normalization_max_value_lbl,*self.channel_layout.normalization_max_value_le,
|
|
247
|
+
self.channel_layout.add_col_btn]
|
|
278
248
|
|
|
279
249
|
if self.cellpose_button.isChecked():
|
|
280
250
|
self.spatial_calib_le.setToolTip('Cellpose rescales the images such that the cells are 30.0 pixels. You can compute the scale from the training data as\n(pixel calibration [µm] in training images)*(cell diameter [px] in training images)/(30 [px]).\nIf you pass images with a different calibration to the model, they will be rescaled automatically.\nThe rescaling is ignored if you pass a diameter different from 30 px below.')
|
|
281
|
-
self.
|
|
282
|
-
self.
|
|
283
|
-
for c in
|
|
251
|
+
self.channel_layout.channel_labels[0].setText('cyto: ')
|
|
252
|
+
self.channel_layout.channel_labels[1].setText('nuclei: ')
|
|
253
|
+
for c in [self.threshold_config_button]:
|
|
284
254
|
c.hide()
|
|
285
255
|
for c in self.cellpose_options+self.base_block_options:
|
|
286
256
|
c.show()
|
|
287
257
|
self.unlock_upload()
|
|
288
258
|
elif self.stardist_button.isChecked():
|
|
289
259
|
self.spatial_calib_le.setToolTip('')
|
|
290
|
-
self.
|
|
291
|
-
self.
|
|
292
|
-
for c in self.
|
|
260
|
+
self.channel_layout.channel_labels[0].setText('channel 1: ')
|
|
261
|
+
self.channel_layout.channel_labels[1].setText('channel 2: ')
|
|
262
|
+
for c in self.base_block_options:
|
|
293
263
|
c.show()
|
|
294
264
|
for c in self.cellpose_options+[self.threshold_config_button]:
|
|
295
265
|
c.hide()
|
|
296
266
|
self.unlock_upload()
|
|
297
267
|
else:
|
|
298
|
-
for c in self.
|
|
268
|
+
for c in self.base_block_options + self.cellpose_options:
|
|
299
269
|
c.hide()
|
|
300
270
|
self.threshold_config_button.show()
|
|
301
271
|
self.upload_button.setEnabled(True)
|
|
302
|
-
|
|
272
|
+
|
|
273
|
+
self.resize(self.sizeHint());
|
|
274
|
+
#self.adjustSize()
|
|
303
275
|
|
|
304
276
|
def upload_model(self):
|
|
305
277
|
|
|
306
278
|
"""
|
|
307
279
|
Upload the model.
|
|
308
280
|
"""
|
|
281
|
+
|
|
282
|
+
channels = []
|
|
283
|
+
for i in range(len(self.channel_layout.channel_cbs)):
|
|
284
|
+
channels.append(self.channel_layout.channel_cbs[i].currentText())
|
|
309
285
|
|
|
310
286
|
if self.file_label.text()=='No file chosen':
|
|
311
287
|
msgBox = QMessageBox()
|
|
@@ -334,6 +310,7 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
334
310
|
return None
|
|
335
311
|
|
|
336
312
|
elif self.cellpose_button.isChecked():
|
|
313
|
+
|
|
337
314
|
try:
|
|
338
315
|
if not os.path.exists(self.folder_dest):
|
|
339
316
|
os.mkdir(self.folder_dest)
|
|
@@ -347,6 +324,23 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
347
324
|
returnValue = msgBox.exec()
|
|
348
325
|
if returnValue == QMessageBox.Ok:
|
|
349
326
|
return None
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
model = CellposeModel(pretrained_model=self.destination, model_type=None, nchan=len(channels))
|
|
330
|
+
self.scale_model = model.diam_mean
|
|
331
|
+
print(f'{self.scale_model=}')
|
|
332
|
+
del model
|
|
333
|
+
gc.collect()
|
|
334
|
+
except Exception as e:
|
|
335
|
+
print(e)
|
|
336
|
+
msgBox = QMessageBox()
|
|
337
|
+
msgBox.setIcon(QMessageBox.Critical)
|
|
338
|
+
msgBox.setText(f"Cellpose model could not be loaded...")
|
|
339
|
+
msgBox.setWindowTitle("Error")
|
|
340
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
341
|
+
returnValue = msgBox.exec()
|
|
342
|
+
if returnValue == QMessageBox.Ok:
|
|
343
|
+
return None
|
|
350
344
|
|
|
351
345
|
self.generate_input_config()
|
|
352
346
|
self.parent_window.init_seg_model_list()
|
|
@@ -355,12 +349,12 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
355
349
|
if self.mode=="targets":
|
|
356
350
|
self.parent_window.threshold_config_targets = self.filename
|
|
357
351
|
self.parent_window.seg_model_list.setCurrentText('Threshold')
|
|
358
|
-
print('Path to
|
|
352
|
+
print('Path to the traditional segmentation pipeline successfully set in celldetective...')
|
|
359
353
|
self.close()
|
|
360
354
|
elif self.mode=="effectors":
|
|
361
355
|
self.parent_window.threshold_config_effectors = self.filename
|
|
362
356
|
self.parent_window.seg_model_list.setCurrentText('Threshold')
|
|
363
|
-
print('Path to
|
|
357
|
+
print('Path to the traditional segmentation pipeline successfully set in celldetective...')
|
|
364
358
|
self.close()
|
|
365
359
|
|
|
366
360
|
def generate_input_config(self):
|
|
@@ -370,69 +364,91 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
370
364
|
a configuration to use the uploaded model properly.
|
|
371
365
|
"""
|
|
372
366
|
|
|
367
|
+
if self.threshold_button.isChecked():
|
|
368
|
+
model_type = "threshold"
|
|
369
|
+
return None
|
|
370
|
+
|
|
371
|
+
channels = []
|
|
372
|
+
for i in range(len(self.channel_layout.channel_cbs)):
|
|
373
|
+
channels.append(self.channel_layout.channel_cbs[i].currentText())
|
|
374
|
+
|
|
375
|
+
slots_to_keep = np.where(np.array(channels)!='--')[0]
|
|
376
|
+
while '--' in channels:
|
|
377
|
+
channels.remove('--')
|
|
378
|
+
|
|
379
|
+
norm_values = np.array([[float(a.replace(',','.')),float(b.replace(',','.'))] for a,b in zip([l.text() for l in self.channel_layout.normalization_min_value_le],
|
|
380
|
+
[l.text() for l in self.channel_layout.normalization_max_value_le])])
|
|
381
|
+
norm_values = norm_values[slots_to_keep]
|
|
382
|
+
norm_values = [list(v) for v in norm_values]
|
|
383
|
+
|
|
384
|
+
clip_values = np.array(self.channel_layout.clip_option)
|
|
385
|
+
clip_values = list(clip_values[slots_to_keep])
|
|
386
|
+
clip_values = [bool(c) for c in clip_values]
|
|
387
|
+
|
|
388
|
+
normalization_mode = np.array(self.channel_layout.normalization_mode)
|
|
389
|
+
normalization_mode = list(normalization_mode[slots_to_keep])
|
|
390
|
+
normalization_mode = [bool(m) for m in normalization_mode]
|
|
391
|
+
|
|
373
392
|
dico = {}
|
|
374
393
|
|
|
375
394
|
# Check model option
|
|
376
395
|
if self.stardist_button.isChecked():
|
|
377
396
|
|
|
378
397
|
# Get channels
|
|
379
|
-
channels = []
|
|
380
|
-
for c in self.combos:
|
|
381
|
-
|
|
382
|
-
|
|
398
|
+
# channels = []
|
|
399
|
+
# for c in self.combos:
|
|
400
|
+
# if c.currentText()!="--":
|
|
401
|
+
# channels.append(c.currentText())
|
|
383
402
|
model_type = "stardist"
|
|
384
403
|
spatial_calib = float(self.spatial_calib_le.text().replace(',','.'))
|
|
385
|
-
normalize = self.normalize_checkbox.isChecked()
|
|
404
|
+
#normalize = self.normalize_checkbox.isChecked()
|
|
386
405
|
dico.update({"channels": channels,
|
|
387
|
-
"normalize": normalize,
|
|
406
|
+
#"normalize": normalize,
|
|
388
407
|
"spatial_calibration": spatial_calib,
|
|
389
|
-
'normalization_percentile':
|
|
390
|
-
'normalization_clip':
|
|
391
|
-
'normalization_values':
|
|
408
|
+
'normalization_percentile': normalization_mode,
|
|
409
|
+
'normalization_clip': clip_values,
|
|
410
|
+
'normalization_values': norm_values,
|
|
392
411
|
})
|
|
393
412
|
|
|
394
413
|
elif self.cellpose_button.isChecked():
|
|
395
414
|
|
|
396
415
|
# Get channels (cyto and nucleus)
|
|
397
|
-
channels = []
|
|
398
|
-
for c in self.combos[:2]:
|
|
399
|
-
|
|
400
|
-
|
|
416
|
+
# channels = []
|
|
417
|
+
# for c in self.combos[:2]:
|
|
418
|
+
# if c.currentText()!="--":
|
|
419
|
+
# channels.append(c.currentText())
|
|
401
420
|
|
|
402
421
|
model_type = "cellpose"
|
|
403
|
-
#
|
|
422
|
+
#cellpose requires at least two channels
|
|
404
423
|
if len(channels)==1:
|
|
405
424
|
channels += ['None']
|
|
425
|
+
normalization_mode += [True]
|
|
426
|
+
clip_values += [True]
|
|
427
|
+
norm_values += [[1,99]]
|
|
406
428
|
|
|
407
429
|
diameter = float(self.cp_diameter_le.text().replace(',','.'))
|
|
408
430
|
cellprob_threshold = float(self.cp_cellprob_le.text().replace(',','.'))
|
|
409
431
|
flow_threshold = float(self.cp_flow_le.text().replace(',','.'))
|
|
410
|
-
normalize = self.normalize_checkbox.isChecked()
|
|
432
|
+
#normalize = self.normalize_checkbox.isChecked()
|
|
411
433
|
spatial_calib = float(self.spatial_calib_le.text().replace(',','.'))
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
else:
|
|
416
|
-
norm_percentile = [False]*len(channels)
|
|
417
|
-
norm_clip = [False]*len(channels)
|
|
418
|
-
normalization_values = [[1.0,99.0]]*len(channels)
|
|
434
|
+
# assume 30 px diameter in cellpose model
|
|
435
|
+
spatial_calib = spatial_calib * diameter / self.scale_model
|
|
436
|
+
diameter = 30.0
|
|
419
437
|
|
|
420
438
|
dico.update({"channels": channels,
|
|
421
439
|
"diameter": diameter,
|
|
422
440
|
"cellprob_threshold": cellprob_threshold,
|
|
423
441
|
"flow_threshold": flow_threshold,
|
|
424
|
-
"normalize": normalize,
|
|
442
|
+
#"normalize": normalize,
|
|
425
443
|
"spatial_calibration": spatial_calib,
|
|
426
|
-
'normalization_percentile':
|
|
427
|
-
'normalization_clip':
|
|
428
|
-
'normalization_values':
|
|
444
|
+
'normalization_percentile': normalization_mode,
|
|
445
|
+
'normalization_clip': clip_values,
|
|
446
|
+
'normalization_values': norm_values,
|
|
429
447
|
})
|
|
430
448
|
|
|
431
|
-
elif self.threshold_button.isChecked():
|
|
432
|
-
model_type = "threshold"
|
|
433
|
-
return None
|
|
434
449
|
|
|
435
450
|
dico.update({"model_type": model_type})
|
|
451
|
+
print(f"{dico=}")
|
|
436
452
|
json_object = json.dumps(dico, indent=4)
|
|
437
453
|
|
|
438
454
|
# Writing to sample.json
|
|
@@ -450,8 +466,8 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
450
466
|
# shutil.rmtree(self.folder_dest)
|
|
451
467
|
#os.mkdir(self.folder_dest)
|
|
452
468
|
|
|
453
|
-
print("Configuration successfully written in ",self.folder_dest+"
|
|
454
|
-
with open(self.folder_dest+"
|
|
469
|
+
print("Configuration successfully written in ",self.folder_dest+os.sep+"config_input.json")
|
|
470
|
+
with open(self.folder_dest+os.sep+"config_input.json", "w") as outfile:
|
|
455
471
|
outfile.write(json_object)
|
|
456
472
|
|
|
457
473
|
def open_threshold_config_wizard(self):
|