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.
Files changed (54) hide show
  1. celldetective/__main__.py +12 -5
  2. celldetective/events.py +28 -2
  3. celldetective/gui/about.py +0 -1
  4. celldetective/gui/analyze_block.py +3 -18
  5. celldetective/gui/btrack_options.py +126 -21
  6. celldetective/gui/classifier_widget.py +68 -107
  7. celldetective/gui/configure_new_exp.py +37 -4
  8. celldetective/gui/control_panel.py +14 -30
  9. celldetective/gui/generic_signal_plot.py +793 -0
  10. celldetective/gui/gui_utils.py +401 -226
  11. celldetective/gui/json_readers.py +0 -2
  12. celldetective/gui/layouts.py +269 -25
  13. celldetective/gui/measurement_options.py +14 -23
  14. celldetective/gui/neighborhood_options.py +6 -16
  15. celldetective/gui/plot_measurements.py +10 -23
  16. celldetective/gui/plot_signals_ui.py +53 -687
  17. celldetective/gui/process_block.py +320 -186
  18. celldetective/gui/retrain_segmentation_model_options.py +30 -47
  19. celldetective/gui/retrain_signal_model_options.py +5 -14
  20. celldetective/gui/seg_model_loader.py +129 -113
  21. celldetective/gui/signal_annotator.py +93 -103
  22. celldetective/gui/signal_annotator2.py +9 -13
  23. celldetective/gui/styles.py +32 -0
  24. celldetective/gui/survival_ui.py +49 -712
  25. celldetective/gui/tableUI.py +4 -39
  26. celldetective/gui/thresholds_gui.py +38 -11
  27. celldetective/gui/viewers.py +6 -7
  28. celldetective/io.py +62 -84
  29. celldetective/measure.py +374 -15
  30. celldetective/models/segmentation_effectors/ricm-bimodal/config_input.json +130 -0
  31. celldetective/models/segmentation_effectors/ricm-bimodal/ricm-bimodal +0 -0
  32. celldetective/models/segmentation_effectors/ricm-bimodal/training_instructions.json +37 -0
  33. celldetective/neighborhood.py +3 -7
  34. celldetective/preprocessing.py +2 -4
  35. celldetective/relative_measurements.py +0 -3
  36. celldetective/scripts/analyze_signals.py +0 -1
  37. celldetective/scripts/measure_cells.py +1 -3
  38. celldetective/scripts/measure_relative.py +1 -2
  39. celldetective/scripts/segment_cells.py +16 -12
  40. celldetective/scripts/segment_cells_thresholds.py +17 -10
  41. celldetective/scripts/track_cells.py +18 -18
  42. celldetective/scripts/train_segmentation_model.py +1 -2
  43. celldetective/scripts/train_signal_model.py +0 -3
  44. celldetective/segmentation.py +1 -1
  45. celldetective/signals.py +20 -8
  46. celldetective/tracking.py +2 -1
  47. celldetective/utils.py +126 -18
  48. {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/METADATA +19 -12
  49. celldetective-1.2.2.dist-info/RECORD +92 -0
  50. {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/WHEEL +1 -1
  51. celldetective-1.2.0.dist-info/RECORD +0 -88
  52. {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/LICENSE +0 -0
  53. {celldetective-1.2.0.dist-info → celldetective-1.2.2.dist-info}/entry_points.txt +0 -0
  54. {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, QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
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, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, GeometryChoice, OperationChoice
4
+ from celldetective.gui.gui_utils import center_window
5
5
  from celldetective.gui.layouts import ChannelNormGenerator
6
6
 
7
- from superqt import QLabeledDoubleRangeSlider, QLabeledDoubleSlider,QLabeledSlider
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 extract_experiment_channels, get_software_location
11
- from celldetective.io import interpret_tracking_configuration, load_frames, get_segmentation_datasets_list, locate_segmentation_dataset
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,01')
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('4')
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(1.5)
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.25)
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,300)
330
+ self.epochs_slider.setRange(1,500)
331
+ self.lr_le.setText('0,0003')
338
332
  else:
339
- self.epochs_slider.setRange(1,3000)
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 os.sep.join([self.pretrained_model,"config_input.json"]) in subfiles:
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(os.sep)[-1]}_{datetime.today().strftime('%Y-%m-%d')}")
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
- print(self.pretrained_model)
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 = QWidget()
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
- self.diamWidget.close()
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(os.sep)[-2]
481
- self.model_name = self.pretrained_model.split(os.sep)[-1]
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, QTextEdit, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
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, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, GeometryChoice, OperationChoice
4
+ from celldetective.gui.gui_utils import center_window
5
5
  from celldetective.gui.layouts import ChannelNormGenerator
6
- from superqt import QLabeledDoubleRangeSlider, QLabeledDoubleSlider, QLabeledSlider, QSearchableComboBox
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 extract_experiment_channels, get_software_location
10
- from celldetective.io import interpret_tracking_configuration, load_frames, locate_signal_dataset, get_signal_datasets_list, load_experiment_tables
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, QLabel, QCheckBox, QLineEdit, QHBoxLayout, QRadioButton, QComboBox, QFileDialog, QPushButton, QMessageBox
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.generate_stardist_specific_block()
61
- self.layout.addLayout(self.stardist_block, 3,0,1,2)
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, self.ch_1_label, self.combo_ch1, self.ch_2_label, self.combo_ch2,
93
- self.normalize_checkbox, self.normalize_lbl,
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.combos:
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.combos]):
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.combos[:2]]):
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 = QGridLayout()
124
+ self.base_block = QVBoxLayout()
125
125
 
126
126
  pixel_calib_layout = QHBoxLayout()
127
- self.calibration_label = QLabel("pixel calibration: ")
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, 0,0,1,3)
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
- Create StarDist specific fields to use the model properly when calling it from the app.
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 = QGridLayout()
186
- self.cp_diameter_label = QLabel('diameter: ')
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
- self.cellpose_block.addWidget(self.cp_diameter_label, 0, 0, 1, 2)
190
- self.cellpose_block.addWidget(self.cp_diameter_le, 0, 1, 1, 2)
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
- self.cellpose_block.addWidget(self.cp_cellprob_label, 1, 0, 1, 2)
197
- self.cellpose_block.addWidget(self.cp_cellprob_le, 1, 1, 1, 2)
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
- self.cellpose_block.addWidget(self.cp_flow_label, 2, 0, 1, 2)
203
- self.cellpose_block.addWidget(self.cp_flow_le, 2, 1, 1, 2)
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.ch_1_label.setText('cyto: ')
282
- self.ch_2_label.setText('nuclei: ')
283
- for c in self.stardist_options+[self.threshold_config_button]:
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.ch_1_label.setText('channel 1: ')
291
- self.ch_2_label.setText('channel 2: ')
292
- for c in self.stardist_options+self.base_block_options:
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.stardist_options+self.cellpose_options+self.base_block_options:
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
- self.adjustSize()
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 threshold configuration successfully set in the software')
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 threshold configuration successfully set in the software')
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
- if c.currentText()!="--":
382
- channels.append(c.currentText())
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': norm_percentile,
390
- 'normalization_clip': norm_clip,
391
- 'normalization_values': 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
- if c.currentText()!="--":
400
- channels.append(c.currentText())
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
- # cellpose requires at least two channels
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
- if normalize:
413
- norm_percentile = [True]*len(channels)
414
- norm_clip = [True]*len(channels)
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': norm_percentile,
427
- 'normalization_clip': norm_clip,
428
- 'normalization_values': 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+"/config_input.json")
454
- with open(self.folder_dest+"/config_input.json", "w") as outfile:
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):