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.
- celldetective/_version.py +1 -1
- celldetective/events.py +88 -11
- celldetective/extra_properties.py +5 -1
- celldetective/gui/InitWindow.py +35 -9
- celldetective/gui/classifier_widget.py +99 -23
- celldetective/gui/control_panel.py +7 -1
- celldetective/gui/generic_signal_plot.py +161 -2
- celldetective/gui/gui_utils.py +90 -1
- celldetective/gui/layouts.py +128 -7
- celldetective/gui/measurement_options.py +3 -3
- celldetective/gui/plot_signals_ui.py +8 -3
- celldetective/gui/process_block.py +77 -32
- celldetective/gui/retrain_segmentation_model_options.py +24 -10
- celldetective/gui/signal_annotator.py +53 -26
- celldetective/gui/signal_annotator2.py +17 -30
- celldetective/gui/survival_ui.py +24 -3
- celldetective/gui/tableUI.py +300 -183
- celldetective/gui/viewers.py +263 -3
- celldetective/io.py +56 -3
- celldetective/links/zenodo.json +136 -123
- celldetective/measure.py +3 -0
- celldetective/models/tracking_configs/biased_motion.json +68 -0
- celldetective/models/tracking_configs/no_z_motion.json +202 -0
- celldetective/neighborhood.py +154 -69
- celldetective/preprocessing.py +172 -3
- celldetective/relative_measurements.py +128 -4
- celldetective/scripts/measure_cells.py +3 -3
- celldetective/signals.py +212 -215
- celldetective/tracking.py +7 -3
- celldetective/utils.py +22 -6
- {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/METADATA +3 -3
- {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/RECORD +36 -34
- {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/WHEEL +1 -1
- {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/LICENSE +0 -0
- {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/entry_points.txt +0 -0
- {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("
|
|
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('
|
|
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.
|
|
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("
|
|
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
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
|
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("
|
|
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('
|
|
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.
|
|
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.
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
341
|
-
except:
|
|
342
|
-
|
|
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.
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
self.generate_signal_choices()
|
|
79
|
+
self.frame_lbl = QLabel('frame: ')
|
|
80
|
+
self.looped_animation()
|
|
81
|
+
self.create_cell_signal_canvas()
|
|
76
82
|
|
|
77
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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(
|
|
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
|
|
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.
|
|
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(
|
|
148
|
-
self.left_panel.setSpacing(
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
celldetective/gui/survival_ui.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|