celldetective 1.1.1.post1__tar.gz → 1.1.1.post4__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.post1 → celldetective-1.1.1.post4}/PKG-INFO +1 -1
  2. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/__main__.py +17 -0
  3. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/classifier_widget.py +10 -3
  4. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/control_panel.py +11 -4
  5. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/layouts.py +253 -8
  6. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/neighborhood_options.py +11 -5
  7. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/retrain_segmentation_model_options.py +66 -164
  8. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/retrain_signal_model_options.py +18 -164
  9. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/signal_annotator.py +85 -25
  10. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/tableUI.py +174 -65
  11. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/viewers.py +1 -1
  12. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/io.py +69 -3
  13. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/neighborhood.py +96 -26
  14. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/preprocessing.py +95 -63
  15. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/segment_cells.py +1 -0
  16. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/train_segmentation_model.py +11 -22
  17. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/segmentation.py +67 -29
  18. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/utils.py +54 -14
  19. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/PKG-INFO +1 -1
  20. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/setup.py +1 -1
  21. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_segmentation.py +1 -1
  22. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/LICENSE +0 -0
  23. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/README.md +0 -0
  24. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/__init__.py +0 -0
  25. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/datasets/segmentation_annotations/blank +0 -0
  26. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/datasets/signal_annotations/blank +0 -0
  27. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/events.py +0 -0
  28. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/extra_properties.py +0 -0
  29. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/filters.py +0 -0
  30. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/__init__.py +0 -0
  31. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/about.py +0 -0
  32. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/analyze_block.py +0 -0
  33. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/btrack_options.py +0 -0
  34. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/configure_new_exp.py +0 -0
  35. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/gui_utils.py +0 -0
  36. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/json_readers.py +0 -0
  37. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/measurement_options.py +0 -0
  38. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/plot_measurements.py +0 -0
  39. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/plot_signals_ui.py +0 -0
  40. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/process_block.py +0 -0
  41. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/seg_model_loader.py +0 -0
  42. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/signal_annotator_options.py +0 -0
  43. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/styles.py +0 -0
  44. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/survival_ui.py +0 -0
  45. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/gui/thresholds_gui.py +0 -0
  46. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/logo-large.png +0 -0
  47. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/logo.png +0 -0
  48. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/signals_icon.png +0 -0
  49. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/splash-test.png +0 -0
  50. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/splash.png +0 -0
  51. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/splash0.png +0 -0
  52. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/survival2.png +0 -0
  53. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/vignette_signals2.png +0 -0
  54. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/icons/vignette_signals2.svg +0 -0
  55. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/links/zenodo.json +0 -0
  56. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/measure.py +0 -0
  57. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/blank +0 -0
  58. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -0
  59. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  60. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -0
  61. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/segmentation_generic/blank +0 -0
  62. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/segmentation_targets/blank +0 -0
  63. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/signal_detection/blank +0 -0
  64. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/tracking_configs/mcf7.json +0 -0
  65. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/tracking_configs/ricm.json +0 -0
  66. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/models/tracking_configs/ricm2.json +0 -0
  67. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/analyze_signals.py +0 -0
  68. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/measure_cells.py +0 -0
  69. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  70. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/track_cells.py +0 -0
  71. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/scripts/train_signal_model.py +0 -0
  72. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/signals.py +0 -0
  73. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective/tracking.py +0 -0
  74. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/SOURCES.txt +0 -0
  75. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/dependency_links.txt +0 -0
  76. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/entry_points.txt +0 -0
  77. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/not-zip-safe +0 -0
  78. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/requires.txt +0 -0
  79. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/celldetective.egg-info/top_level.txt +0 -0
  80. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/setup.cfg +0 -0
  81. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/__init__.py +0 -0
  82. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_events.py +0 -0
  83. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_filters.py +0 -0
  84. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_io.py +0 -0
  85. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_measure.py +0 -0
  86. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_neighborhood.py +0 -0
  87. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_preprocessing.py +0 -0
  88. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_signals.py +0 -0
  89. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/tests/test_tracking.py +0 -0
  90. {celldetective-1.1.1.post1 → celldetective-1.1.1.post4}/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.post1
3
+ Version: 1.1.1.post4
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro
@@ -108,6 +108,10 @@ class AppInitWindow(QMainWindow):
108
108
  OptionsMenu.addAction(self.MemoryAndThreadsAction)
109
109
  menuBar.addMenu(OptionsMenu)
110
110
 
111
+ PluginsMenu = QMenu("Plugins", self)
112
+ PluginsMenu.addAction(self.CorrectAnnotationAction)
113
+ menuBar.addMenu(PluginsMenu)
114
+
111
115
  helpMenu = QMenu("Help", self)
112
116
  helpMenu.clear()
113
117
  helpMenu.addAction(self.DocumentationAction)
@@ -130,6 +134,8 @@ class AppInitWindow(QMainWindow):
130
134
 
131
135
  self.MemoryAndThreadsAction = QAction('Memory & Threads...')
132
136
 
137
+ self.CorrectAnnotationAction = QAction('Correct a segmentation annotation...')
138
+
133
139
  self.newExpAction = QAction('New', self)
134
140
  self.newExpAction.setShortcut("Ctrl+N")
135
141
  self.newExpAction.setShortcutVisibleInContextMenu(True)
@@ -156,6 +162,7 @@ class AppInitWindow(QMainWindow):
156
162
  self.openModels.triggered.connect(self.open_models_folder)
157
163
  self.AboutAction.triggered.connect(self.open_about_window)
158
164
  self.MemoryAndThreadsAction.triggered.connect(self.set_memory_and_threads)
165
+ self.CorrectAnnotationAction.triggered.connect(self.correct_seg_annotation)
159
166
 
160
167
  self.DocumentationAction.triggered.connect(self.open_documentation)
161
168
 
@@ -186,6 +193,15 @@ class AppInitWindow(QMainWindow):
186
193
  for r in self.recentFileActs:
187
194
  r.triggered.connect(lambda checked, item=r: self.load_recent_exp(item.text()))
188
195
 
196
+ def correct_seg_annotation(self):
197
+
198
+ self.filename,_ = QFileDialog.getOpenFileName(self,"Open Image", "/home/", "TIF Files (*.tif)")
199
+ if self.filename!='':
200
+ print('Opening ',self.filename,' in napari...')
201
+ correct_annotation(self.filename)
202
+ else:
203
+ return None
204
+
189
205
  def set_memory_and_threads(self):
190
206
 
191
207
  print('setting memory and threads')
@@ -405,6 +421,7 @@ if __name__ == "__main__":
405
421
  import subprocess
406
422
  import os
407
423
  from celldetective.gui.about import AboutWidget
424
+ from celldetective.io import correct_annotation
408
425
  import psutil
409
426
  import subprocess
410
427
  import json
@@ -12,6 +12,7 @@ from sklearn.metrics import r2_score
12
12
  from scipy.optimize import curve_fit
13
13
  from celldetective.gui import Styles
14
14
  from math import ceil
15
+ from celldetective.utils import extract_cols_from_query
15
16
 
16
17
  def step_function(t, t_shift, dt):
17
18
  return 1/(1+np.exp(-(t-t_shift)/dt))
@@ -263,17 +264,23 @@ class ClassifierWidget(QWidget, Styles):
263
264
  self.propscanvas.canvas.draw_idle()
264
265
 
265
266
  def apply_property_query(self):
267
+
266
268
  query = self.property_query_le.text()
267
269
  self.df[self.class_name] = 1
268
270
 
269
- print(query, self.class_name)
271
+ cols = extract_cols_from_query(query)
272
+ print(cols)
273
+ cols_in_df = np.all([c in list(self.df.columns) for c in cols], axis=0)
274
+ print(f'Testing if columns from query are in the dataframe: {cols_in_df}...')
270
275
 
271
276
  if query=='':
272
277
  print('empty query')
273
278
  else:
274
279
  try:
275
- self.selection = self.df.query(query).index
276
- print(self.selection)
280
+ if cols_in_df:
281
+ self.selection = self.df.dropna(subset=cols).query(query).index
282
+ else:
283
+ self.selection = self.df.query(query).index
277
284
  self.df.loc[self.selection, self.class_name] = 0
278
285
  except Exception as e:
279
286
  print(e)
@@ -16,6 +16,7 @@ import subprocess
16
16
  from celldetective.gui.viewers import StackVisualizer
17
17
  from celldetective.utils import extract_experiment_channels
18
18
  from celldetective.gui import Styles
19
+ import pandas as pd
19
20
 
20
21
  class ControlPanel(QMainWindow, Styles):
21
22
 
@@ -452,26 +453,32 @@ class ControlPanel(QMainWindow, Styles):
452
453
  self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
453
454
 
454
455
  if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv'])):
456
+ cols = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv']), nrows=1).columns.tolist()
455
457
  self.ProcessEffectors.check_measurements_btn.setEnabled(True)
456
- self.ProcessEffectors.check_signals_btn.setEnabled(True)
458
+ if 'TRACK_ID' in cols:
459
+ self.ProcessEffectors.check_signals_btn.setEnabled(True)
457
460
  #self.ProcessEffectors.signal_analysis_action.setEnabled(True)
458
461
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
459
-
462
+ self.ProcessEffectors.classify_btn.setEnabled(True)
460
463
  else:
461
464
  self.ProcessEffectors.check_measurements_btn.setEnabled(False)
462
465
  self.ProcessEffectors.check_signals_btn.setEnabled(False)
463
466
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
464
467
  self.ProcessEffectors.view_tab_btn.setEnabled(False)
468
+ self.ProcessEffectors.classify_btn.setEnabled(False)
465
469
 
466
470
  if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_targets.csv'])):
471
+ cols = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_targets.csv']), nrows=1).columns.tolist()
467
472
  self.ProcessTargets.check_measurements_btn.setEnabled(True)
468
- self.ProcessTargets.check_signals_btn.setEnabled(True)
473
+ if 'TRACK_ID' in cols:
474
+ self.ProcessTargets.check_signals_btn.setEnabled(True)
469
475
  #self.ProcessTargets.signal_analysis_action.setEnabled(True)
470
476
  self.ProcessTargets.view_tab_btn.setEnabled(True)
471
-
477
+ self.ProcessTargets.classify_btn.setEnabled(True)
472
478
  else:
473
479
  self.ProcessTargets.check_measurements_btn.setEnabled(False)
474
480
  self.ProcessTargets.check_signals_btn.setEnabled(False)
475
481
  #self.ProcessTargets.signal_analysis_action.setEnabled(False)
476
482
  self.ProcessTargets.view_tab_btn.setEnabled(False)
483
+ self.ProcessTargets.classify_btn.setEnabled(False)
477
484
 
@@ -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
 
@@ -747,10 +990,12 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
747
990
  )
748
991
  bg = bg[0]
749
992
  bg = bg['bg']
750
-
751
- self.viewer = StackVisualizer(
752
- stack=[bg],
753
- window_title='Reconstructed background',
754
- frame_slider = False,
755
- )
756
- self.viewer.show()
993
+ print(bg)
994
+ if len(bg)>0:
995
+
996
+ self.viewer = StackVisualizer(
997
+ stack=[bg],
998
+ window_title='Reconstructed background',
999
+ frame_slider = False,
1000
+ )
1001
+ self.viewer.show()
@@ -319,16 +319,16 @@ class ConfigNeighborhoods(QWidget, Styles):
319
319
  def fill_cbs_of_neighbor_population(self):
320
320
 
321
321
  population = self.neighbor_population_cb.currentText()
322
- class_cols, status_cols, time_cols = self.locate_population_specific_columns(population)
322
+ class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
323
323
  self.neighbor_population_status_cb.clear()
324
- self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
324
+ self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
325
325
 
326
326
  def fill_cbs_of_reference_population(self):
327
327
 
328
328
  population = self.reference_population_cb.currentText()
329
- class_cols, status_cols, time_cols = self.locate_population_specific_columns(population)
329
+ class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
330
330
  self.reference_population_status_cb.clear()
331
- self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
331
+ self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
332
332
  self.event_time_cb.addItems(['--', 't0']+time_cols)
333
333
 
334
334
  def switch_not_reference(self):
@@ -364,6 +364,7 @@ class ConfigNeighborhoods(QWidget, Styles):
364
364
 
365
365
  class_idx = np.array([s.startswith('class_') for s in self.all_columns])
366
366
  status_idx = np.array([s.startswith('status_') for s in self.all_columns])
367
+ group_idx = np.array([s.startswith('group_') for s in self.all_columns])
367
368
  time_idx = np.array([s.startswith('t_') for s in self.all_columns])
368
369
 
369
370
  if len(class_idx)>0:
@@ -379,12 +380,17 @@ class ConfigNeighborhoods(QWidget, Styles):
379
380
  else:
380
381
  status_columns = []
381
382
 
383
+ if len(group_idx)>0:
384
+ group_columns = list(self.all_columns[group_idx])
385
+ else:
386
+ group_columns = []
387
+
382
388
  if len(time_idx)>0:
383
389
  time_columns = list(self.all_columns[time_idx])
384
390
  else:
385
391
  time_columns = []
386
392
 
387
- return class_columns, status_columns, time_columns
393
+ return class_columns, status_columns, group_columns, time_columns
388
394
 
389
395
  def write_instructions(self):
390
396