celldetective 1.3.0.post1__py3-none-any.whl → 1.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +88 -11
  3. celldetective/extra_properties.py +5 -1
  4. celldetective/gui/InitWindow.py +35 -9
  5. celldetective/gui/classifier_widget.py +99 -23
  6. celldetective/gui/control_panel.py +7 -1
  7. celldetective/gui/generic_signal_plot.py +161 -2
  8. celldetective/gui/gui_utils.py +90 -1
  9. celldetective/gui/layouts.py +128 -7
  10. celldetective/gui/measurement_options.py +3 -3
  11. celldetective/gui/plot_signals_ui.py +8 -3
  12. celldetective/gui/process_block.py +77 -32
  13. celldetective/gui/retrain_segmentation_model_options.py +24 -10
  14. celldetective/gui/signal_annotator.py +53 -26
  15. celldetective/gui/signal_annotator2.py +17 -30
  16. celldetective/gui/survival_ui.py +24 -3
  17. celldetective/gui/tableUI.py +300 -183
  18. celldetective/gui/viewers.py +263 -3
  19. celldetective/io.py +56 -3
  20. celldetective/links/zenodo.json +136 -123
  21. celldetective/measure.py +3 -0
  22. celldetective/models/tracking_configs/biased_motion.json +68 -0
  23. celldetective/models/tracking_configs/no_z_motion.json +202 -0
  24. celldetective/neighborhood.py +154 -69
  25. celldetective/preprocessing.py +172 -3
  26. celldetective/relative_measurements.py +128 -4
  27. celldetective/scripts/measure_cells.py +3 -3
  28. celldetective/signals.py +212 -215
  29. celldetective/tracking.py +7 -3
  30. celldetective/utils.py +22 -6
  31. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/METADATA +3 -3
  32. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/RECORD +36 -34
  33. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/WHEEL +1 -1
  34. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/LICENSE +0 -0
  35. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/entry_points.txt +0 -0
  36. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/top_level.txt +0 -0
@@ -30,10 +30,10 @@ import json
30
30
  import psutil
31
31
  from celldetective.neighborhood import compute_neighborhood_at_position, compute_contact_neighborhood_at_position
32
32
  from celldetective.gui.gui_utils import FigureCanvas
33
- from celldetective.preprocessing import correct_background_model_free, correct_background_model
33
+ from celldetective.preprocessing import correct_background_model_free, correct_background_model, correct_channel_offset
34
34
  from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
35
35
  from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout, help_generic
36
- from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout
36
+ from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, ChannelOffsetOptionsLayout
37
37
  from celldetective.gui import Styles
38
38
  from celldetective.utils import get_software_location
39
39
 
@@ -166,10 +166,10 @@ class ProcessPanel(QFrame, Styles):
166
166
  self.generate_signal_analysis_options()
167
167
 
168
168
  self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
169
- self.view_tab_btn = QPushButton("View table")
169
+ self.view_tab_btn = QPushButton("Explore table")
170
170
  self.view_tab_btn.setStyleSheet(self.button_style_sheet_2)
171
171
  self.view_tab_btn.clicked.connect(self.view_table_ui)
172
- self.view_tab_btn.setToolTip('View table')
172
+ self.view_tab_btn.setToolTip('Explore table')
173
173
  self.view_tab_btn.setIcon(icon(MDI6.table,color="#1565c0"))
174
174
  self.view_tab_btn.setIconSize(QSize(20, 20))
175
175
  #self.view_tab_btn.setEnabled(False)
@@ -177,7 +177,7 @@ class ProcessPanel(QFrame, Styles):
177
177
 
178
178
  self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
179
179
  self.submit_btn = QPushButton("Submit")
180
- self.submit_btn.setStyleSheet(self.button_style_sheet_2)
180
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
181
181
  self.submit_btn.clicked.connect(self.process_population)
182
182
  self.grid_contents.addWidget(self.submit_btn, 11, 0, 1, 4)
183
183
 
@@ -228,7 +228,7 @@ class ProcessPanel(QFrame, Styles):
228
228
 
229
229
  signal_layout = QVBoxLayout()
230
230
  signal_hlayout = QHBoxLayout()
231
- self.signal_analysis_action = QCheckBox("SIGNAL ANALYSIS")
231
+ self.signal_analysis_action = QCheckBox("DETECT EVENTS")
232
232
  self.signal_analysis_action.setStyleSheet("""
233
233
  font-size: 10px;
234
234
  padding-left: 10px;
@@ -920,7 +920,7 @@ class ProcessPanel(QFrame, Styles):
920
920
  plot_mode = 'plot_track_signals'
921
921
  if 'TRACK_ID' not in list(self.df.columns):
922
922
  plot_mode = 'static'
923
- self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population=self.mode, plot_mode=plot_mode)
923
+ self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population=self.mode, plot_mode=plot_mode, save_inplace_option=True)
924
924
  self.tab_ui.show()
925
925
  else:
926
926
  print('Table could not be loaded...')
@@ -1207,28 +1207,19 @@ class NeighPanel(QFrame, Styles):
1207
1207
  self.measure_pairs_action.setToolTip("Measure the relative quantities defined for the cell pairs, for all neighborhoods.")
1208
1208
  rel_layout.addWidget(self.measure_pairs_action, 90)
1209
1209
 
1210
- # self.visu_btn = QPushButton()
1211
- # self.visu_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
1212
- # self.visu_btn.setIconSize(QSize(20, 20))
1213
- # self.visu_btn.clicked.connect(self.check_measurements2)
1214
- # self.visu_btn.setToolTip("Open measurement annotator for two populations.")
1215
- # self.visu_btn.setStyleSheet(self.button_select_all)
1216
- # self.grid_contents.addWidget(self.visu_btn, 1,1,1,1,alignment=Qt.AlignRight)
1217
- # rel_layout.addWidget(self.visu_btn, 6)
1218
-
1219
- # self.config_rel_annotator_btn = QPushButton()
1220
- # self.config_rel_annotator_btn.setIcon(icon(MDI6.cog_outline, color="black"))
1221
- # self.config_rel_annotator_btn.setIconSize(QSize(20, 20))
1222
- # self.config_rel_annotator_btn.setToolTip("Configure the animation of the annotation tool.")
1223
- # self.config_rel_annotator_btn.setStyleSheet(self.button_select_all)
1224
- # self.config_rel_annotator_btn.clicked.connect(self.open_signal_annotator_configuration_ui)
1225
- # # self.grid_contents.addWidget(self.config_rel_annotator_btn, 1,2,1,1, alignment=Qt.AlignRight)
1226
- # rel_layout.addWidget(self.config_rel_annotator_btn, 6)
1210
+ self.classify_pairs_btn = QPushButton()
1211
+ self.classify_pairs_btn.setIcon(icon(MDI6.scatter_plot, color="black"))
1212
+ self.classify_pairs_btn.setIconSize(QSize(20, 20))
1213
+ self.classify_pairs_btn.setToolTip("Classify data.")
1214
+ self.classify_pairs_btn.setStyleSheet(self.button_select_all)
1215
+ self.classify_pairs_btn.clicked.connect(self.open_classifier_ui_pairs)
1216
+ rel_layout.addWidget(self.classify_pairs_btn, 5) #4,2,1,1, alignment=Qt.AlignRight
1217
+
1227
1218
  self.grid_contents.addLayout(rel_layout, 6, 0, 1, 4)
1228
1219
 
1229
1220
  signal_layout = QVBoxLayout()
1230
1221
  signal_hlayout = QHBoxLayout()
1231
- self.signal_analysis_action = QCheckBox("PAIR SIGNAL ANALYSIS")
1222
+ self.signal_analysis_action = QCheckBox("DETECT PAIR EVENTS")
1232
1223
  self.signal_analysis_action.setStyleSheet("""
1233
1224
  font-size: 10px;
1234
1225
  padding-left: 10px;
@@ -1283,10 +1274,10 @@ class NeighPanel(QFrame, Styles):
1283
1274
  self.grid_contents.addLayout(signal_layout, 7, 0, 1, 4)
1284
1275
  self.grid_contents.addWidget(QHSeperationLine(), 11, 0, 1, 4)
1285
1276
 
1286
- self.view_tab_btn = QPushButton("View table")
1277
+ self.view_tab_btn = QPushButton("Explore table")
1287
1278
  self.view_tab_btn.setStyleSheet(self.button_style_sheet_2)
1288
1279
  self.view_tab_btn.clicked.connect(self.view_table_ui)
1289
- self.view_tab_btn.setToolTip('View table')
1280
+ self.view_tab_btn.setToolTip('Explore table')
1290
1281
  self.view_tab_btn.setIcon(icon(MDI6.table,color="#1565c0"))
1291
1282
  self.view_tab_btn.setIconSize(QSize(20, 20))
1292
1283
  #self.view_tab_btn.setEnabled(False)
@@ -1295,7 +1286,7 @@ class NeighPanel(QFrame, Styles):
1295
1286
  #self.grid_contents.addWidget(QLabel(''), 12, 0, 1, 4)
1296
1287
 
1297
1288
  self.submit_btn = QPushButton("Submit")
1298
- self.submit_btn.setStyleSheet(self.button_style_sheet_2)
1289
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
1299
1290
  self.submit_btn.setToolTip("Compute the neighborhoods of the selected positions.")
1300
1291
  self.submit_btn.clicked.connect(self.process_neighborhood)
1301
1292
  self.grid_contents.addWidget(self.submit_btn, 14, 0, 1, 4)
@@ -1304,6 +1295,26 @@ class NeighPanel(QFrame, Styles):
1304
1295
  self.neigh_action.setChecked(True)
1305
1296
  self.neigh_action.setChecked(False)
1306
1297
 
1298
+ def open_classifier_ui_pairs(self):
1299
+
1300
+ self.mode = "pairs"
1301
+ self.load_available_tables()
1302
+ if self.df is None:
1303
+
1304
+ msgBox = QMessageBox()
1305
+ msgBox.setIcon(QMessageBox.Warning)
1306
+ msgBox.setText("No table was found...")
1307
+ msgBox.setWindowTitle("Warning")
1308
+ msgBox.setStandardButtons(QMessageBox.Ok)
1309
+ returnValue = msgBox.exec()
1310
+ if returnValue == QMessageBox.Ok:
1311
+ return None
1312
+ else:
1313
+ return None
1314
+ else:
1315
+ self.ClassifierWidget = ClassifierWidget(self)
1316
+ self.ClassifierWidget.show()
1317
+
1307
1318
 
1308
1319
  def help_neighborhood(self):
1309
1320
 
@@ -1360,7 +1371,7 @@ class NeighPanel(QFrame, Styles):
1360
1371
 
1361
1372
  if self.df is not None:
1362
1373
  plot_mode = 'static'
1363
- self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population='pairs', plot_mode=plot_mode)
1374
+ self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population='pairs', plot_mode=plot_mode, save_inplace_option=True)
1364
1375
  self.tab_ui.show()
1365
1376
  else:
1366
1377
  print('Table could not be loaded...')
@@ -1657,12 +1668,33 @@ class PreprocessingPanel(QFrame, Styles):
1657
1668
 
1658
1669
  self.protocol_layout.title_layout.addWidget(self.help_background_btn, 5, alignment=Qt.AlignRight)
1659
1670
 
1671
+
1672
+ self.channel_offset_correction_layout = QVBoxLayout()
1673
+
1674
+ self.channel_shift_lbl = QLabel("CHANNEL OFFSET CORRECTION")
1675
+ self.channel_shift_lbl.setStyleSheet("""
1676
+ font-weight: bold;
1677
+ padding: 0px;
1678
+ """)
1679
+ self.channel_offset_correction_layout.addWidget(self.channel_shift_lbl, alignment=Qt.AlignCenter)
1680
+
1681
+ self.channel_offset_options_layout = ChannelOffsetOptionsLayout(self)
1682
+ self.channel_offset_correction_layout.addLayout(self.channel_offset_options_layout)
1683
+
1684
+ self.protocol_layout.correction_layout.addWidget(QLabel(''))
1685
+ self.protocol_layout.correction_layout.addLayout(self.channel_offset_correction_layout)
1686
+
1660
1687
  self.grid_contents.addLayout(self.protocol_layout,0,0,1,4)
1688
+
1661
1689
  self.submit_preprocessing_btn = QPushButton("Submit")
1662
- self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet_2)
1690
+ self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet)
1663
1691
  self.submit_preprocessing_btn.clicked.connect(self.launch_preprocessing)
1692
+
1664
1693
  self.grid_contents.addWidget(self.submit_preprocessing_btn, 1,0,1,4)
1665
1694
 
1695
+ def add_offset_instructions_to_parent_list(self):
1696
+ print('adding instructions')
1697
+
1666
1698
 
1667
1699
  def launch_preprocessing(self):
1668
1700
 
@@ -1712,7 +1744,7 @@ class PreprocessingPanel(QFrame, Styles):
1712
1744
  export_prefix = None
1713
1745
 
1714
1746
  if correction_protocol['correction_type']=='model-free':
1715
-
1747
+ print(f'Model-free correction; {movie_prefix=} {export_prefix=}')
1716
1748
  correct_background_model_free(self.exp_dir,
1717
1749
  well_option=well_option,
1718
1750
  position_option=pos_option,
@@ -1726,7 +1758,7 @@ class PreprocessingPanel(QFrame, Styles):
1726
1758
  )
1727
1759
 
1728
1760
  elif correction_protocol['correction_type']=='fit':
1729
-
1761
+ print(f'Fit correction; {movie_prefix=} {export_prefix=} {correction_protocol=}')
1730
1762
  correct_background_model(self.exp_dir,
1731
1763
  well_option=well_option,
1732
1764
  position_option=pos_option,
@@ -1738,6 +1770,19 @@ class PreprocessingPanel(QFrame, Styles):
1738
1770
  export_prefix = export_prefix,
1739
1771
  **correction_protocol,
1740
1772
  )
1773
+ elif correction_protocol['correction_type']=='offset':
1774
+ print(f'Offset correction; {movie_prefix=} {export_prefix=} {correction_protocol=}')
1775
+ correct_channel_offset(self.exp_dir,
1776
+ well_option=well_option,
1777
+ position_option=pos_option,
1778
+ export= True,
1779
+ return_stacks=False,
1780
+ show_progress_per_well = True,
1781
+ show_progress_per_pos = True,
1782
+ movie_prefix = movie_prefix,
1783
+ export_prefix = export_prefix,
1784
+ **correction_protocol,
1785
+ )
1741
1786
  print('Done.')
1742
1787
 
1743
1788
 
@@ -8,7 +8,7 @@ from superqt import QLabeledDoubleSlider,QLabeledSlider
8
8
  from superqt.fonticon import icon
9
9
  from fonticon_mdi6 import MDI6
10
10
  from celldetective.utils import get_software_location
11
- from celldetective.io import get_segmentation_datasets_list, locate_segmentation_dataset
11
+ from celldetective.io import get_segmentation_datasets_list, locate_segmentation_dataset, get_segmentation_models_list
12
12
  from celldetective.segmentation import train_segmentation_model
13
13
  from celldetective.gui.layouts import CellposeParamsWidget
14
14
  import numpy as np
@@ -85,8 +85,11 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
85
85
  self.submit_btn.clicked.connect(self.prep_model)
86
86
  self.main_layout.addWidget(self.submit_btn)
87
87
  self.submit_btn.setEnabled(False)
88
+ self.submit_warning = QLabel('')
89
+ self.main_layout.addWidget(self.submit_warning)
88
90
 
89
91
  self.spatial_calib_le.textChanged.connect(self.activate_train_btn)
92
+ self.modelname_le.setText(f"Untitled_model_{datetime.today().strftime('%Y-%m-%d')}")
90
93
 
91
94
  #self.populate_left_panel()
92
95
  #grid.addLayout(self.left_side, 0, 0, 1, 1)
@@ -273,6 +276,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
273
276
  modelname_layout.addWidget(QLabel('Model name: '), 30)
274
277
  self.modelname_le = QLineEdit()
275
278
  self.modelname_le.setText(f"Untitled_model_{datetime.today().strftime('%Y-%m-%d')}")
279
+ self.modelname_le.textChanged.connect(self.activate_train_btn)
276
280
  modelname_layout.addWidget(self.modelname_le, 70)
277
281
  layout.addLayout(modelname_layout)
278
282
 
@@ -317,13 +321,21 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
317
321
  spatial_calib_layout.addWidget(self.spatial_calib_le, 70)
318
322
  layout.addLayout(spatial_calib_layout)
319
323
 
320
-
321
-
322
324
  def activate_train_btn(self):
323
- if self.spatial_calib_le.text()=='':
324
- self.submit_btn.setEnabled(False)
325
- elif not np.all([cb.currentText()=='--' for cb in self.ch_norm.channel_cbs]):
325
+
326
+ current_name = self.modelname_le.text()
327
+ models = get_segmentation_models_list(mode=self.mode, return_path=False)
328
+ if not current_name in models and not self.spatial_calib_le.text()=='' and not np.all([cb.currentText()=='--' for cb in self.ch_norm.channel_cbs]):
326
329
  self.submit_btn.setEnabled(True)
330
+ self.submit_warning.setText('')
331
+ else:
332
+ self.submit_btn.setEnabled(False)
333
+ if current_name in models:
334
+ self.submit_warning.setText('A model with this name already exists... Please pick another.')
335
+ elif self.spatial_calib_le.text()=='':
336
+ self.submit_warning.setText('Please provide a valid spatial calibration...')
337
+ elif np.all([cb.currentText()=='--' for cb in self.ch_norm.channel_cbs]):
338
+ self.submit_warning.setText('Please provide valid channels...')
327
339
 
328
340
  def rescale_slider(self):
329
341
  if self.stardist_model.isChecked():
@@ -336,11 +348,13 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
336
348
 
337
349
  def showDialog_pretrained(self):
338
350
 
339
- try:
340
- self.cancel_pretrained.click()
341
- except:
342
- pass
351
+ # try:
352
+ # self.cancel_pretrained.click()
353
+ # except Exception as e:
354
+ # print(e)
355
+ # pass
343
356
 
357
+ self.clear_pretrained()
344
358
  self.pretrained_model = None
345
359
  self.pretrained_model = QFileDialog.getExistingDirectory(
346
360
  self, "Open Directory",
@@ -35,7 +35,10 @@ class SignalAnnotator(QMainWindow, Styles):
35
35
  def __init__(self, parent_window=None):
36
36
 
37
37
  super().__init__()
38
+
38
39
  center_window(self)
40
+ self.proceed = True
41
+ self.setAttribute(Qt.WA_DeleteOnClose)
39
42
 
40
43
  self.parent_window = parent_window
41
44
  self.setWindowTitle("Signal annotator")
@@ -65,18 +68,19 @@ class SignalAnnotator(QMainWindow, Styles):
65
68
  self.status_name = 'status'
66
69
 
67
70
  self.locate_stack()
68
- self.load_annotator_config()
69
- self.locate_tracks()
70
- self.prepare_stack()
71
+ if not self.proceed:
72
+ self.close()
73
+ else:
74
+ self.load_annotator_config()
75
+ self.locate_tracks()
76
+ self.prepare_stack()
71
77
 
72
- self.generate_signal_choices()
73
- self.frame_lbl = QLabel('frame: ')
74
- self.looped_animation()
75
- self.create_cell_signal_canvas()
78
+ self.generate_signal_choices()
79
+ self.frame_lbl = QLabel('frame: ')
80
+ self.looped_animation()
81
+ self.create_cell_signal_canvas()
76
82
 
77
- self.populate_widget()
78
-
79
- self.setAttribute(Qt.WA_DeleteOnClose)
83
+ self.populate_widget()
80
84
 
81
85
  def populate_widget(self):
82
86
 
@@ -645,11 +649,14 @@ class SignalAnnotator(QMainWindow, Styles):
645
649
  if len(movies) == 0:
646
650
  msgBox = QMessageBox()
647
651
  msgBox.setIcon(QMessageBox.Warning)
648
- msgBox.setText("No movies are detected in the experiment folder. Cannot load an image to test Haralick.")
652
+ msgBox.setText("No movie is detected in the experiment folder.\nPlease check the stack prefix...")
649
653
  msgBox.setWindowTitle("Warning")
650
654
  msgBox.setStandardButtons(QMessageBox.Ok)
651
655
  returnValue = msgBox.exec()
652
- if returnValue == QMessageBox.Yes:
656
+ if returnValue == QMessageBox.Ok:
657
+ self.proceed = False
658
+ self.close()
659
+ else:
653
660
  self.close()
654
661
  else:
655
662
  self.stack_path = movies[0]
@@ -977,15 +984,17 @@ class SignalAnnotator(QMainWindow, Styles):
977
984
  self.stack[np.where(self.stack > 0.)] = np.log(self.stack[np.where(self.stack > 0.)])
978
985
 
979
986
  def closeEvent(self, event):
980
-
981
- self.stop()
982
- # result = QMessageBox.question(self,
983
- # "Confirm Exit...",
984
- # "Are you sure you want to exit ?",
985
- # QMessageBox.Yes| QMessageBox.No,
986
- # )
987
- del self.stack
988
- gc.collect()
987
+ try:
988
+ self.stop()
989
+ # result = QMessageBox.question(self,
990
+ # "Confirm Exit...",
991
+ # "Are you sure you want to exit ?",
992
+ # QMessageBox.Yes| QMessageBox.No,
993
+ # )
994
+ del self.stack
995
+ gc.collect()
996
+ except:
997
+ pass
989
998
 
990
999
  def looped_animation(self):
991
1000
 
@@ -1118,6 +1127,7 @@ class SignalAnnotator(QMainWindow, Styles):
1118
1127
  try:
1119
1128
  min_values = []
1120
1129
  max_values = []
1130
+ feats = []
1121
1131
  for i in range(len(self.signal_choice_cb)):
1122
1132
  signal = self.signal_choice_cb[i].currentText()
1123
1133
  if signal == '--':
@@ -1127,9 +1137,26 @@ class SignalAnnotator(QMainWindow, Styles):
1127
1137
  minn = np.nanpercentile(self.df_tracks.loc[:, signal].to_numpy().flatten(), 1)
1128
1138
  min_values.append(minn)
1129
1139
  max_values.append(maxx)
1140
+ feats.append(signal)
1141
+
1142
+ smallest_value = np.amin(min_values)
1143
+ feat_smallest_value = feats[np.argmin(min_values)]
1144
+ min_feat = self.df_tracks[feat_smallest_value].min()
1145
+ max_feat = self.df_tracks[feat_smallest_value].max()
1146
+ pad_small = (max_feat - min_feat) * 0.05
1147
+ if pad_small==0:
1148
+ pad_small = 0.05
1149
+
1150
+ largest_value = np.amax(max_values)
1151
+ feat_largest_value = feats[np.argmax(max_values)]
1152
+ min_feat = self.df_tracks[feat_largest_value].min()
1153
+ max_feat = self.df_tracks[feat_largest_value].max()
1154
+ pad_large = (max_feat - min_feat) * 0.05
1155
+ if pad_large==0:
1156
+ pad_large = 0.05
1130
1157
 
1131
1158
  if len(min_values) > 0:
1132
- self.cell_ax.set_ylim(np.amin(min_values), np.amax(max_values))
1159
+ self.cell_ax.set_ylim(smallest_value - pad_small, largest_value + pad_large)
1133
1160
  except Exception as e:
1134
1161
  pass
1135
1162
 
@@ -2305,8 +2332,8 @@ class MeasureAnnotator(SignalAnnotator):
2305
2332
  else:
2306
2333
  self.current_stack = self.current_stack[0]
2307
2334
  if self.log_option:
2308
- self.current_stack[np.where(self.current_stack > 0.)] = np.log(
2309
- self.current_stack[np.where(self.current_stack > 0.)])
2335
+ self.current_stack[np.where((self.current_stack > 0.)&(self.current_stack==self.current_stack))] = np.log(
2336
+ self.current_stack[np.where((self.current_stack > 0.)&(self.current_stack==self.current_stack))])
2310
2337
 
2311
2338
  def changed_channel(self):
2312
2339
 
@@ -2422,11 +2449,11 @@ class MeasureAnnotator(SignalAnnotator):
2422
2449
  if len(movies) == 0:
2423
2450
  msgBox = QMessageBox()
2424
2451
  msgBox.setIcon(QMessageBox.Warning)
2425
- msgBox.setText("No movies are detected in the experiment folder. Cannot load an image to test Haralick.")
2452
+ msgBox.setText("No movie is detected in the experiment folder.\nPlease check the stack prefix...")
2426
2453
  msgBox.setWindowTitle("Warning")
2427
2454
  msgBox.setStandardButtons(QMessageBox.Ok)
2428
2455
  returnValue = msgBox.exec()
2429
- if returnValue == QMessageBox.Yes:
2456
+ if returnValue == QMessageBox.Ok:
2430
2457
  self.close()
2431
2458
  else:
2432
2459
  self.stack_path = movies[0]
@@ -140,17 +140,22 @@ class SignalAnnotator2(QMainWindow,Styles):
140
140
 
141
141
  self.button_widget = QWidget()
142
142
  main_layout = QHBoxLayout()
143
+ main_layout.setSpacing(30)
144
+
143
145
  self.button_widget.setLayout(main_layout)
144
146
 
145
147
  main_layout.setContentsMargins(30,30,30,30)
146
148
  self.left_panel = QVBoxLayout()
147
- self.left_panel.setContentsMargins(30,30,30,30)
148
- self.left_panel.setSpacing(10)
149
+ self.left_panel.setContentsMargins(5,5,5,5)
150
+ self.left_panel.setSpacing(5)
149
151
 
150
152
  self.right_panel = QVBoxLayout()
153
+ self.right_panel.setSpacing(0)
154
+ self.right_panel.setContentsMargins(5,5,5,5)
151
155
 
152
156
  #NEIGHBORHOOD
153
157
  neigh_hbox = QHBoxLayout()
158
+ neigh_hbox.setContentsMargins(0,0,0,0)
154
159
  neigh_hbox.addWidget(QLabel('neighborhood: '), 25)
155
160
  self.neighborhood_choice_cb = QComboBox()
156
161
  self.neighborhood_choice_cb.addItems(self.neighborhood_cols)
@@ -193,17 +198,17 @@ class SignalAnnotator2(QMainWindow,Styles):
193
198
  self.relative_del_class_btn.clicked.connect(self.del_relative_event_class)
194
199
  subclass_hbox.addWidget(self.relative_del_class_btn, 5)
195
200
  class_hbox.addLayout(subclass_hbox, 75)
196
-
197
-
198
201
  self.left_panel.addLayout(class_hbox)
199
202
 
200
203
  self.cell_events_hbox = QHBoxLayout()
204
+ self.cell_events_hbox.setContentsMargins(0,0,0,0)
201
205
  self.cell_events_hbox.addWidget(QLabel('reference event: '), 25)
202
206
  self.reference_event_choice_cb = QComboBox()
203
207
  self.cell_events_hbox.addWidget(self.reference_event_choice_cb, 75)
204
208
 
205
209
  #if 'self' not in self.neighborhood_choice_cb.currentText():
206
210
  self.neigh_cell_events_hbox = QHBoxLayout()
211
+ self.neigh_cell_events_hbox.setContentsMargins(0,0,0,0)
207
212
  self.neigh_lab=QLabel('neighbor event: ')
208
213
  self.neigh_cell_events_hbox.addWidget(self.neigh_lab, 25)
209
214
  self.neighbor_event_choice_cb = QComboBox()
@@ -214,7 +219,7 @@ class SignalAnnotator2(QMainWindow,Styles):
214
219
 
215
220
  # Text information about selected cells
216
221
  self.cell_info_hbox = QHBoxLayout()
217
- self.cell_info_hbox.setContentsMargins(30,30,30,30)
222
+ self.cell_info_hbox.setContentsMargins(30,0,30,0)
218
223
 
219
224
  reference_layout = QVBoxLayout()
220
225
  reference_layout.addWidget(self.reference_cell_info)
@@ -232,7 +237,7 @@ class SignalAnnotator2(QMainWindow,Styles):
232
237
 
233
238
  # Annotation buttons
234
239
  options_hbox = QHBoxLayout()
235
- options_hbox.setContentsMargins(150, 30, 50, 0)
240
+ options_hbox.setContentsMargins(150, 0, 50, 0)
236
241
  self.event_btn = QRadioButton('event')
237
242
  self.event_btn.setStyleSheet(self.button_style_sheet_2)
238
243
  self.event_btn.toggled.connect(self.enable_time_of_interest)
@@ -256,7 +261,7 @@ class SignalAnnotator2(QMainWindow,Styles):
256
261
  self.left_panel.addLayout(options_hbox)
257
262
 
258
263
  time_option_hbox = QHBoxLayout()
259
- time_option_hbox.setContentsMargins(100, 30, 100, 30)
264
+ time_option_hbox.setContentsMargins(100, 0, 100, 0)
260
265
  self.time_of_interest_label = QLabel('time of interest: ')
261
266
  time_option_hbox.addWidget(self.time_of_interest_label, 30)
262
267
  self.time_of_interest_le = QLineEdit()
@@ -264,6 +269,7 @@ class SignalAnnotator2(QMainWindow,Styles):
264
269
  self.left_panel.addLayout(time_option_hbox)
265
270
 
266
271
  main_action_hbox = QHBoxLayout()
272
+ main_action_hbox.setContentsMargins(100, 0, 100, 0)
267
273
  self.correct_btn = QPushButton('correct')
268
274
  self.correct_btn.setIcon(icon(MDI6.redo_variant, color="white"))
269
275
  self.correct_btn.setIconSize(QSize(20, 20))
@@ -320,7 +326,7 @@ class SignalAnnotator2(QMainWindow,Styles):
320
326
  self.left_panel.addLayout(plot_buttons_hbox)
321
327
 
322
328
  signal_choice_grid = QVBoxLayout()
323
- signal_choice_grid.setContentsMargins(30,0,30,50)
329
+ signal_choice_grid.setContentsMargins(30,0,70,5)
324
330
 
325
331
  header_layout = QHBoxLayout()
326
332
  header_layout.addWidget(QLabel('reference'), 23, alignment=Qt.AlignCenter)
@@ -332,32 +338,14 @@ class SignalAnnotator2(QMainWindow,Styles):
332
338
  for i in range(self.n_signals):
333
339
 
334
340
  h_layout = QHBoxLayout()
341
+ if i==(self.n_signals-1):
342
+ h_layout.setContentsMargins(0,0,0,0)
335
343
  h_layout.addWidget(self.reference_pop_option_buttons[i], 23, alignment=Qt.AlignCenter)
336
344
  h_layout.addWidget(self.neighbor_pop_option_buttons[i], 23, alignment=Qt.AlignCenter)
337
345
  h_layout.addWidget(self.relative_pop_option_buttons[i], 23, alignment=Qt.AlignCenter)
338
346
  h_layout.addWidget(self.signal_choices[i], 30)
339
347
  signal_choice_grid.addLayout(h_layout)
340
348
 
341
- # signal_choice_vbox = QVBoxLayout()
342
- # signal_choice_vbox.setContentsMargins(30,0,30,50)
343
- # for i in range(len(self.signal_choices)):
344
- # signal_choice_grid.addWidget(self.signal_choices[i],i+1,3)
345
- # # hlayout = QHBoxLayout()
346
- # #
347
- # # #hlayout.addLayout(self.signal_labels[i], 20)
348
- # # #hlayout.addLayout(self.signal_choices[i], 75)
349
- # # # if i==0:
350
- # # # hlayout.addWidget(self.signal_choices[i], 75,alignment=Qt.AlignBottom)
351
- # # # else:
352
- # # hlayout.addWidget(self.signal_choices[i], 75)
353
- # # #hlayout.addWidget(self.log_btns[i], 5)
354
- # # signal_choice_vbox.addLayout(hlayout)
355
-
356
- # self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
357
- # self.log_btns[i].setStyleSheet(self.parent.parent.parent.button_select_all)
358
- # self.log_btns[i].clicked.connect(lambda ch, i=i: self.switch_to_log(i))
359
- #signal_choice_hbox.addLayout(signal_choice_vbox,alignment=Qt.AlignCenter)
360
-
361
349
  self.left_panel.addLayout(signal_choice_grid)
362
350
 
363
351
  btn_hbox = QHBoxLayout()
@@ -377,7 +365,6 @@ class SignalAnnotator2(QMainWindow,Styles):
377
365
  # Animation
378
366
  animation_buttons_box = QHBoxLayout()
379
367
 
380
-
381
368
  animation_buttons_box.addWidget(self.frame_lbl, 20, alignment=Qt.AlignLeft)
382
369
 
383
370
  self.first_frame_btn = QPushButton()
@@ -1817,7 +1804,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1817
1804
  else:
1818
1805
  self.stack = self.stack[0]
1819
1806
  if self.log_option:
1820
- self.stack[np.where(self.stack>0.)] = np.log(self.stack[np.where(self.stack>0.)])
1807
+ self.stack[np.where((self.stack>0.)&(self.stack==self.stack))] = np.log(self.stack[np.where((self.stack>0.))&(self.stack==self.stack)])
1821
1808
 
1822
1809
  print(f'Load stack of shape: {self.stack.shape}.')
1823
1810
 
@@ -125,7 +125,16 @@ class ConfigSurvival(QWidget, Styles):
125
125
  select_layout.addWidget(self.query_le, 66)
126
126
  main_layout.addLayout(select_layout)
127
127
 
128
- self.cbs[0].setCurrentIndex(0)
128
+ time_cut_layout = QHBoxLayout()
129
+ cut_time_lbl = QLabel('cut obs.\ntime [min]: ')
130
+ cut_time_lbl.setToolTip('Filter out later events from\nthe analysis (in absolute time).')
131
+ time_cut_layout.addWidget(cut_time_lbl, 33)
132
+ self.query_time_cut = QLineEdit()
133
+ self.query_time_cut.setValidator(self.float_validator)
134
+ time_cut_layout.addWidget(self.query_time_cut, 66)
135
+ main_layout.addLayout(time_cut_layout)
136
+
137
+ self.set_classes_and_times()
129
138
  self.cbs[1].setCurrentText('t_firstdetection')
130
139
 
131
140
  time_calib_layout = QHBoxLayout()
@@ -167,6 +176,8 @@ class ConfigSurvival(QWidget, Styles):
167
176
  print('no column starts with t')
168
177
  self.auto_close = True
169
178
  return None
179
+ if 't0' in self.all_columns:
180
+ time_columns.append('t0')
170
181
 
171
182
  self.cbs[1].clear()
172
183
  self.cbs[1].addItems(np.unique(self.cb_options[1]+time_columns))
@@ -259,17 +270,27 @@ class ConfigSurvival(QWidget, Styles):
259
270
 
260
271
  def compute_survival_functions(self):
261
272
 
273
+ cut_observation_time = None
274
+ try:
275
+ cut_observation_time = float(self.query_time_cut.text().replace(',','.')) / self.FrameToMin
276
+ if not 0<cut_observation_time<=(self.df['FRAME'].max()):
277
+ print('Invalid cut time (larger than movie length)... Not applied.')
278
+ cut_observation_time = None
279
+ except Exception as e:
280
+ pass
281
+ print(f"{cut_observation_time=}")
282
+
262
283
  # Per position survival
263
284
  for block,movie_group in self.df.groupby(['well','position']):
264
285
 
265
- ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin)
286
+ ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time)
266
287
  if ks is not None:
267
288
  self.df_pos_info.loc[self.df_pos_info['pos_path']==block[1],'survival_fit'] = ks
268
289
 
269
290
  # Per well survival
270
291
  for well,well_group in self.df.groupby('well'):
271
292
 
272
- ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin)
293
+ ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time)
273
294
  if ks is not None:
274
295
  self.df_well_info.loc[self.df_well_info['well_path']==well,'survival_fit'] = ks
275
296