celldetective 1.3.5__tar.gz → 1.3.6.post2__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 (114) hide show
  1. {celldetective-1.3.5 → celldetective-1.3.6.post2}/PKG-INFO +2 -1
  2. celldetective-1.3.6.post2/celldetective/_version.py +1 -0
  3. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/filters.py +22 -2
  4. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/btrack_options.py +151 -1
  5. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/classifier_widget.py +26 -22
  6. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/configure_new_exp.py +13 -0
  7. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/control_panel.py +1 -0
  8. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/gui_utils.py +138 -8
  9. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/measurement_options.py +84 -24
  10. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/neighborhood_options.py +1 -1
  11. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/signal_annotator.py +6 -1
  12. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/signal_annotator2.py +22 -19
  13. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/survival_ui.py +0 -2
  14. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/thresholds_gui.py +9 -52
  15. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/viewers.py +58 -21
  16. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/io.py +31 -8
  17. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/measure.py +132 -157
  18. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/measure_cells.py +28 -13
  19. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/segment_cells.py +24 -20
  20. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/segment_cells_thresholds.py +21 -21
  21. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/track_cells.py +55 -17
  22. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/tracking.py +78 -53
  23. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/utils.py +5 -2
  24. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/PKG-INFO +2 -1
  25. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/requires.txt +1 -0
  26. celldetective-1.3.5/celldetective/_version.py +0 -1
  27. {celldetective-1.3.5 → celldetective-1.3.6.post2}/LICENSE +0 -0
  28. {celldetective-1.3.5 → celldetective-1.3.6.post2}/README.md +0 -0
  29. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/__init__.py +0 -0
  30. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/__main__.py +0 -0
  31. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/datasets/segmentation_annotations/blank +0 -0
  32. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/datasets/signal_annotations/blank +0 -0
  33. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/events.py +0 -0
  34. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/extra_properties.py +0 -0
  35. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/InitWindow.py +0 -0
  36. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/__init__.py +0 -0
  37. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/about.py +0 -0
  38. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/analyze_block.py +0 -0
  39. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/generic_signal_plot.py +0 -0
  40. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/DL-segmentation-strategy.json +0 -0
  41. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/Threshold-vs-DL.json +0 -0
  42. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/cell-populations.json +0 -0
  43. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/exp-structure.json +0 -0
  44. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/feature-btrack.json +0 -0
  45. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/neighborhood.json +0 -0
  46. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/prefilter-for-segmentation.json +0 -0
  47. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/preprocessing.json +0 -0
  48. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/propagate-classification.json +0 -0
  49. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/track-postprocessing.json +0 -0
  50. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/help/tracking.json +0 -0
  51. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/json_readers.py +0 -0
  52. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/layouts.py +0 -0
  53. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/plot_measurements.py +0 -0
  54. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/plot_signals_ui.py +0 -0
  55. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/process_block.py +0 -0
  56. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/retrain_segmentation_model_options.py +0 -0
  57. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/retrain_signal_model_options.py +0 -0
  58. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/seg_model_loader.py +0 -0
  59. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/signal_annotator_options.py +0 -0
  60. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/styles.py +0 -0
  61. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/gui/tableUI.py +0 -0
  62. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/logo-large.png +0 -0
  63. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/logo.png +0 -0
  64. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/signals_icon.png +0 -0
  65. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/splash-test.png +0 -0
  66. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/splash.png +0 -0
  67. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/splash0.png +0 -0
  68. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/survival2.png +0 -0
  69. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/vignette_signals2.png +0 -0
  70. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/icons/vignette_signals2.svg +0 -0
  71. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/links/zenodo.json +0 -0
  72. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/pair_signal_detection/blank +0 -0
  73. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_effectors/blank +0 -0
  74. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -0
  75. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  76. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -0
  77. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -0
  78. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  79. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_generic/blank +0 -0
  80. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/segmentation_targets/blank +0 -0
  81. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/signal_detection/blank +0 -0
  82. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/tracking_configs/biased_motion.json +0 -0
  83. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/tracking_configs/mcf7.json +0 -0
  84. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/tracking_configs/no_z_motion.json +0 -0
  85. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/tracking_configs/ricm.json +0 -0
  86. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/models/tracking_configs/ricm2.json +0 -0
  87. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/neighborhood.py +0 -0
  88. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/preprocessing.py +0 -0
  89. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/relative_measurements.py +0 -0
  90. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/analyze_signals.py +0 -0
  91. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/measure_relative.py +0 -0
  92. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/train_segmentation_model.py +0 -0
  93. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/scripts/train_signal_model.py +0 -0
  94. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/segmentation.py +0 -0
  95. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective/signals.py +0 -0
  96. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/SOURCES.txt +0 -0
  97. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/dependency_links.txt +0 -0
  98. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/entry_points.txt +0 -0
  99. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/not-zip-safe +0 -0
  100. {celldetective-1.3.5 → celldetective-1.3.6.post2}/celldetective.egg-info/top_level.txt +0 -0
  101. {celldetective-1.3.5 → celldetective-1.3.6.post2}/setup.cfg +0 -0
  102. {celldetective-1.3.5 → celldetective-1.3.6.post2}/setup.py +0 -0
  103. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/__init__.py +0 -0
  104. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_events.py +0 -0
  105. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_filters.py +0 -0
  106. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_io.py +0 -0
  107. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_measure.py +0 -0
  108. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_neighborhood.py +0 -0
  109. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_preprocessing.py +0 -0
  110. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_qt.py +0 -0
  111. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_segmentation.py +0 -0
  112. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_signals.py +0 -0
  113. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_tracking.py +0 -0
  114. {celldetective-1.3.5 → celldetective-1.3.6.post2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: celldetective
3
- Version: 1.3.5
3
+ Version: 1.3.6.post2
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro
@@ -42,6 +42,7 @@ Requires-Dist: pytest-qt
42
42
  Requires-Dist: h5py
43
43
  Requires-Dist: cliffs_delta
44
44
  Requires-Dist: requests
45
+ Requires-Dist: trackpy
45
46
 
46
47
  # Celldetective
47
48
 
@@ -0,0 +1 @@
1
+ __version__ = "1.3.6.post2"
@@ -88,9 +88,13 @@ def laplace_filter(img, output=float, interpolate=True, *kwargs):
88
88
  img = interpolate_nan(img.astype(float))
89
89
  return snd.laplace(img.astype(float), *kwargs)
90
90
 
91
- def dog_filter(img, sigma_low, sigma_high, interpolate=True, *kwargs):
91
+ def dog_filter(img, blob_size=None, sigma_low=1, sigma_high=2, interpolate=True, *kwargs):
92
+
92
93
  if interpolate:
93
94
  img = interpolate_nan(img.astype(float))
95
+ if blob_size is not None:
96
+ sigma_low = 1.0 / (1.0 + np.sqrt(2)) * blob_size
97
+ sigma_high = np.sqrt(2)*sigma_low
94
98
  return difference_of_gaussians(img.astype(float), sigma_low, sigma_high, *kwargs)
95
99
 
96
100
  def otsu_filter(img, *kwargs):
@@ -115,15 +119,31 @@ def sauvola_filter(img, *kwargs):
115
119
  binary = img >= thresh
116
120
  return binary.astype(float)
117
121
 
118
- def log_filter(img, sigma, *kwargs):
122
+ def log_filter(img, blob_size=None, sigma=1, interpolate=True, *kwargs):
119
123
 
120
124
  if interpolate:
121
125
  img = interpolate_nan(img.astype(float))
126
+ if blob_size is not None:
127
+ sigma_low = 1.0 / (1.0 + np.sqrt(2)) * blob_size
128
+ sigma_high = np.sqrt(2)*sigma_low
129
+
122
130
  return snd.gaussian_laplace(img.astype(float), sigma, *kwargs)
123
131
 
124
132
  def tophat_filter(img, size, connectivity=4, interpolate=True, *kwargs):
133
+
125
134
  if interpolate:
126
135
  img = interpolate_nan(img.astype(float))
127
136
  structure = snd.generate_binary_structure(rank=2, connectivity=connectivity)
128
137
  img = snd.white_tophat(img.astype(float), structure=structure, size=size, *kwargs)
129
138
  return img
139
+
140
+ def invert_filter(img, value=65535, *kwargs):
141
+
142
+ img = img.astype(float)
143
+
144
+ image_fill = np.zeros_like(img)
145
+ image_fill[:,:] = value
146
+
147
+ inverted = np.subtract(image_fill, img, where=img==img)
148
+ return inverted
149
+
@@ -1,5 +1,7 @@
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 QRadioButton, QButtonGroup, QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
2
2
  from PyQt5.QtCore import Qt, QSize
3
+ from PyQt5.QtGui import QDoubleValidator
4
+
3
5
  from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, help_generic
4
6
  from superqt import QLabeledDoubleSlider,QLabeledSlider
5
7
  from superqt.fonticon import icon
@@ -30,6 +32,8 @@ class ConfigTracking(QMainWindow, Styles):
30
32
  self.setWindowTitle("Configure tracking")
31
33
  self.mode = self.parent_window.mode
32
34
  self.exp_dir = self.parent_window.exp_dir
35
+ self.floatValidator = QDoubleValidator()
36
+
33
37
  if self.mode=="targets":
34
38
  self.config_name = os.sep.join(["configs", "btrack_config_targets.json"])
35
39
  self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs","tracking_instructions_targets.json"])
@@ -68,9 +72,25 @@ class ConfigTracking(QMainWindow, Styles):
68
72
  main_layout.setContentsMargins(30,30,30,30)
69
73
 
70
74
  # First collapsable Frame CONFIG
75
+
76
+ self.btrack_option = QRadioButton('bTrack')
77
+ self.btrack_option.setChecked(True)
78
+
79
+ self.trackpy_option = QRadioButton('trackpy')
80
+ self.tracker_option_group = QButtonGroup()
81
+ self.tracker_option_group.addButton(self.btrack_option)
82
+ self.tracker_option_group.addButton(self.trackpy_option)
83
+
71
84
  self.config_frame = QFrame()
72
85
  self.config_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
73
86
  self.populate_config_frame()
87
+
88
+ tracker_hbox = QHBoxLayout()
89
+ tracker_hbox.setContentsMargins(15,15,15,15)
90
+ tracker_hbox.addWidget(self.btrack_option, 50, alignment=Qt.AlignCenter)
91
+ tracker_hbox.addWidget(self.trackpy_option, 50, alignment=Qt.AlignCenter)
92
+ main_layout.addLayout(tracker_hbox)
93
+
74
94
  main_layout.addWidget(self.config_frame)
75
95
 
76
96
  # Second collapsable frame FEATURES
@@ -79,6 +99,12 @@ class ConfigTracking(QMainWindow, Styles):
79
99
  self.populate_features_frame()
80
100
  main_layout.addWidget(self.features_frame)
81
101
 
102
+ self.config_trackpy_frame = QFrame()
103
+ self.config_trackpy_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
104
+ self.populate_config_trackpy_frame()
105
+ main_layout.addWidget(self.config_trackpy_frame)
106
+ self.config_trackpy_frame.hide()
107
+
82
108
  # Third collapsable frame POST-PROCESSING
83
109
  self.post_proc_frame = QFrame()
84
110
  self.post_proc_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
@@ -102,9 +128,27 @@ class ConfigTracking(QMainWindow, Styles):
102
128
  self.setCentralWidget(self.scroll_area)
103
129
  self.show()
104
130
 
131
+ self.btrack_option.toggled.connect(self.show_tracking_options)
132
+ self.trackpy_option.toggled.connect(self.show_tracking_options)
133
+
105
134
  QApplication.processEvents()
106
135
  self.adjustScrollArea()
107
136
 
137
+ def show_tracking_options(self):
138
+
139
+ if self.btrack_option.isChecked():
140
+ self.config_frame.show()
141
+ self.features_frame.show()
142
+ self.config_trackpy_frame.hide()
143
+ #self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
144
+ #self.adjustSize()
145
+ else:
146
+ self.config_frame.hide()
147
+ self.features_frame.hide()
148
+ self.config_trackpy_frame.show()
149
+ #self.scroll_area.setMinimumHeight(self.minimum_height)
150
+ #self.adjustSize()
151
+
108
152
 
109
153
  def populate_post_proc_frame(self):
110
154
 
@@ -539,6 +583,29 @@ class ConfigTracking(QMainWindow, Styles):
539
583
  self.collapse_config_btn.clicked.connect(self.collapse_config_advanced)
540
584
  #self.ContentsConfig.hide()
541
585
 
586
+ def populate_config_trackpy_frame(self):
587
+
588
+ grid = QGridLayout(self.config_trackpy_frame)
589
+ panel_title = QLabel(f"CONFIGURATION")
590
+ panel_title.setStyleSheet("""
591
+ font-weight: bold;
592
+ padding: 0px;
593
+ """)
594
+
595
+ grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
596
+
597
+ self.collapse_config_trackpy_btn = QPushButton()
598
+ self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_down,color="black"))
599
+ self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
600
+ self.collapse_config_trackpy_btn.setStyleSheet(self.button_select_all)
601
+ grid.addWidget(self.collapse_config_trackpy_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
602
+ self.generate_config_trackpy_panel_contents()
603
+ grid.addWidget(self.ContentsConfigTrackpy, 1, 0, 1, 4, alignment=Qt.AlignTop)
604
+ self.collapse_config_trackpy_btn.clicked.connect(lambda: self.ContentsConfigTrackpy.setHidden(not self.ContentsConfigTrackpy.isHidden()))
605
+ self.collapse_config_trackpy_btn.clicked.connect(self.collapse_config_trackpy_advanced)
606
+ #self.ContentsConfig.hide()
607
+
608
+
542
609
  def collapse_config_advanced(self):
543
610
 
544
611
  """
@@ -561,6 +628,56 @@ class ConfigTracking(QMainWindow, Styles):
561
628
  self.collapse_config_btn.setIconSize(QSize(20, 20))
562
629
  self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
563
630
 
631
+ def collapse_config_trackpy_advanced(self):
632
+
633
+ """
634
+ Switch the chevron icon and adjust the size for the CONFIG frame.
635
+ """
636
+
637
+
638
+ post_open = not self.ContentsPostProc.isHidden()
639
+ is_open = np.array([post_open])
640
+
641
+ if self.ContentsConfigTrackpy.isHidden():
642
+ self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_down,color="black"))
643
+ self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
644
+ if len(is_open[is_open])==0:
645
+ self.scroll_area.setMinimumHeight(int(self.minimum_height))
646
+ self.adjustSize()
647
+ else:
648
+ self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_up,color="black"))
649
+ self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
650
+ self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
651
+
652
+
653
+ def generate_config_trackpy_panel_contents(self):
654
+
655
+ self.ContentsConfigTrackpy = QFrame()
656
+ layout = QVBoxLayout(self.ContentsConfigTrackpy)
657
+ layout.setContentsMargins(0,0,0,0)
658
+
659
+ sr_layout = QHBoxLayout()
660
+ self.search_range_lbl = QLabel("search range [px]: ")
661
+ self.search_range_le = QLineEdit('30')
662
+ self.search_range_le.setPlaceholderText('search distance in pixels')
663
+ self.search_range_le.setValidator(self.floatValidator)
664
+ sr_layout.addWidget(self.search_range_lbl, 30)
665
+ sr_layout.addWidget(self.search_range_le, 70)
666
+ layout.addLayout(sr_layout)
667
+
668
+ memory_layout = QHBoxLayout()
669
+ self.memory_lbl = QLabel("memory [# frames]: ")
670
+ self.memory_slider = QLabeledSlider()
671
+ self.memory_slider.setSingleStep(1)
672
+ self.memory_slider.setTickInterval(1)
673
+ self.memory_slider.setSingleStep(1)
674
+ self.memory_slider.setOrientation(1)
675
+ self.memory_slider.setRange(0,self.parent_window.parent_window.len_movie)
676
+ self.memory_slider.setValue(0)
677
+ memory_layout.addWidget(self.memory_lbl, 30)
678
+ memory_layout.addWidget(self.memory_slider, 70)
679
+ layout.addLayout(memory_layout)
680
+
564
681
 
565
682
  def generate_config_panel_contents(self):
566
683
 
@@ -741,7 +858,23 @@ class ConfigTracking(QMainWindow, Styles):
741
858
  """
742
859
 
743
860
  print('Writing instructions...')
861
+
862
+ if self.btrack_option.isChecked():
863
+ btrack_option = True
864
+ else:
865
+ btrack_option = False
866
+
867
+ # Fetch trackpky params
868
+ if not btrack_option:
869
+ search_range = int(self.search_range_le.text().replace(',','.'))
870
+ memory = self.memory_slider.value()
871
+ else:
872
+ search_range = None
873
+ memory = None
874
+
744
875
  tracking_options = {'btrack_config_path': self.config_path}
876
+ tracking_options.update({'btrack_option': btrack_option, 'search_range': search_range, 'memory': memory})
877
+
745
878
  if not self.features_ticked:
746
879
  features = None
747
880
  masked_channels = None
@@ -850,6 +983,23 @@ class ConfigTracking(QMainWindow, Styles):
850
983
  self.ContentsFeatures.hide()
851
984
  self.uncheck_features()
852
985
 
986
+ btrack_option = True
987
+ if 'btrack_option' in tracking_instructions:
988
+ btrack_option = tracking_instructions['btrack_option']
989
+ if btrack_option:
990
+ self.btrack_option.click()
991
+ else:
992
+ self.trackpy_option.click()
993
+
994
+ if 'search_range' in tracking_instructions:
995
+ search_range = tracking_instructions['search_range']
996
+ if search_range is not None:
997
+ self.search_range_le.setText(str(search_range).replace('.',','))
998
+ if 'memory' in tracking_instructions:
999
+ memory = tracking_instructions['memory']
1000
+ if memory is not None:
1001
+ self.memory_slider.setValue(memory)
1002
+
853
1003
  # Uncheck channels that are masked
854
1004
  mask_channels = tracking_instructions['mask_channels']
855
1005
  if (mask_channels is not None) and len(mask_channels)>0:
@@ -95,7 +95,6 @@ class ClassifierWidget(QWidget, Styles):
95
95
  layout.addLayout(slider_alpha_hbox)
96
96
 
97
97
 
98
-
99
98
  self.features_cb = [QSearchableComboBox() for i in range(2)]
100
99
  self.log_btns = [QPushButton() for i in range(2)]
101
100
 
@@ -129,7 +128,6 @@ class ClassifierWidget(QWidget, Styles):
129
128
  layout.addLayout(hbox_classify)
130
129
 
131
130
  self.time_corr = QCheckBox('Time correlated')
132
- self.time_corr.toggled.connect(self.activate_time_corr_options)
133
131
  if "TRACK_ID" in self.df.columns:
134
132
  self.time_corr.setEnabled(True)
135
133
  else:
@@ -150,17 +148,14 @@ class ClassifierWidget(QWidget, Styles):
150
148
 
151
149
  self.irreversible_event_btn = QRadioButton('irreversible event')
152
150
  self.unique_state_btn = QRadioButton('unique state')
151
+ self.transient_event_btn = QRadioButton('transient event')
153
152
  time_corr_btn_group = QButtonGroup()
154
153
  self.unique_state_btn.click()
155
- self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
156
-
157
- for btn in self.time_corr_options:
158
- time_corr_btn_group.addButton(btn)
159
- btn.setEnabled(False)
160
154
 
161
155
  time_corr_layout = QHBoxLayout()
162
- time_corr_layout.addWidget(self.unique_state_btn, 50, alignment=Qt.AlignCenter)
163
- time_corr_layout.addWidget(self.irreversible_event_btn, 50,alignment=Qt.AlignCenter)
156
+ time_corr_layout.addWidget(self.unique_state_btn, 33, alignment=Qt.AlignCenter)
157
+ time_corr_layout.addWidget(self.irreversible_event_btn, 33,alignment=Qt.AlignCenter)
158
+ time_corr_layout.addWidget(self.transient_event_btn, 33,alignment=Qt.AlignCenter)
164
159
  layout.addLayout(time_corr_layout)
165
160
 
166
161
  self.prereq_event_check = QCheckBox('prerequisite event:')
@@ -171,12 +166,6 @@ class ClassifierWidget(QWidget, Styles):
171
166
  self.prereq_event_check.setEnabled(False)
172
167
  self.prereq_event_cb.setEnabled(False)
173
168
 
174
- prereq_layout = QHBoxLayout()
175
- prereq_layout.addWidget(QLabel(''), 50)
176
- prereq_layout.addWidget(self.prereq_event_check, 15)
177
- prereq_layout.addWidget(self.prereq_event_cb, 35)
178
- layout.addLayout(prereq_layout)
179
-
180
169
  self.r2_slider = QLabeledDoubleSlider()
181
170
  self.r2_slider.setValue(0.75)
182
171
  self.r2_slider.setRange(0,1)
@@ -186,17 +175,32 @@ class ClassifierWidget(QWidget, Styles):
186
175
  self.r2_label.setToolTip('Minimum R2 between the fit sigmoid and the binary response to the filters to accept the event.')
187
176
 
188
177
  r2_threshold_layout = QHBoxLayout()
189
- r2_threshold_layout.addWidget(QLabel(''), 50)
190
- r2_threshold_layout.addWidget(self.r2_label, 15)
191
- r2_threshold_layout.addWidget(self.r2_slider, 35)
192
- layout.addLayout(r2_threshold_layout)
178
+ r2_threshold_layout.addWidget(QLabel(''), 33)
179
+ r2_threshold_layout.addWidget(self.r2_label, 13)
180
+ r2_threshold_layout.addWidget(self.r2_slider, 20)
181
+ r2_threshold_layout.addWidget(QLabel(''), 33)
182
+
183
+ layout.addLayout(r2_threshold_layout)
193
184
 
185
+ self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn, self.prereq_event_check, self.prereq_event_cb, self.transient_event_btn]
186
+ for btn in [self.irreversible_event_btn, self.unique_state_btn, self.transient_event_btn]:
187
+ time_corr_btn_group.addButton(btn)
188
+ btn.setEnabled(False)
189
+ self.time_corr.toggled.connect(self.activate_time_corr_options)
190
+
194
191
  self.irreversible_event_btn.clicked.connect(self.activate_r2)
195
192
  self.unique_state_btn.clicked.connect(self.activate_r2)
193
+ self.transient_event_btn.clicked.connect(self.activate_r2)
196
194
 
197
195
  for wg in [self.r2_slider, self.r2_label]:
198
196
  wg.setEnabled(False)
199
197
 
198
+ prereq_layout = QHBoxLayout()
199
+ prereq_layout.setContentsMargins(30,0,0,0)
200
+ prereq_layout.addWidget(self.prereq_event_check, 20)
201
+ prereq_layout.addWidget(self.prereq_event_cb, 80)
202
+ layout.addLayout(prereq_layout)
203
+
200
204
  layout.addWidget(QLabel())
201
205
 
202
206
  self.submit_btn = QPushButton('apply')
@@ -225,10 +229,10 @@ class ClassifierWidget(QWidget, Styles):
225
229
 
226
230
  def activate_r2(self):
227
231
  if self.irreversible_event_btn.isChecked() and self.time_corr.isChecked():
228
- for wg in [self.r2_slider, self.r2_label, self.prereq_event_check]:
232
+ for wg in [self.r2_slider, self.r2_label]:
229
233
  wg.setEnabled(True)
230
234
  else:
231
- for wg in [self.r2_slider, self.r2_label, self.prereq_event_check]:
235
+ for wg in [self.r2_slider, self.r2_label]:
232
236
  wg.setEnabled(False)
233
237
 
234
238
  def activate_time_corr_options(self):
@@ -441,7 +445,7 @@ class ClassifierWidget(QWidget, Styles):
441
445
  if self.prereq_event_check.isChecked() and "t_"+self.prereq_event_cb.currentText() in self.cols:
442
446
  pre_event = self.prereq_event_cb.currentText()
443
447
 
444
- self.df = interpret_track_classification(self.df, self.class_name_user, irreversible_event=self.irreversible_event_btn.isChecked(), unique_state=self.unique_state_btn.isChecked(), r2_threshold=self.r2_slider.value(), pre_event=pre_event)
448
+ self.df = interpret_track_classification(self.df, self.class_name_user, irreversible_event=self.irreversible_event_btn.isChecked(), unique_state=self.unique_state_btn.isChecked(), transient_event=self.transient_event_btn.isChecked(),r2_threshold=self.r2_slider.value(), pre_event=pre_event)
445
449
 
446
450
  else:
447
451
  self.group_name_user = 'group_' + self.name_le.text()
@@ -453,6 +453,9 @@ class ConfigNewExperiment(QMainWindow, Styles):
453
453
  config.set('Labels', 'concentrations', self.concentrations)
454
454
  config.set('Labels', 'pharmaceutical_agents', self.pharmaceutical_agents)
455
455
 
456
+ config.add_section('Metadata')
457
+ config.set('Metadata', 'concentration_units', self.concentration_units)
458
+
456
459
  # save to a file
457
460
  with open('config.ini', 'w') as configfile:
458
461
  config.write(configfile)
@@ -480,6 +483,8 @@ class SetupConditionLabels(QWidget, Styles):
480
483
  self.antibodies_cbs = [QLineEdit() for i in range(self.n_wells)]
481
484
  self.concentrations_cbs = [QLineEdit() for i in range(self.n_wells)]
482
485
  self.pharmaceutical_agents_cbs = [QLineEdit() for i in range(self.n_wells)]
486
+ self.concentration_units_le = QLineEdit('pM')
487
+ self.concentration_units_le.setPlaceholderText('concentration units')
483
488
 
484
489
  for i in range(self.n_wells):
485
490
  hbox = QHBoxLayout()
@@ -504,6 +509,12 @@ class SetupConditionLabels(QWidget, Styles):
504
509
 
505
510
  self.layout.addLayout(hbox)
506
511
 
512
+ concentration_units_layout = QHBoxLayout()
513
+ concentration_units_layout.addWidget(QLabel('concentration\nunits: '),5,alignment=Qt.AlignLeft)
514
+ concentration_units_layout.addWidget(self.concentration_units_le,10)
515
+ concentration_units_layout.addWidget(QLabel(''), 85)
516
+ self.layout.addLayout(concentration_units_layout)
517
+
507
518
  btn_hbox = QHBoxLayout()
508
519
  btn_hbox.setContentsMargins(0,20,0,0)
509
520
  self.skip_btn = QPushButton('Skip')
@@ -556,6 +567,8 @@ class SetupConditionLabels(QWidget, Styles):
556
567
  pharamaceutical_text = [c.text() for c in self.pharmaceutical_agents_cbs]
557
568
  self.parent_window.pharmaceutical_agents = ','.join(pharamaceutical_text)
558
569
 
570
+ self.parent_window.concentration_units = self.concentration_units_le.text()
571
+
559
572
 
560
573
 
561
574
 
@@ -338,6 +338,7 @@ class ControlPanel(QMainWindow, Styles):
338
338
  self.antibodies = get_experiment_antibodies(self.exp_dir)
339
339
  self.pharmaceutical_agents = get_experiment_pharmaceutical_agents(self.exp_dir)
340
340
 
341
+ self.metadata = ConfigSectionMap(self.exp_config,"Metadata")
341
342
  print('Experiment configuration successfully read...')
342
343
 
343
344
  def closeEvent(self, event):
@@ -1,5 +1,5 @@
1
1
  import numpy as np
2
- from PyQt5.QtWidgets import QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
2
+ from PyQt5.QtWidgets import QGridLayout, QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
3
3
  QPushButton, QLabel, QHBoxLayout, QCheckBox, QFileDialog, QToolButton, QMenu, QStylePainter, QStyleOptionComboBox, QStyle
4
4
  from PyQt5.QtCore import Qt, QSize, QAbstractTableModel, QEvent, pyqtSignal
5
5
  from PyQt5.QtGui import QDoubleValidator, QIntValidator, QStandardItemModel, QPalette
@@ -15,6 +15,125 @@ import celldetective.extra_properties as extra_properties
15
15
  from inspect import getmembers, isfunction
16
16
  from celldetective.filters import *
17
17
  from os import sep
18
+ import json
19
+
20
+
21
+ class PreprocessingLayout(QVBoxLayout, Styles):
22
+
23
+ """
24
+ A widget that allows user to choose preprocessing filters for an image
25
+ """
26
+
27
+ def __init__(self, parent_window=None, apply_btn_option=True, *args, **kwargs):
28
+ super().__init__(*args, **kwargs)
29
+
30
+ self.parent_window = parent_window
31
+ self.apply_btn_option = apply_btn_option
32
+ self.generate_components()
33
+ self.add_to_layout()
34
+
35
+ def add_to_layout(self):
36
+
37
+ self.setContentsMargins(20, 20, 20, 20)
38
+
39
+ button_layout = QHBoxLayout()
40
+ button_layout.addWidget(self.preprocess_lbl, 85, alignment=Qt.AlignLeft)
41
+ button_layout.addWidget(self.delete_filter_btn, 5)
42
+ button_layout.addWidget(self.add_filter_btn, 5)
43
+ button_layout.addWidget(self.help_prefilter_btn, 5)
44
+ self.addLayout(button_layout, 25)
45
+
46
+ self.addWidget(self.list, 70)
47
+ if self.apply_btn_option:
48
+ self.addWidget(self.apply_btn, 5)
49
+
50
+ def generate_components(self):
51
+
52
+ self.list = ListWidget(FilterChoice, [])
53
+
54
+ self.preprocess_lbl = QLabel('Preprocessing')
55
+ self.preprocess_lbl.setStyleSheet("font-weight: bold;")
56
+
57
+ self.delete_filter_btn = QPushButton()
58
+ self.delete_filter_btn.setStyleSheet(self.button_select_all)
59
+ self.delete_filter_btn.setIcon(icon(MDI6.trash_can, color="black"))
60
+ self.delete_filter_btn.setToolTip("Remove filter")
61
+ self.delete_filter_btn.setIconSize(QSize(20, 20))
62
+ self.delete_filter_btn.clicked.connect(self.list.removeSel)
63
+
64
+ self.add_filter_btn = QPushButton()
65
+ self.add_filter_btn.setStyleSheet(self.button_select_all)
66
+ self.add_filter_btn.setIcon(icon(MDI6.filter_plus, color="black"))
67
+ self.add_filter_btn.setToolTip("Add filter")
68
+ self.add_filter_btn.setIconSize(QSize(20, 20))
69
+ self.add_filter_btn.clicked.connect(self.list.addItem)
70
+
71
+ self.help_prefilter_btn = QPushButton()
72
+ self.help_prefilter_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
73
+ self.help_prefilter_btn.setIconSize(QSize(20, 20))
74
+ self.help_prefilter_btn.clicked.connect(self.help_prefilter)
75
+ self.help_prefilter_btn.setStyleSheet(self.button_select_all)
76
+ self.help_prefilter_btn.setToolTip("Help.")
77
+
78
+ if self.apply_btn_option:
79
+ self.apply_btn = QPushButton("Apply")
80
+ self.apply_btn.setIcon(icon(MDI6.filter_cog_outline, color="white"))
81
+ self.apply_btn.setIconSize(QSize(20, 20))
82
+ self.apply_btn.setStyleSheet(self.button_style_sheet)
83
+ self.apply_btn.clicked.connect(self.parent_window.preprocess_image)
84
+
85
+ def help_prefilter(self):
86
+
87
+ """
88
+ Helper for prefiltering strategy
89
+ """
90
+
91
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','prefilter-for-segmentation.json'])
92
+
93
+ with open(dict_path) as f:
94
+ d = json.load(f)
95
+
96
+ suggestion = help_generic(d)
97
+ if isinstance(suggestion, str):
98
+ print(f"{suggestion=}")
99
+ msgBox = QMessageBox()
100
+ msgBox.setIcon(QMessageBox.Information)
101
+ msgBox.setTextFormat(Qt.RichText)
102
+ msgBox.setText(f"The suggested technique is to {suggestion}.\nSee a tutorial <a href='https://celldetective.readthedocs.io/en/latest/segment.html'>here</a>.")
103
+ msgBox.setWindowTitle("Info")
104
+ msgBox.setStandardButtons(QMessageBox.Ok)
105
+ returnValue = msgBox.exec()
106
+ if returnValue == QMessageBox.Ok:
107
+ return None
108
+
109
+
110
+ class PreprocessingLayout2(PreprocessingLayout):
111
+
112
+ def __init__(self, fraction=75, *args, **kwargs):
113
+
114
+ self.fraction = fraction
115
+ super().__init__(apply_btn_option=False, *args, **kwargs)
116
+ self.preprocess_lbl.setText('Preprocessing: ')
117
+ self.preprocess_lbl.setStyleSheet("")
118
+ self.setContentsMargins(0,0,0,0)
119
+
120
+ def add_to_layout(self):
121
+
122
+ main_layout = QHBoxLayout()
123
+ main_layout.setContentsMargins(0,0,0,0)
124
+ main_layout.setSpacing(5)
125
+ main_layout.addWidget(self.preprocess_lbl, self.fraction, alignment=Qt.AlignTop)
126
+
127
+ list_grid = QGridLayout()
128
+ list_grid.addWidget(self.list, 0, 0, 2, 2)
129
+ list_grid.addWidget(self.add_filter_btn, 0, 2, 1, 1)
130
+ list_grid.addWidget(self.delete_filter_btn, 1, 2, 1, 1)
131
+ main_layout.addLayout(list_grid, 100-self.fraction)
132
+ self.add_filter_btn.setFixedWidth(35) # Ensure the button width is fixed
133
+ self.delete_filter_btn.setFixedWidth(35)
134
+ list_grid.setColumnStretch(2, 0)
135
+
136
+ self.addLayout(main_layout)
18
137
 
19
138
 
20
139
  class QCheckableComboBox(QComboBox):
@@ -427,7 +546,7 @@ class QHSeperationLine(QFrame):
427
546
  self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
428
547
 
429
548
 
430
- class FeatureChoice(QWidget):
549
+ class FeatureChoice(QWidget, Styles):
431
550
 
432
551
  def __init__(self, parent_window):
433
552
  super().__init__()
@@ -435,7 +554,6 @@ class FeatureChoice(QWidget):
435
554
  self.setWindowTitle("Add feature")
436
555
  # Create the QComboBox and add some items
437
556
  self.combo_box = QComboBox(self)
438
- center_window(self)
439
557
 
440
558
  standard_measurements = ["area",
441
559
  "area_bbox",
@@ -465,12 +583,15 @@ class FeatureChoice(QWidget):
465
583
  self.combo_box.addItems(standard_measurements)
466
584
 
467
585
  self.add_btn = QPushButton("Add")
586
+ self.add_btn.setStyleSheet(self.button_style_sheet)
468
587
  self.add_btn.clicked.connect(self.add_current_feature)
469
588
 
470
589
  # Create the layout
471
590
  layout = QVBoxLayout(self)
472
591
  layout.addWidget(self.combo_box)
473
592
  layout.addWidget(self.add_btn)
593
+ center_window(self)
594
+
474
595
 
475
596
  def add_current_feature(self):
476
597
  filtername = self.combo_box.currentText()
@@ -478,7 +599,7 @@ class FeatureChoice(QWidget):
478
599
  self.close()
479
600
 
480
601
 
481
- class FilterChoice(QWidget):
602
+ class FilterChoice(QWidget, Styles):
482
603
 
483
604
  def __init__(self, parent_window):
484
605
 
@@ -499,9 +620,10 @@ class FilterChoice(QWidget):
499
620
  'laplace_filter': None,
500
621
  'abs_filter': None,
501
622
  'ln_filter': None,
623
+ 'invert_filter': {'value': 65535},
502
624
  'subtract_filter': {'value': 1},
503
- 'dog_filter': {'sigma_low': 0.8, 'sigma_high': 1.6},
504
- 'log_filter': {'sigma': 2},
625
+ 'dog_filter': {'blob_size': 30},
626
+ 'log_filter': {'blob_size': 30},
505
627
  'tophat_filter': {'size': 4, 'connectivity': 4},
506
628
  'otsu_filter': None,
507
629
  'local_filter': {'block_size': 73, 'method': 'mean', 'offset': 0},
@@ -523,16 +645,18 @@ class FilterChoice(QWidget):
523
645
  self.arguments_labels = [QLabel('') for i in range(3)]
524
646
  for i in range(2):
525
647
  hbox = QHBoxLayout()
526
- hbox.addWidget(self.arguments_labels[i], 20)
527
- hbox.addWidget(self.arguments_le[i], 80)
648
+ hbox.addWidget(self.arguments_labels[i], 40)
649
+ hbox.addWidget(self.arguments_le[i], 60)
528
650
  layout.addLayout(hbox)
529
651
 
530
652
  self.add_btn = QPushButton("Add")
653
+ self.add_btn.setStyleSheet(self.button_style_sheet)
531
654
  self.add_btn.clicked.connect(self.add_current_feature)
532
655
  layout.addWidget(self.add_btn)
533
656
 
534
657
  self.combo_box.setCurrentIndex(0)
535
658
  self.update_arguments()
659
+ center_window(self)
536
660
 
537
661
  def add_current_feature(self):
538
662
 
@@ -787,6 +911,9 @@ class ListWidget(QWidget):
787
911
  self.addItemWindow = self.choiceWidget(self)
788
912
  self.addItemWindow.show()
789
913
 
914
+ def addItemToList(self, item):
915
+ self.list_widget.addItems([item])
916
+
790
917
  def getItems(self):
791
918
 
792
919
  """
@@ -814,6 +941,9 @@ class ListWidget(QWidget):
814
941
  items.append(self.dtype(self.list_widget.item(x).text()))
815
942
  return items
816
943
 
944
+ def clear(self):
945
+ self.items = []
946
+ self.list_widget.clear()
817
947
 
818
948
  def removeSel(self):
819
949