celldetective 1.1.1__tar.gz → 1.1.1.post3__tar.gz

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 (90) hide show
  1. {celldetective-1.1.1 → celldetective-1.1.1.post3}/PKG-INFO +1 -1
  2. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/extra_properties.py +1 -1
  3. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/filters.py +39 -11
  4. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/classifier_widget.py +56 -7
  5. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/layouts.py +246 -3
  6. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/retrain_segmentation_model_options.py +66 -164
  7. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/retrain_signal_model_options.py +18 -164
  8. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/tableUI.py +175 -61
  9. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/thresholds_gui.py +44 -5
  10. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/viewers.py +1 -1
  11. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/io.py +29 -14
  12. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/preprocessing.py +23 -11
  13. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/segment_cells.py +13 -4
  14. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/train_segmentation_model.py +11 -22
  15. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/segmentation.py +16 -12
  16. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/utils.py +6 -2
  17. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/PKG-INFO +1 -1
  18. {celldetective-1.1.1 → celldetective-1.1.1.post3}/setup.py +1 -1
  19. {celldetective-1.1.1 → celldetective-1.1.1.post3}/LICENSE +0 -0
  20. {celldetective-1.1.1 → celldetective-1.1.1.post3}/README.md +0 -0
  21. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/__init__.py +0 -0
  22. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/__main__.py +0 -0
  23. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/datasets/segmentation_annotations/blank +0 -0
  24. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/datasets/signal_annotations/blank +0 -0
  25. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/events.py +0 -0
  26. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/__init__.py +0 -0
  27. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/about.py +0 -0
  28. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/analyze_block.py +0 -0
  29. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/btrack_options.py +0 -0
  30. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/configure_new_exp.py +0 -0
  31. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/control_panel.py +0 -0
  32. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/gui_utils.py +0 -0
  33. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/json_readers.py +0 -0
  34. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/measurement_options.py +0 -0
  35. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/neighborhood_options.py +0 -0
  36. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/plot_measurements.py +0 -0
  37. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/plot_signals_ui.py +0 -0
  38. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/process_block.py +0 -0
  39. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/seg_model_loader.py +0 -0
  40. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/signal_annotator.py +0 -0
  41. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/signal_annotator_options.py +0 -0
  42. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/styles.py +0 -0
  43. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/gui/survival_ui.py +0 -0
  44. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/logo-large.png +0 -0
  45. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/logo.png +0 -0
  46. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/signals_icon.png +0 -0
  47. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/splash-test.png +0 -0
  48. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/splash.png +0 -0
  49. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/splash0.png +0 -0
  50. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/survival2.png +0 -0
  51. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/vignette_signals2.png +0 -0
  52. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/icons/vignette_signals2.svg +0 -0
  53. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/links/zenodo.json +0 -0
  54. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/measure.py +0 -0
  55. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/segmentation_effectors/blank +0 -0
  56. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -0
  57. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  58. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -0
  59. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/segmentation_generic/blank +0 -0
  60. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/segmentation_targets/blank +0 -0
  61. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/signal_detection/blank +0 -0
  62. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/tracking_configs/mcf7.json +0 -0
  63. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/tracking_configs/ricm.json +0 -0
  64. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/models/tracking_configs/ricm2.json +0 -0
  65. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/neighborhood.py +0 -0
  66. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/analyze_signals.py +0 -0
  67. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/measure_cells.py +0 -0
  68. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  69. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/track_cells.py +0 -0
  70. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/scripts/train_signal_model.py +0 -0
  71. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/signals.py +0 -0
  72. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective/tracking.py +0 -0
  73. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/SOURCES.txt +0 -0
  74. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/dependency_links.txt +0 -0
  75. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/entry_points.txt +0 -0
  76. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/not-zip-safe +0 -0
  77. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/requires.txt +0 -0
  78. {celldetective-1.1.1 → celldetective-1.1.1.post3}/celldetective.egg-info/top_level.txt +0 -0
  79. {celldetective-1.1.1 → celldetective-1.1.1.post3}/setup.cfg +0 -0
  80. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/__init__.py +0 -0
  81. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_events.py +0 -0
  82. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_filters.py +0 -0
  83. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_io.py +0 -0
  84. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_measure.py +0 -0
  85. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_neighborhood.py +0 -0
  86. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_preprocessing.py +0 -0
  87. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_segmentation.py +0 -0
  88. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_signals.py +0 -0
  89. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_tracking.py +0 -0
  90. {celldetective-1.1.1 → celldetective-1.1.1.post3}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: celldetective
3
- Version: 1.1.1
3
+ Version: 1.1.1.post3
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro
@@ -7,7 +7,7 @@ If intensity is in function name, it will be replaced by the name of the channel
7
7
  import warnings
8
8
 
9
9
  import numpy as np
10
- from scipy.ndimage import distance_transform_edt
10
+ from scipy.ndimage import distance_transform_edt, center_of_mass
11
11
  from scipy.spatial.distance import euclidean
12
12
 
13
13
 
@@ -1,21 +1,36 @@
1
1
  from skimage.filters import difference_of_gaussians, threshold_otsu, threshold_local, threshold_niblack, threshold_sauvola
2
+ from celldetective.utils import interpolate_nan
2
3
  import scipy.ndimage as snd
3
4
  import numpy as np
4
5
 
5
- def gauss_filter(img, sigma, *kwargs):
6
+ def gauss_filter(img, sigma, interpolate=True, *kwargs):
7
+ if interpolate:
8
+ img = interpolate_nan(img.astype(float))
6
9
  return snd.gaussian_filter(img.astype(float), sigma, *kwargs)
7
10
 
8
- def median_filter(img, size, *kwargs):
11
+ def median_filter(img, size, interpolate=True, *kwargs):
12
+ if interpolate:
13
+ img = interpolate_nan(img.astype(float))
14
+
9
15
  size = int(size)
10
16
  return snd.median_filter(img, size, *kwargs)
11
17
 
12
- def maximum_filter(img, size, *kwargs):
18
+ def maximum_filter(img, size, interpolate=True, *kwargs):
19
+ if interpolate:
20
+ img = interpolate_nan(img.astype(float))
21
+
13
22
  return snd.maximum_filter(img.astype(float), size, *kwargs)
14
23
 
15
- def minimum_filter(img, size, *kwargs):
24
+ def minimum_filter(img, size, interpolate=True, *kwargs):
25
+ if interpolate:
26
+ img = interpolate_nan(img.astype(float))
27
+
16
28
  return snd.minimum_filter(img.astype(float), size, *kwargs)
17
29
 
18
- def percentile_filter(img, percentile, size, *kwargs):
30
+ def percentile_filter(img, percentile, size, interpolate=True, *kwargs):
31
+ if interpolate:
32
+ img = interpolate_nan(img.astype(float))
33
+
19
34
  return snd.percentile_filter(img.astype(float), percentile, size, *kwargs)
20
35
 
21
36
  def subtract_filter(img, value, *kwargs):
@@ -24,14 +39,19 @@ def subtract_filter(img, value, *kwargs):
24
39
  def abs_filter(img, *kwargs):
25
40
  return np.abs(img)
26
41
 
27
- def ln_filter(img, *kwargs):
42
+ def ln_filter(img, interpolate=True, *kwargs):
43
+ if interpolate:
44
+ img = interpolate_nan(img.astype(float))
28
45
 
29
46
  img[np.where(img>0.)] = np.log(img[np.where(img>0.)])
30
47
  img[np.where(img<=0.)] = 0.
31
48
 
32
49
  return img
33
50
 
34
- def variance_filter(img, size):
51
+ def variance_filter(img, size, interpolate=True):
52
+
53
+ if interpolate:
54
+ img = interpolate_nan(img.astype(float))
35
55
 
36
56
  size = int(size)
37
57
  img = img.astype(float)
@@ -41,8 +61,10 @@ def variance_filter(img, size):
41
61
 
42
62
  return img
43
63
 
44
- def std_filter(img, size):
64
+ def std_filter(img, size, interpolate=True):
45
65
 
66
+ if interpolate:
67
+ img = interpolate_nan(img.astype(float))
46
68
  size = int(size)
47
69
  img = img.astype(float)
48
70
  win_mean = snd.uniform_filter(img, (size,size), mode='wrap')
@@ -53,10 +75,14 @@ def std_filter(img, size):
53
75
 
54
76
  return img
55
77
 
56
- def laplace_filter(img, output=float, *kwargs):
78
+ def laplace_filter(img, output=float, interpolate=True, *kwargs):
79
+ if interpolate:
80
+ img = interpolate_nan(img.astype(float))
57
81
  return snd.laplace(img.astype(float), *kwargs)
58
82
 
59
- def dog_filter(img, sigma_low, sigma_high, *kwargs):
83
+ def dog_filter(img, sigma_low, sigma_high, interpolate=True, *kwargs):
84
+ if interpolate:
85
+ img = interpolate_nan(img.astype(float))
60
86
  return difference_of_gaussians(img.astype(float), sigma_low, sigma_high, *kwargs)
61
87
 
62
88
  def otsu_filter(img, *kwargs):
@@ -82,7 +108,9 @@ def sauvola_filter(img, *kwargs):
82
108
  def log_filter(img, sigma, *kwargs):
83
109
  return snd.gaussian_laplace(img.astype(float), sigma, *kwargs)
84
110
 
85
- def tophat_filter(img, size, connectivity=4, *kwargs):
111
+ def tophat_filter(img, size, connectivity=4, interpolate=True, *kwargs):
112
+ if interpolate:
113
+ img = interpolate_nan(img.astype(float))
86
114
  structure = snd.generate_binary_structure(rank=2, connectivity=connectivity)
87
115
  img = snd.white_tophat(img.astype(float), structure=structure, size=size, *kwargs)
88
116
  return img
@@ -136,9 +136,10 @@ class ClassifierWidget(QWidget, Styles):
136
136
 
137
137
  self.irreversible_event_btn = QRadioButton('irreversible event')
138
138
  self.unique_state_btn = QRadioButton('unique state')
139
- self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
140
139
  time_corr_btn_group = QButtonGroup()
141
140
  self.unique_state_btn.click()
141
+ self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
142
+
142
143
  for btn in self.time_corr_options:
143
144
  time_corr_btn_group.addButton(btn)
144
145
  btn.setEnabled(False)
@@ -148,8 +149,28 @@ class ClassifierWidget(QWidget, Styles):
148
149
  time_corr_layout.addWidget(self.irreversible_event_btn, 50,alignment=Qt.AlignCenter)
149
150
  layout.addLayout(time_corr_layout)
150
151
 
152
+ self.r2_slider = QLabeledDoubleSlider()
153
+ self.r2_slider.setValue(0.75)
154
+ self.r2_slider.setRange(0,1)
155
+ self.r2_slider.setSingleStep(0.01)
156
+ self.r2_slider.setOrientation(1)
157
+ self.r2_label = QLabel('R2 tolerance:')
158
+ self.r2_label.setToolTip('Minimum R2 between the fit sigmoid and the binary response to the filters to accept the event.')
159
+ r2_threshold_layout = QHBoxLayout()
160
+ r2_threshold_layout.addWidget(QLabel(''), 50)
161
+ r2_threshold_layout.addWidget(self.r2_label, 15)
162
+ r2_threshold_layout.addWidget(self.r2_slider, 35)
163
+ layout.addLayout(r2_threshold_layout)
164
+
165
+ self.irreversible_event_btn.clicked.connect(self.activate_r2)
166
+ self.unique_state_btn.clicked.connect(self.activate_r2)
167
+
168
+ for wg in [self.r2_slider, self.r2_label]:
169
+ wg.setEnabled(False)
170
+
151
171
  layout.addWidget(QLabel())
152
172
 
173
+
153
174
  self.submit_btn = QPushButton('apply')
154
175
  self.submit_btn.setStyleSheet(self.button_style_sheet)
155
176
  self.submit_btn.clicked.connect(self.submit_classification)
@@ -158,14 +179,30 @@ class ClassifierWidget(QWidget, Styles):
158
179
  self.frame_slider.valueChanged.connect(self.set_frame)
159
180
  self.alpha_slider.valueChanged.connect(self.set_transparency)
160
181
 
182
+ def activate_r2(self):
183
+ if self.irreversible_event_btn.isChecked() and self.time_corr.isChecked():
184
+ for wg in [self.r2_slider, self.r2_label]:
185
+ wg.setEnabled(True)
186
+ else:
187
+ for wg in [self.r2_slider, self.r2_label]:
188
+ wg.setEnabled(False)
189
+
161
190
  def activate_time_corr_options(self):
162
191
 
163
192
  if self.time_corr.isChecked():
164
193
  for btn in self.time_corr_options:
165
194
  btn.setEnabled(True)
195
+ if self.irreversible_event_btn.isChecked():
196
+ for wg in [self.r2_slider, self.r2_label]:
197
+ wg.setEnabled(True)
198
+ else:
199
+ for wg in [self.r2_slider, self.r2_label]:
200
+ wg.setEnabled(False)
166
201
  else:
167
202
  for btn in self.time_corr_options:
168
203
  btn.setEnabled(False)
204
+ for wg in [self.r2_slider, self.r2_label]:
205
+ wg.setEnabled(False)
169
206
 
170
207
  def init_class(self):
171
208
 
@@ -207,8 +244,20 @@ class ClassifierWidget(QWidget, Styles):
207
244
  self.scat_props.set_alpha(self.currentAlpha)
208
245
  self.ax_props.set_xlabel(self.features_cb[1].currentText())
209
246
  self.ax_props.set_ylabel(self.features_cb[0].currentText())
210
- self.ax_props.set_xlim(1*self.df[self.features_cb[1].currentText()].min(),1.0*self.df[self.features_cb[1].currentText()].max())
211
- self.ax_props.set_ylim(1*self.df[self.features_cb[0].currentText()].min(),1.0*self.df[self.features_cb[0].currentText()].max())
247
+
248
+
249
+ feat_x = self.features_cb[1].currentText()
250
+ feat_y = self.features_cb[0].currentText()
251
+ min_x = self.df.dropna(subset=feat_x)[feat_x].min()
252
+ max_x = self.df.dropna(subset=feat_x)[feat_x].max()
253
+ min_y = self.df.dropna(subset=feat_y)[feat_y].min()
254
+ max_y = self.df.dropna(subset=feat_y)[feat_y].max()
255
+
256
+ if min_x==min_x and max_x==max_x:
257
+ self.ax_props.set_xlim(min_x, max_x)
258
+ if min_y==min_y and max_y==max_y:
259
+ self.ax_props.set_ylim(min_y, max_y)
260
+
212
261
  if feature_changed:
213
262
  self.propscanvas.canvas.toolbar.update()
214
263
  self.propscanvas.canvas.draw_idle()
@@ -400,15 +449,16 @@ class ClassifierWidget(QWidget, Styles):
400
449
  timeline = group['FRAME'].values
401
450
 
402
451
  try:
403
- popt, pcov = curve_fit(step_function, timeline, status_signal,p0=[self.df['FRAME'].max()//2, 0.5],maxfev=10000)
404
- r2 = r2_score(status_signal, step_function(timeline, *popt))
452
+ popt, pcov = curve_fit(step_function, timeline.astype(int), status_signal, p0=[self.df['FRAME'].max()//2, 0.8],maxfev=30000)
453
+ values = [step_function(t, *popt) for t in timeline]
454
+ r2 = r2_score(status_signal,values)
405
455
  except Exception as e:
406
456
  print(e)
407
457
  self.df.loc[indices, self.class_name_user] = 2.0
408
458
  self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
409
459
  continue
410
460
 
411
- if r2 > 0.7:
461
+ if r2 > float(self.r2_slider.value()):
412
462
  t0 = popt[0]
413
463
  self.df.loc[indices, self.class_name_user.replace('class','t')] = t0
414
464
  self.df.loc[indices, self.class_name_user] = 0.0
@@ -426,4 +476,3 @@ class ClassifierWidget(QWidget, Styles):
426
476
 
427
477
 
428
478
 
429
-
@@ -3,7 +3,7 @@ from celldetective.gui.gui_utils import ThresholdLineEdit
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from PyQt5.QtGui import QIntValidator
5
5
 
6
- from superqt import QLabeledRangeSlider, QLabeledSlider, QLabeledDoubleRangeSlider
6
+ from superqt import QLabeledRangeSlider, QLabeledSlider, QLabeledDoubleRangeSlider, QSearchableComboBox
7
7
 
8
8
  from superqt.fonticon import icon
9
9
  from fonticon_mdi6 import MDI6
@@ -12,6 +12,249 @@ from celldetective.gui.viewers import ThresholdedStackVisualizer, CellEdgeVisual
12
12
  from celldetective.gui import Styles
13
13
  from celldetective.gui.gui_utils import QuickSliderLayout
14
14
  from celldetective.preprocessing import correct_background_model, correct_background_model_free, estimate_background_per_condition
15
+ from functools import partial
16
+ from glob import glob
17
+ import os
18
+ import pandas as pd
19
+ import numpy as np
20
+
21
+ class ChannelNormGenerator(QVBoxLayout, Styles):
22
+
23
+ """Generator for list of channels"""
24
+
25
+ def __init__(self, parent_window=None, init_n_channels=4, mode='signals', *args):
26
+ super().__init__(*args)
27
+
28
+ self.parent_window = parent_window
29
+ self.mode = mode
30
+ self.init_n_channels = init_n_channels
31
+
32
+ if hasattr(self.parent_window.parent_window, 'locate_image'):
33
+ self.attr_parent = self.parent_window.parent_window
34
+ elif hasattr(self.parent_window.parent_window.parent_window, 'locate_image'):
35
+ self.attr_parent = self.parent_window.parent_window.parent_window
36
+ else:
37
+ self.attr_parent = self.parent_window.parent_window.parent_window.parent_window
38
+
39
+ self.channel_names = self.attr_parent.exp_channels
40
+ self.setContentsMargins(15,15,15,15)
41
+ self.generate_widgets()
42
+ self.add_to_layout()
43
+
44
+ def generate_widgets(self):
45
+
46
+ self.channel_cbs = [QSearchableComboBox() for i in range(self.init_n_channels)]
47
+ self.normalization_mode_btns = [QPushButton('') for i in range(self.init_n_channels)]
48
+ self.normalization_mode = [True for i in range(self.init_n_channels)]
49
+ self.normalization_clip_btns = [QPushButton('') for i in range(self.init_n_channels)]
50
+ self.clip_option = [False for i in range(self.init_n_channels)]
51
+
52
+ for i in range(self.init_n_channels):
53
+ self.normalization_mode_btns[i].setIcon(icon(MDI6.percent_circle,color="#1565c0"))
54
+ self.normalization_mode_btns[i].setIconSize(QSize(20, 20))
55
+ self.normalization_mode_btns[i].setStyleSheet(self.button_select_all)
56
+ self.normalization_mode_btns[i].setToolTip("Switch to absolute normalization values.")
57
+ self.normalization_mode_btns[i].clicked.connect(partial(self.switch_normalization_mode, i))
58
+
59
+ self.normalization_clip_btns[i].setIcon(icon(MDI6.content_cut,color="black"))
60
+ self.normalization_clip_btns[i].setIconSize(QSize(20, 20))
61
+ self.normalization_clip_btns[i].setStyleSheet(self.button_select_all)
62
+ self.normalization_clip_btns[i].clicked.connect(partial(self.switch_clipping_mode, i))
63
+ self.normalization_clip_btns[i].setToolTip('clip')
64
+
65
+ self.normalization_min_value_lbl = [QLabel('Min %: ') for i in range(self.init_n_channels)]
66
+ self.normalization_min_value_le = [QLineEdit('0.1') for i in range(self.init_n_channels)]
67
+ self.normalization_max_value_lbl = [QLabel('Max %: ') for i in range(self.init_n_channels)]
68
+ self.normalization_max_value_le = [QLineEdit('99.99') for i in range(self.init_n_channels)]
69
+
70
+ if self.mode=='signals':
71
+ tables = glob(self.parent_window.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{self.parent_window.mode}.csv']))
72
+ all_measurements = []
73
+ for tab in tables:
74
+ cols = pd.read_csv(tab, nrows=1).columns.tolist()
75
+ all_measurements.extend(cols)
76
+ all_measurements = np.unique(all_measurements)
77
+
78
+ if self.mode=='signals':
79
+ generic_measurements = ['brightfield_channel', 'live_nuclei_channel', 'dead_nuclei_channel',
80
+ 'effector_fluo_channel', 'adhesion_channel', 'fluo_channel_1', 'fluo_channel_2',
81
+ "area", "area_bbox","area_convex","area_filled","major_axis_length",
82
+ "minor_axis_length",
83
+ "eccentricity",
84
+ "equivalent_diameter_area",
85
+ "euler_number",
86
+ "extent",
87
+ "feret_diameter_max",
88
+ "orientation",
89
+ "perimeter",
90
+ "perimeter_crofton",
91
+ "solidity",
92
+ "angular_second_moment",
93
+ "contrast",
94
+ "correlation",
95
+ "sum_of_square_variance",
96
+ "inverse_difference_moment",
97
+ "sum_average",
98
+ "sum_variance",
99
+ "sum_entropy",
100
+ "entropy",
101
+ "difference_variance",
102
+ "difference_entropy",
103
+ "information_measure_of_correlation_1",
104
+ "information_measure_of_correlation_2",
105
+ "maximal_correlation_coefficient",
106
+ "POSITION_X",
107
+ "POSITION_Y",
108
+ ]
109
+ elif self.mode=='channels':
110
+ generic_measurements = ['brightfield_channel', 'live_nuclei_channel', 'dead_nuclei_channel',
111
+ 'effector_fluo_channel', 'adhesion_channel', 'fluo_channel_1', 'fluo_channel_2', 'None']
112
+
113
+ if self.mode=='channels':
114
+ all_measurements = []
115
+ exp_ch = self.attr_parent.exp_channels
116
+ for c in exp_ch:
117
+ all_measurements.append(c)
118
+
119
+ self.channel_items = np.unique(generic_measurements + list(all_measurements))
120
+ self.channel_items = np.insert(self.channel_items, 0, '--')
121
+
122
+ self.add_col_btn = QPushButton('Add channel')
123
+ self.add_col_btn.clicked.connect(self.add_channel)
124
+ self.add_col_btn.setStyleSheet(self.button_add)
125
+ self.add_col_btn.setIcon(icon(MDI6.plus,color="black"))
126
+
127
+ def add_channel(self):
128
+
129
+ self.channel_cbs.append(QSearchableComboBox())
130
+ self.channel_cbs[-1].addItems(self.channel_items)
131
+ self.channel_cbs[-1].currentIndexChanged.connect(self.check_valid_channels)
132
+
133
+ self.normalization_mode_btns.append(QPushButton(''))
134
+ self.normalization_mode.append(True)
135
+ self.normalization_clip_btns.append(QPushButton(''))
136
+ self.clip_option.append(False)
137
+
138
+ self.normalization_mode_btns[-1].setIcon(icon(MDI6.percent_circle,color="#1565c0"))
139
+ self.normalization_mode_btns[-1].setIconSize(QSize(20, 20))
140
+ self.normalization_mode_btns[-1].setStyleSheet(self.button_select_all)
141
+ self.normalization_mode_btns[-1].setToolTip("Switch to absolute normalization values.")
142
+ self.normalization_mode_btns[-1].clicked.connect(partial(self.switch_normalization_mode, len(self.channel_cbs)-1))
143
+
144
+ self.normalization_clip_btns[-1].setIcon(icon(MDI6.content_cut,color="black"))
145
+ self.normalization_clip_btns[-1].setIconSize(QSize(20, 20))
146
+ self.normalization_clip_btns[-1].setStyleSheet(self.button_select_all)
147
+ self.normalization_clip_btns[-1].clicked.connect(partial(self.switch_clipping_mode, len(self.channel_cbs)-1))
148
+ self.normalization_clip_btns[-1].setToolTip('clip')
149
+
150
+ self.normalization_min_value_lbl.append(QLabel('Min %: '))
151
+ self.normalization_min_value_le.append(QLineEdit('0.1'))
152
+ self.normalization_max_value_lbl.append(QLabel('Max %: '))
153
+ self.normalization_max_value_le.append(QLineEdit('99.99'))
154
+
155
+ ch_layout = QHBoxLayout()
156
+ ch_layout.addWidget(QLabel(f'channel {len(self.channel_cbs)-1}: '), 30)
157
+ ch_layout.addWidget(self.channel_cbs[-1], 70)
158
+ self.channels_vb.addLayout(ch_layout)
159
+
160
+ channel_norm_options_layout = QHBoxLayout()
161
+ channel_norm_options_layout.setContentsMargins(130,0,0,0)
162
+ channel_norm_options_layout.addWidget(self.normalization_min_value_lbl[-1])
163
+ channel_norm_options_layout.addWidget(self.normalization_min_value_le[-1])
164
+ channel_norm_options_layout.addWidget(self.normalization_max_value_lbl[-1])
165
+ channel_norm_options_layout.addWidget(self.normalization_max_value_le[-1])
166
+ channel_norm_options_layout.addWidget(self.normalization_clip_btns[-1])
167
+ channel_norm_options_layout.addWidget(self.normalization_mode_btns[-1])
168
+ self.channels_vb.addLayout(channel_norm_options_layout)
169
+
170
+
171
+ def add_to_layout(self):
172
+
173
+ self.channels_vb = QVBoxLayout()
174
+ self.channel_option_layouts = []
175
+ for i in range(len(self.channel_cbs)):
176
+
177
+ ch_layout = QHBoxLayout()
178
+ ch_layout.addWidget(QLabel(f'channel {i}: '), 30)
179
+ self.channel_cbs[i].addItems(self.channel_items)
180
+ self.channel_cbs[i].currentIndexChanged.connect(self.check_valid_channels)
181
+ ch_layout.addWidget(self.channel_cbs[i], 70)
182
+ self.channels_vb.addLayout(ch_layout)
183
+
184
+ channel_norm_options_layout = QHBoxLayout()
185
+ channel_norm_options_layout.setContentsMargins(130,0,0,0)
186
+ channel_norm_options_layout.addWidget(self.normalization_min_value_lbl[i])
187
+ channel_norm_options_layout.addWidget(self.normalization_min_value_le[i])
188
+ channel_norm_options_layout.addWidget(self.normalization_max_value_lbl[i])
189
+ channel_norm_options_layout.addWidget(self.normalization_max_value_le[i])
190
+ channel_norm_options_layout.addWidget(self.normalization_clip_btns[i])
191
+ channel_norm_options_layout.addWidget(self.normalization_mode_btns[i])
192
+ self.channels_vb.addLayout(channel_norm_options_layout)
193
+
194
+ self.addLayout(self.channels_vb)
195
+
196
+ add_hbox = QHBoxLayout()
197
+ add_hbox.addWidget(QLabel(''), 66)
198
+ add_hbox.addWidget(self.add_col_btn, 33, alignment=Qt.AlignRight)
199
+ self.addLayout(add_hbox)
200
+
201
+ def switch_normalization_mode(self, index):
202
+
203
+ """
204
+ Use absolute or percentile values for the normalization of each individual channel.
205
+
206
+ """
207
+
208
+ currentNormMode = self.normalization_mode[index]
209
+ self.normalization_mode[index] = not currentNormMode
210
+
211
+ if self.normalization_mode[index]:
212
+ self.normalization_mode_btns[index].setIcon(icon(MDI6.percent_circle,color="#1565c0"))
213
+ self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
214
+ self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
215
+ self.normalization_mode_btns[index].setToolTip("Switch to absolute normalization values.")
216
+ self.normalization_min_value_lbl[index].setText('Min %: ')
217
+ self.normalization_max_value_lbl[index].setText('Max %: ')
218
+ self.normalization_min_value_le[index].setText('0.1')
219
+ self.normalization_max_value_le[index].setText('99.99')
220
+
221
+ else:
222
+ self.normalization_mode_btns[index].setIcon(icon(MDI6.percent_circle_outline,color="black"))
223
+ self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
224
+ self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
225
+ self.normalization_mode_btns[index].setToolTip("Switch to percentile normalization values.")
226
+ self.normalization_min_value_lbl[index].setText('Min: ')
227
+ self.normalization_min_value_le[index].setText('0')
228
+ self.normalization_max_value_lbl[index].setText('Max: ')
229
+ self.normalization_max_value_le[index].setText('1000')
230
+
231
+ def switch_clipping_mode(self, index):
232
+
233
+ currentClipMode = self.clip_option[index]
234
+ self.clip_option[index] = not currentClipMode
235
+
236
+ if self.clip_option[index]:
237
+ self.normalization_clip_btns[index].setIcon(icon(MDI6.content_cut,color="#1565c0"))
238
+ self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
239
+ self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
240
+
241
+ else:
242
+ self.normalization_clip_btns[index].setIcon(icon(MDI6.content_cut,color="black"))
243
+ self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
244
+ self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
245
+
246
+ def check_valid_channels(self):
247
+
248
+ if np.all([cb.currentText()=='--' for cb in self.channel_cbs]):
249
+ self.parent_window.submit_btn.setEnabled(False)
250
+
251
+ if hasattr(self.parent_window, "spatial_calib_le"):
252
+ if self.parent_window.spatial_calib_le.text()!='--':
253
+ self.parent_window.submit_btn.setEnabled(True)
254
+ else:
255
+ self.parent_window.submit_btn.setEnabled(True)
256
+
257
+
15
258
 
16
259
  class BackgroundFitCorrectionLayout(QGridLayout, Styles):
17
260
 
@@ -702,12 +945,12 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
702
945
  show_progress_per_pos = False,
703
946
  )
704
947
 
705
-
706
948
  self.viewer = StackVisualizer(
707
949
  stack=corrected_stacks[0],
708
950
  window_title='Corrected channel',
709
951
  frame_slider = True,
710
- contrast_slider = True
952
+ contrast_slider = True,
953
+ target_channel=self.channels_cb.currentIndex(),
711
954
  )
712
955
  self.viewer.show()
713
956