celldetective 1.3.5__py3-none-any.whl → 1.3.6.post2__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/filters.py +22 -2
- celldetective/gui/btrack_options.py +151 -1
- celldetective/gui/classifier_widget.py +26 -22
- celldetective/gui/configure_new_exp.py +13 -0
- celldetective/gui/control_panel.py +1 -0
- celldetective/gui/gui_utils.py +138 -8
- celldetective/gui/measurement_options.py +84 -24
- celldetective/gui/neighborhood_options.py +1 -1
- celldetective/gui/signal_annotator.py +6 -1
- celldetective/gui/signal_annotator2.py +22 -19
- celldetective/gui/survival_ui.py +0 -2
- celldetective/gui/thresholds_gui.py +9 -52
- celldetective/gui/viewers.py +58 -21
- celldetective/io.py +31 -8
- celldetective/measure.py +132 -157
- celldetective/scripts/measure_cells.py +28 -13
- celldetective/scripts/segment_cells.py +24 -20
- celldetective/scripts/segment_cells_thresholds.py +21 -21
- celldetective/scripts/track_cells.py +55 -17
- celldetective/tracking.py +78 -53
- celldetective/utils.py +5 -2
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/METADATA +2 -1
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/RECORD +28 -28
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/LICENSE +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/WHEEL +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post2.dist-info}/top_level.txt +0 -0
|
@@ -27,9 +27,10 @@ from pathlib import Path
|
|
|
27
27
|
|
|
28
28
|
from celldetective.gui.viewers import CellEdgeVisualizer, SpotDetectionVisualizer
|
|
29
29
|
from celldetective.gui.layouts import ProtocolDesignerLayout, BackgroundFitCorrectionLayout, LocalCorrectionLayout
|
|
30
|
-
from celldetective.gui.gui_utils import ThresholdLineEdit
|
|
30
|
+
from celldetective.gui.gui_utils import ThresholdLineEdit, PreprocessingLayout2
|
|
31
31
|
from celldetective.gui import Styles
|
|
32
32
|
|
|
33
|
+
|
|
33
34
|
class ConfigMeasurements(QMainWindow, Styles):
|
|
34
35
|
"""
|
|
35
36
|
UI to set measurement instructions.
|
|
@@ -477,6 +478,8 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
477
478
|
Write the selected options in a json file for later reading by the software.
|
|
478
479
|
"""
|
|
479
480
|
|
|
481
|
+
print(f"{self.spot_preprocessing.list.items=}")
|
|
482
|
+
|
|
480
483
|
print('Writing instructions...')
|
|
481
484
|
measurement_options = {}
|
|
482
485
|
background_correction = self.protocol_layout.protocols
|
|
@@ -508,8 +511,11 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
508
511
|
'isotropic_operations': isotropic_operations})
|
|
509
512
|
spot_detection = None
|
|
510
513
|
if self.spot_check.isChecked():
|
|
514
|
+
image_preprocessing = self.spot_preprocessing.list.items
|
|
515
|
+
if image_preprocessing==[]:
|
|
516
|
+
image_preprocessing = None
|
|
511
517
|
spot_detection = {'channel': self.spot_channel.currentText(), 'diameter': float(self.diameter_value.text().replace(',','.')),
|
|
512
|
-
'threshold': float(self.threshold_value.text().replace(',','.'))}
|
|
518
|
+
'threshold': float(self.threshold_value.text().replace(',','.')), 'image_preprocessing': image_preprocessing}
|
|
513
519
|
measurement_options.update({'spot_detection': spot_detection})
|
|
514
520
|
if self.clear_previous_btn.isChecked():
|
|
515
521
|
self.clear_previous = True
|
|
@@ -518,7 +524,6 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
518
524
|
measurement_options.update({'clear_previous': self.clear_previous})
|
|
519
525
|
|
|
520
526
|
|
|
521
|
-
|
|
522
527
|
print('Measurement instructions: ', measurement_options)
|
|
523
528
|
file_name = self.measure_instructions_path
|
|
524
529
|
with open(file_name, 'w') as f:
|
|
@@ -589,7 +594,13 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
589
594
|
self.spot_channel.setCurrentText(idx)
|
|
590
595
|
self.diameter_value.setText(str(spot_detection['diameter']))
|
|
591
596
|
self.threshold_value.setText(str(spot_detection['threshold']))
|
|
592
|
-
|
|
597
|
+
if 'image_preprocessing' in spot_detection:
|
|
598
|
+
items = spot_detection['image_preprocessing']
|
|
599
|
+
if items is not None:
|
|
600
|
+
items_for_list = [a[0] for a in items]
|
|
601
|
+
for it in items_for_list:
|
|
602
|
+
self.spot_preprocessing.list.addItemToList(it)
|
|
603
|
+
self.spot_preprocessing.list.items = items
|
|
593
604
|
|
|
594
605
|
if 'border_distances' in measurement_instructions:
|
|
595
606
|
border_distances = measurement_instructions['border_distances']
|
|
@@ -886,43 +897,81 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
886
897
|
|
|
887
898
|
def populate_spot_detection(self):
|
|
888
899
|
|
|
889
|
-
layout =
|
|
900
|
+
layout = QVBoxLayout(self.spot_detection_frame)
|
|
901
|
+
|
|
890
902
|
self.spot_detection_lbl = QLabel("SPOT DETECTION")
|
|
891
903
|
self.spot_detection_lbl.setStyleSheet("""font-weight: bold;padding: 0px;""")
|
|
892
|
-
layout.addWidget(self.spot_detection_lbl,
|
|
893
|
-
|
|
904
|
+
layout.addWidget(self.spot_detection_lbl, alignment=Qt.AlignCenter)
|
|
905
|
+
|
|
906
|
+
perform_hbox = QHBoxLayout()
|
|
907
|
+
self.spot_check = QCheckBox('Perform spot detection')
|
|
894
908
|
self.spot_check.toggled.connect(self.enable_spot_detection)
|
|
895
|
-
|
|
896
|
-
|
|
909
|
+
perform_hbox.addWidget(self.spot_check, 95)
|
|
910
|
+
|
|
911
|
+
self.spot_viewer_btn = QPushButton()
|
|
912
|
+
self.spot_viewer_btn.clicked.connect(self.spot_preview)
|
|
913
|
+
self.spot_viewer_btn.setIcon(icon(MDI6.image_check, color="k"))
|
|
914
|
+
self.spot_viewer_btn.setStyleSheet(self.button_select_all)
|
|
915
|
+
self.spot_viewer_btn.setToolTip('Set detection parameters visually.')
|
|
916
|
+
perform_hbox.addWidget(self.spot_viewer_btn, 5)
|
|
917
|
+
layout.addLayout(perform_hbox)
|
|
918
|
+
|
|
919
|
+
channel_hbox = QHBoxLayout()
|
|
920
|
+
self.spot_channel_lbl = QLabel("Channel: ")
|
|
897
921
|
self.spot_channel = QComboBox()
|
|
898
922
|
self.spot_channel.addItems(self.channel_names)
|
|
899
|
-
|
|
900
|
-
|
|
923
|
+
channel_hbox.addWidget(self.spot_channel_lbl, 30)
|
|
924
|
+
channel_hbox.addWidget(self.spot_channel, 70)
|
|
925
|
+
layout.addLayout(channel_hbox)
|
|
926
|
+
|
|
927
|
+
self.spot_preprocessing = PreprocessingLayout2(fraction=30, parent_window=self)
|
|
928
|
+
layout.addLayout(self.spot_preprocessing)
|
|
929
|
+
|
|
930
|
+
# continue switching to VBox + HBox down
|
|
931
|
+
diam_hbox = QHBoxLayout()
|
|
901
932
|
self.diameter_lbl = QLabel('Spot diameter: ')
|
|
902
933
|
self.diameter_value = QLineEdit()
|
|
903
934
|
self.diameter_value.setValidator(self.onlyFloat)
|
|
904
935
|
self.diameter_value.setText('7')
|
|
905
936
|
self.diameter_value.textChanged.connect(self.enable_spot_preview)
|
|
906
937
|
|
|
907
|
-
|
|
908
|
-
|
|
938
|
+
diam_hbox.addWidget(self.diameter_lbl,30)
|
|
939
|
+
diam_hbox.addWidget(self.diameter_value, 70)
|
|
940
|
+
layout.addLayout(diam_hbox)
|
|
941
|
+
|
|
942
|
+
thresh_hbox = QHBoxLayout()
|
|
909
943
|
self.threshold_lbl = QLabel('Spot threshold: ')
|
|
910
944
|
self.threshold_value = QLineEdit()
|
|
911
945
|
self.threshold_value.setValidator(self.onlyFloat)
|
|
912
946
|
self.threshold_value.setText('0')
|
|
913
947
|
self.threshold_value.textChanged.connect(self.enable_spot_preview)
|
|
914
948
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
self.
|
|
921
|
-
self.
|
|
922
|
-
self.
|
|
923
|
-
layout.addWidget(self.
|
|
924
|
-
|
|
925
|
-
|
|
949
|
+
thresh_hbox.addWidget(self.threshold_lbl, 30)
|
|
950
|
+
thresh_hbox.addWidget(self.threshold_value, 70)
|
|
951
|
+
layout.addLayout(thresh_hbox)
|
|
952
|
+
|
|
953
|
+
# #invert_layout = QHBoxLayout()
|
|
954
|
+
# self.invert_check = QCheckBox('invert')
|
|
955
|
+
# self.invert_value_le = QLineEdit('65535')
|
|
956
|
+
# self.invert_value_le.setValidator(self.onlyFloat)
|
|
957
|
+
# layout.addWidget(self.invert_check, 6, 0)
|
|
958
|
+
# layout.addWidget(self.invert_value_le, 6, 1)
|
|
959
|
+
# #layout.addLayout(invert_layout, 5, 1, 1, 1)
|
|
960
|
+
|
|
961
|
+
self.spot_detection_widgets = [self.spot_channel,
|
|
962
|
+
self.spot_channel_lbl,
|
|
963
|
+
self.diameter_value,
|
|
964
|
+
self.diameter_lbl,
|
|
965
|
+
self.threshold_value,
|
|
966
|
+
self.threshold_lbl,
|
|
967
|
+
self.spot_viewer_btn,
|
|
968
|
+
#self.invert_check,
|
|
969
|
+
#self.invert_value_le,
|
|
970
|
+
self.spot_preprocessing.list,
|
|
971
|
+
self.spot_preprocessing.add_filter_btn,
|
|
972
|
+
self.spot_preprocessing.delete_filter_btn,
|
|
973
|
+
self.spot_preprocessing.preprocess_lbl
|
|
974
|
+
]
|
|
926
975
|
for wg in self.spot_detection_widgets:
|
|
927
976
|
wg.setEnabled(False)
|
|
928
977
|
|
|
@@ -940,6 +989,13 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
940
989
|
if self.test_frame is not None:
|
|
941
990
|
self.locate_mask()
|
|
942
991
|
if self.test_mask is not None:
|
|
992
|
+
|
|
993
|
+
# invert_value = self.invert_value_le.text().replace(',','.')
|
|
994
|
+
# if invert_value != '':
|
|
995
|
+
# invert_value = float(invert_value)
|
|
996
|
+
# else:
|
|
997
|
+
# invert_value = None
|
|
998
|
+
|
|
943
999
|
self.spot_visual = SpotDetectionVisualizer(frame_slider=True,
|
|
944
1000
|
contrast_slider=True,
|
|
945
1001
|
cell_type=self.mode,
|
|
@@ -952,6 +1008,10 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
952
1008
|
parent_channel_cb=self.spot_channel,
|
|
953
1009
|
parent_diameter_le=self.diameter_value,
|
|
954
1010
|
parent_threshold_le=self.threshold_value,
|
|
1011
|
+
parent_preprocessing_list=self.spot_preprocessing.list,
|
|
1012
|
+
#parent_invert_check=self.invert_check,
|
|
1013
|
+
#invert = self.invert_check.isChecked(),
|
|
1014
|
+
#invert_value = self.invert_value_le.text().replace(',','.'),
|
|
955
1015
|
PxToUm = 1,)
|
|
956
1016
|
self.spot_visual.show()
|
|
957
1017
|
#self.spot_visual = ThresholdSpot(current_channel=self.spot_channel.currentIndex(), img=self.test_frame,
|
|
@@ -400,7 +400,7 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
400
400
|
|
|
401
401
|
status_options = [self.reference_population_status_cb.currentText(), self.neighbor_population_status_cb.currentText()]
|
|
402
402
|
for k in range(2):
|
|
403
|
-
if status_options[k]=='--':
|
|
403
|
+
if status_options[k]=='--' or status_options[k]=='':
|
|
404
404
|
status_options[k] = None
|
|
405
405
|
if pop[0]!=pop[1]:
|
|
406
406
|
mode = 'two-pop'
|
|
@@ -7,7 +7,7 @@ from celldetective.gui.gui_utils import center_window, color_from_state
|
|
|
7
7
|
from superqt import QLabeledDoubleSlider, QLabeledDoubleRangeSlider, QSearchableComboBox
|
|
8
8
|
from celldetective.utils import extract_experiment_channels, get_software_location, _get_img_num_per_channel
|
|
9
9
|
from celldetective.io import auto_load_number_of_frames, load_frames, \
|
|
10
|
-
load_napari_data
|
|
10
|
+
load_napari_data, get_experiment_metadata
|
|
11
11
|
from celldetective.gui.gui_utils import FigureCanvas, color_from_status, color_from_class, ExportPlotBtn
|
|
12
12
|
import json
|
|
13
13
|
import numpy as np
|
|
@@ -781,6 +781,11 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
781
781
|
'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
|
|
782
782
|
'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
|
|
783
783
|
'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent'] + self.class_cols
|
|
784
|
+
meta = get_experiment_metadata(self.exp_dir)
|
|
785
|
+
if meta is not None:
|
|
786
|
+
keys = list(meta.keys())
|
|
787
|
+
cols_to_remove.extend(keys)
|
|
788
|
+
|
|
784
789
|
cols = np.array(list(self.df_tracks.columns))
|
|
785
790
|
time_cols = np.array([c.startswith('t_') for c in cols])
|
|
786
791
|
time_cols = list(cols[time_cols])
|
|
@@ -7,7 +7,7 @@ from celldetective.gui import Styles
|
|
|
7
7
|
from celldetective.gui.gui_utils import center_window
|
|
8
8
|
from superqt import QLabeledDoubleRangeSlider, QSearchableComboBox
|
|
9
9
|
from celldetective.utils import extract_experiment_channels, get_software_location, _get_img_num_per_channel
|
|
10
|
-
from celldetective.io import auto_load_number_of_frames, load_frames
|
|
10
|
+
from celldetective.io import auto_load_number_of_frames, load_frames, get_experiment_metadata
|
|
11
11
|
from celldetective.gui.gui_utils import FigureCanvas, color_from_status, color_from_class
|
|
12
12
|
import json
|
|
13
13
|
import numpy as np
|
|
@@ -66,7 +66,10 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
66
66
|
't0', 'POSITION_X', 'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name',
|
|
67
67
|
'index', 'relxy', 'tc', 'nk', 'concentration', 'antibody', 'cell_type', 'pharmaceutical_agent',
|
|
68
68
|
'reference_population', 'neighbor_population']
|
|
69
|
-
|
|
69
|
+
meta = get_experiment_metadata(self.exp_dir)
|
|
70
|
+
if meta is not None:
|
|
71
|
+
keys = list(meta.keys())
|
|
72
|
+
self.cols_to_remove.extend(keys)
|
|
70
73
|
|
|
71
74
|
# Read instructions from target block for now...
|
|
72
75
|
self.mode = "neighborhood"
|
|
@@ -202,14 +205,14 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
202
205
|
|
|
203
206
|
self.cell_events_hbox = QHBoxLayout()
|
|
204
207
|
self.cell_events_hbox.setContentsMargins(0,0,0,0)
|
|
205
|
-
self.cell_events_hbox.addWidget(QLabel('
|
|
208
|
+
self.cell_events_hbox.addWidget(QLabel('Event: '), 25)
|
|
206
209
|
self.reference_event_choice_cb = QComboBox()
|
|
207
210
|
self.cell_events_hbox.addWidget(self.reference_event_choice_cb, 75)
|
|
208
211
|
|
|
209
212
|
#if 'self' not in self.neighborhood_choice_cb.currentText():
|
|
210
213
|
self.neigh_cell_events_hbox = QHBoxLayout()
|
|
211
214
|
self.neigh_cell_events_hbox.setContentsMargins(0,0,0,0)
|
|
212
|
-
self.neigh_lab=QLabel('
|
|
215
|
+
self.neigh_lab=QLabel('Event: ')
|
|
213
216
|
self.neigh_cell_events_hbox.addWidget(self.neigh_lab, 25)
|
|
214
217
|
self.neighbor_event_choice_cb = QComboBox()
|
|
215
218
|
self.neigh_cell_events_hbox.addWidget(self.neighbor_event_choice_cb, 75)
|
|
@@ -230,7 +233,7 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
230
233
|
neighbor_layout.addLayout(self.neigh_cell_events_hbox)
|
|
231
234
|
|
|
232
235
|
self.cell_info_hbox.addLayout(reference_layout, 33)
|
|
233
|
-
self.cell_info_hbox.addWidget(self.pair_info, 33
|
|
236
|
+
self.cell_info_hbox.addWidget(self.pair_info, 33)
|
|
234
237
|
self.cell_info_hbox.addLayout(neighbor_layout, 33)
|
|
235
238
|
|
|
236
239
|
self.left_panel.addLayout(self.cell_info_hbox)
|
|
@@ -2375,41 +2378,41 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2375
2378
|
|
|
2376
2379
|
df_reference = self.dataframes[self.reference_population]
|
|
2377
2380
|
if self.reference_track_of_interest is not None:
|
|
2378
|
-
reference_cell_selected = f"
|
|
2379
|
-
reference_cell_population = f"
|
|
2381
|
+
reference_cell_selected = f"Ref #{self.reference_track_of_interest}\n"
|
|
2382
|
+
reference_cell_population = f"Pop: {self.reference_population}\n"
|
|
2380
2383
|
#reference_cell_class = f"class: {df_reference[df_reference['TRACK_ID']==self.reference_track_of_interest, self.reference_event_choice_cb.currentText()].values[0]}\n"
|
|
2381
2384
|
#reference_cell_time = f"time of interest: {df_reference[df_reference['TRACK_ID']==self.reference_track_of_interest, ''].values[0]}\n"
|
|
2382
2385
|
self.reference_cell_info.setText(reference_cell_selected+reference_cell_population)
|
|
2383
2386
|
else:
|
|
2384
|
-
reference_cell_selected = f"
|
|
2385
|
-
reference_cell_population = f"
|
|
2387
|
+
reference_cell_selected = f"Ref: None\n"
|
|
2388
|
+
reference_cell_population = f"Pop: {self.reference_population}\n"
|
|
2386
2389
|
self.reference_cell_info.setText(reference_cell_selected+reference_cell_population)
|
|
2387
2390
|
|
|
2388
2391
|
def give_neighbor_cell_information(self):
|
|
2389
2392
|
|
|
2390
2393
|
if self.neighbor_track_of_interest is not None:
|
|
2391
|
-
neighbor_cell_selected = f"
|
|
2392
|
-
neighbor_cell_population = f"
|
|
2394
|
+
neighbor_cell_selected = f"Neigh #{self.neighbor_track_of_interest}\n"
|
|
2395
|
+
neighbor_cell_population = f"Pop: {self.neighbor_population}\n"
|
|
2393
2396
|
#neighbor_cell_time = f"time of interest: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest), self.pair_time_name].to_numpy()[0]}\n"
|
|
2394
2397
|
#neighbor_cell_class = f"class: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest), self.pair_class_name].to_numpy()[0]}\n"
|
|
2395
2398
|
self.neighbor_cell_info.setText(neighbor_cell_selected+neighbor_cell_population) #neighbor_cell_class+neighbor_cell_time
|
|
2396
2399
|
else:
|
|
2397
|
-
neighbor_cell_selected = f"
|
|
2398
|
-
neighbor_cell_population = f"
|
|
2400
|
+
neighbor_cell_selected = f"Neigh: None\n"
|
|
2401
|
+
neighbor_cell_population = f"Pop: {self.neighbor_population}\n"
|
|
2399
2402
|
self.neighbor_cell_info.setText(neighbor_cell_selected+neighbor_cell_population)
|
|
2400
2403
|
|
|
2401
2404
|
def give_pair_information(self):
|
|
2402
2405
|
|
|
2403
2406
|
if self.neighbor_track_of_interest is not None and self.reference_track_of_interest is not None:
|
|
2404
|
-
pair_selected = f"
|
|
2405
|
-
pair_populations = f"populations: ({self.reference_population}, {self.neighbor_population})\n"
|
|
2407
|
+
pair_selected = f"Pair: ({self.reference_track_of_interest},{self.neighbor_track_of_interest})\n"
|
|
2408
|
+
pair_populations = "" #f"populations: ({self.reference_population}, {self.neighbor_population})\n"
|
|
2406
2409
|
current_class = self.relative_class_choice_cb.currentText()
|
|
2407
|
-
pair_class = f"
|
|
2408
|
-
pair_time = f"
|
|
2410
|
+
pair_class = f"Event class: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population)&(~self.df_relative['status_'+self.current_neighborhood].isnull()), current_class].values[0]}\n"
|
|
2411
|
+
pair_time = f"Time: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population)&(~self.df_relative['status_'+self.current_neighborhood].isnull()), self.pair_time_name].values[0]}\n"
|
|
2409
2412
|
self.pair_info.setText(pair_selected+pair_populations+pair_class+pair_time)
|
|
2410
2413
|
else:
|
|
2411
|
-
pair_selected = f"
|
|
2412
|
-
pair_populations = f"populations: ({self.reference_population}, {self.neighbor_population})\n"
|
|
2414
|
+
pair_selected = f"Pair: None\n"
|
|
2415
|
+
pair_populations = "" #f"populations: ({self.reference_population}, {self.neighbor_population})\n"
|
|
2413
2416
|
self.pair_info.setText(pair_selected+pair_populations)
|
|
2414
2417
|
|
|
2415
2418
|
|
celldetective/gui/survival_ui.py
CHANGED
|
@@ -11,11 +11,9 @@ import os
|
|
|
11
11
|
import matplotlib.pyplot as plt
|
|
12
12
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
13
13
|
from glob import glob
|
|
14
|
-
import pandas as pd
|
|
15
14
|
from celldetective.gui import Styles
|
|
16
15
|
from matplotlib import colormaps
|
|
17
16
|
from celldetective.events import compute_survival
|
|
18
|
-
from natsort import natsorted
|
|
19
17
|
from celldetective.relative_measurements import expand_pair_table
|
|
20
18
|
import matplotlib.cm
|
|
21
19
|
from celldetective.neighborhood import extract_neighborhood_in_pair_table
|
|
@@ -3,7 +3,8 @@ from PyQt5.QtWidgets import QAction, QMenu, QMainWindow, QMessageBox, QLabel, QW
|
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator
|
|
4
4
|
|
|
5
5
|
from celldetective.filters import std_filter, gauss_filter
|
|
6
|
-
from celldetective.gui.gui_utils import center_window, FigureCanvas,
|
|
6
|
+
from celldetective.gui.gui_utils import center_window, FigureCanvas, color_from_class, help_generic
|
|
7
|
+
from celldetective.gui.gui_utils import PreprocessingLayout
|
|
7
8
|
from celldetective.utils import get_software_location, extract_experiment_channels, rename_intensity_column, estimate_unreliable_edge
|
|
8
9
|
from celldetective.io import auto_load_number_of_frames, load_frames
|
|
9
10
|
from celldetective.segmentation import threshold_image, identify_markers_from_binary, apply_watershed
|
|
@@ -23,6 +24,7 @@ import os
|
|
|
23
24
|
|
|
24
25
|
from celldetective.gui import Styles
|
|
25
26
|
|
|
27
|
+
|
|
26
28
|
class ThresholdConfigWizard(QMainWindow, Styles):
|
|
27
29
|
"""
|
|
28
30
|
UI to create a threshold pipeline for segmentation.
|
|
@@ -140,53 +142,8 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
140
142
|
|
|
141
143
|
def populate_left_panel(self):
|
|
142
144
|
|
|
143
|
-
self.
|
|
144
|
-
|
|
145
|
-
grid_preprocess = QGridLayout()
|
|
146
|
-
grid_preprocess.setContentsMargins(20, 20, 20, 20)
|
|
147
|
-
|
|
148
|
-
filter_list_option_grid = QHBoxLayout()
|
|
149
|
-
section_preprocess = QLabel("Preprocessing")
|
|
150
|
-
section_preprocess.setStyleSheet("font-weight: bold;")
|
|
151
|
-
filter_list_option_grid.addWidget(section_preprocess, 90, alignment=Qt.AlignLeft)
|
|
152
|
-
|
|
153
|
-
self.delete_filter = QPushButton("")
|
|
154
|
-
self.delete_filter.setStyleSheet(self.button_select_all)
|
|
155
|
-
self.delete_filter.setIcon(icon(MDI6.trash_can, color="black"))
|
|
156
|
-
self.delete_filter.setToolTip("Remove filter")
|
|
157
|
-
self.delete_filter.setIconSize(QSize(20, 20))
|
|
158
|
-
self.delete_filter.clicked.connect(self.filters_qlist.removeSel)
|
|
159
|
-
|
|
160
|
-
self.add_filter = QPushButton("")
|
|
161
|
-
self.add_filter.setStyleSheet(self.button_select_all)
|
|
162
|
-
self.add_filter.setIcon(icon(MDI6.filter_plus, color="black"))
|
|
163
|
-
self.add_filter.setToolTip("Add filter")
|
|
164
|
-
self.add_filter.setIconSize(QSize(20, 20))
|
|
165
|
-
self.add_filter.clicked.connect(self.filters_qlist.addItem)
|
|
166
|
-
|
|
167
|
-
self.help_prefilter_btn = QPushButton()
|
|
168
|
-
self.help_prefilter_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
|
|
169
|
-
self.help_prefilter_btn.setIconSize(QSize(20, 20))
|
|
170
|
-
self.help_prefilter_btn.clicked.connect(self.help_prefilter)
|
|
171
|
-
self.help_prefilter_btn.setStyleSheet(self.button_select_all)
|
|
172
|
-
self.help_prefilter_btn.setToolTip("Help.")
|
|
173
|
-
|
|
174
|
-
# filter_list_option_grid.addWidget(QLabel(""),90)
|
|
175
|
-
filter_list_option_grid.addWidget(self.delete_filter, 5)
|
|
176
|
-
filter_list_option_grid.addWidget(self.add_filter, 5)
|
|
177
|
-
filter_list_option_grid.addWidget(self.help_prefilter_btn, 5)
|
|
178
|
-
|
|
179
|
-
grid_preprocess.addLayout(filter_list_option_grid, 0, 0, 1, 3)
|
|
180
|
-
grid_preprocess.addWidget(self.filters_qlist, 1, 0, 1, 3)
|
|
181
|
-
|
|
182
|
-
self.apply_filters_btn = QPushButton("Apply")
|
|
183
|
-
self.apply_filters_btn.setIcon(icon(MDI6.filter_cog_outline, color="white"))
|
|
184
|
-
self.apply_filters_btn.setIconSize(QSize(20, 20))
|
|
185
|
-
self.apply_filters_btn.setStyleSheet(self.button_style_sheet)
|
|
186
|
-
self.apply_filters_btn.clicked.connect(self.preprocess_image)
|
|
187
|
-
grid_preprocess.addWidget(self.apply_filters_btn, 2, 0, 1, 3)
|
|
188
|
-
|
|
189
|
-
self.left_panel.addLayout(grid_preprocess)
|
|
145
|
+
self.preprocessing = PreprocessingLayout(self)
|
|
146
|
+
self.left_panel.addLayout(self.preprocessing)
|
|
190
147
|
|
|
191
148
|
###################
|
|
192
149
|
# THRESHOLD SECTION
|
|
@@ -484,7 +441,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
484
441
|
self.fcanvas = FigureCanvas(self.fig, interactive=True)
|
|
485
442
|
self.ax.clear()
|
|
486
443
|
|
|
487
|
-
self.im = self.ax.imshow(self.img, cmap='gray')
|
|
444
|
+
self.im = self.ax.imshow(self.img, cmap='gray', interpolation='none')
|
|
488
445
|
|
|
489
446
|
self.binary = threshold_image(self.img, self.threshold_slider.value()[0], self.threshold_slider.value()[1],
|
|
490
447
|
foreground_value=1., fill_holes=True, edge_exclusion=None)
|
|
@@ -640,7 +597,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
640
597
|
"""
|
|
641
598
|
|
|
642
599
|
self.reload_frame()
|
|
643
|
-
filters = self.
|
|
600
|
+
filters = self.preprocessing.list.items
|
|
644
601
|
self.edge = estimate_unreliable_edge(filters)
|
|
645
602
|
self.img = filter_image(self.img, filters)
|
|
646
603
|
self.refresh_imshow()
|
|
@@ -853,7 +810,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
853
810
|
instructions = {
|
|
854
811
|
"target_channel": self.channels_cb.currentText(), # for now index but would be more universal to use name
|
|
855
812
|
"thresholds": self.threshold_slider.value(),
|
|
856
|
-
"filters": self.
|
|
813
|
+
"filters": self.preprocessing.list.items,
|
|
857
814
|
"marker_min_distance": self.min_dist,
|
|
858
815
|
"marker_footprint_size": self.footprint,
|
|
859
816
|
"feature_queries": [self.property_query_le.text()],
|
|
@@ -906,7 +863,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
906
863
|
items_to_add = [f[0] + '_filter' for f in filters]
|
|
907
864
|
self.filters_qlist.list_widget.clear()
|
|
908
865
|
self.filters_qlist.list_widget.addItems(items_to_add)
|
|
909
|
-
self.
|
|
866
|
+
self.preprocessing.list.items = filters
|
|
910
867
|
|
|
911
868
|
self.apply_filters_btn.click()
|
|
912
869
|
|
celldetective/gui/viewers.py
CHANGED
|
@@ -14,7 +14,7 @@ import os
|
|
|
14
14
|
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QLabel, QComboBox, QLineEdit, QListWidget, QShortcut
|
|
15
15
|
from PyQt5.QtCore import Qt, QSize
|
|
16
16
|
from PyQt5.QtGui import QKeySequence, QDoubleValidator
|
|
17
|
-
from celldetective.gui.gui_utils import FigureCanvas, center_window, QuickSliderLayout, QHSeperationLine, ThresholdLineEdit
|
|
17
|
+
from celldetective.gui.gui_utils import FigureCanvas, center_window, QuickSliderLayout, QHSeperationLine, ThresholdLineEdit, PreprocessingLayout2
|
|
18
18
|
from celldetective.gui import Styles
|
|
19
19
|
from superqt import QLabeledDoubleSlider, QLabeledSlider, QLabeledDoubleRangeSlider
|
|
20
20
|
from superqt.fonticon import icon
|
|
@@ -626,21 +626,26 @@ class CellEdgeVisualizer(StackVisualizer):
|
|
|
626
626
|
|
|
627
627
|
class SpotDetectionVisualizer(StackVisualizer):
|
|
628
628
|
|
|
629
|
-
def __init__(self, parent_channel_cb=None, parent_diameter_le=None, parent_threshold_le=None, cell_type='targets', labels=None, *args, **kwargs):
|
|
629
|
+
def __init__(self, parent_channel_cb=None, parent_diameter_le=None, parent_threshold_le=None, parent_preprocessing_list=None, cell_type='targets', labels=None, *args, **kwargs):
|
|
630
630
|
|
|
631
631
|
super().__init__(*args, **kwargs)
|
|
632
632
|
|
|
633
633
|
self.cell_type = cell_type
|
|
634
634
|
self.labels = labels
|
|
635
635
|
self.detection_channel = self.target_channel
|
|
636
|
+
|
|
636
637
|
self.parent_channel_cb = parent_channel_cb
|
|
637
638
|
self.parent_diameter_le = parent_diameter_le
|
|
638
639
|
self.parent_threshold_le = parent_threshold_le
|
|
639
|
-
self.
|
|
640
|
+
self.parent_preprocessing_list = parent_preprocessing_list
|
|
640
641
|
|
|
642
|
+
self.spot_sizes = []
|
|
641
643
|
self.floatValidator = QDoubleValidator()
|
|
642
644
|
self.init_scatter()
|
|
643
|
-
|
|
645
|
+
|
|
646
|
+
self.generate_detection_channel()
|
|
647
|
+
self.detection_channel = self.detection_channel_cb.currentIndex()
|
|
648
|
+
|
|
644
649
|
self.generate_spot_detection_params()
|
|
645
650
|
self.generate_add_measurement_btn()
|
|
646
651
|
self.load_labels()
|
|
@@ -663,10 +668,10 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
663
668
|
|
|
664
669
|
# Data-to-pixel scale
|
|
665
670
|
ax_width_in_pixels = self.ax.bbox.width
|
|
666
|
-
ax_height_in_pixels =
|
|
671
|
+
ax_height_in_pixels =self.ax.bbox.height
|
|
667
672
|
|
|
668
|
-
x_scale = (xlim[1] - xlim[0]) / ax_width_in_pixels
|
|
669
|
-
y_scale = (ylim[1] - ylim[0]) / ax_height_in_pixels
|
|
673
|
+
x_scale = (float(xlim[1]) - float(xlim[0])) / ax_width_in_pixels
|
|
674
|
+
y_scale = (float(ylim[1]) - float(ylim[0])) / ax_height_in_pixels
|
|
670
675
|
|
|
671
676
|
# Choose the smaller scale for square pixels
|
|
672
677
|
scale = min(x_scale, y_scale)
|
|
@@ -674,7 +679,7 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
674
679
|
# Convert radius_px to data units
|
|
675
680
|
if len(self.spot_sizes)>0:
|
|
676
681
|
|
|
677
|
-
radius_data_units = self.spot_sizes / scale
|
|
682
|
+
radius_data_units = self.spot_sizes / float(scale)
|
|
678
683
|
|
|
679
684
|
# Convert to scatter `s` size (points squared)
|
|
680
685
|
radius_pts = radius_data_units * (72. / self.fig.dpi )
|
|
@@ -697,8 +702,7 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
697
702
|
if self.mode=='virtual':
|
|
698
703
|
self.init_label = imread(self.mask_paths[value])
|
|
699
704
|
self.target_img = load_frames(self.img_num_per_channel[self.detection_channel, value],
|
|
700
|
-
self.stack_path,
|
|
701
|
-
normalize_input=False).astype(float)[:,:,0]
|
|
705
|
+
self.stack_path,normalize_input=False).astype(float)[:,:,0]
|
|
702
706
|
elif self.mode=='direct':
|
|
703
707
|
self.init_label = self.labels[value,:,:]
|
|
704
708
|
self.target_img = self.stack[value,:,:,self.detection_channel].copy()
|
|
@@ -707,18 +711,28 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
707
711
|
|
|
708
712
|
self.reset_detection()
|
|
709
713
|
self.control_valid_parameters() # set current diam and threshold
|
|
710
|
-
|
|
714
|
+
#self.change_frame(self.frame_slider.value())
|
|
715
|
+
#self.set_detection_channel_index(self.detection_channel_cb.currentIndex())
|
|
716
|
+
|
|
717
|
+
image_preprocessing = self.preprocessing.list.items
|
|
718
|
+
if image_preprocessing==[]:
|
|
719
|
+
image_preprocessing = None
|
|
720
|
+
|
|
721
|
+
blobs_filtered = extract_blobs_in_image(self.target_img, self.init_label,threshold=self.thresh, diameter=self.diameter, image_preprocessing=image_preprocessing)
|
|
711
722
|
if blobs_filtered is not None:
|
|
712
723
|
self.spot_positions = np.array([[x,y] for y,x,_ in blobs_filtered])
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
print(f"{self.spot_sizes=}")
|
|
724
|
+
if len(self.spot_positions)>0:
|
|
725
|
+
self.spot_sizes = np.sqrt(2)*np.array([sig for _,_,sig in blobs_filtered])
|
|
716
726
|
#radius_pts = self.spot_sizes * (self.fig.dpi / 72.0)
|
|
717
727
|
#sizes = np.pi*(radius_pts**2)
|
|
718
|
-
|
|
719
|
-
|
|
728
|
+
if len(self.spot_positions)>0:
|
|
729
|
+
self.spot_scat.set_offsets(self.spot_positions)
|
|
730
|
+
else:
|
|
731
|
+
empty_offset = np.ma.masked_array([0, 0], mask=True)
|
|
732
|
+
self.spot_scat.set_offsets(empty_offset)
|
|
720
733
|
#self.spot_scat.set_sizes(sizes)
|
|
721
|
-
self.
|
|
734
|
+
if len(self.spot_positions)>0:
|
|
735
|
+
self.update_marker_sizes()
|
|
722
736
|
self.canvas.canvas.draw()
|
|
723
737
|
|
|
724
738
|
def reset_detection(self):
|
|
@@ -778,17 +792,34 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
778
792
|
self.detection_channel_cb.addItems(self.channel_names)
|
|
779
793
|
self.detection_channel_cb.currentIndexChanged.connect(self.set_detection_channel_index)
|
|
780
794
|
channel_layout.addWidget(self.detection_channel_cb, 75)
|
|
795
|
+
|
|
796
|
+
# self.invert_check = QCheckBox('invert')
|
|
797
|
+
# if self.invert:
|
|
798
|
+
# self.invert_check.setChecked(True)
|
|
799
|
+
# self.invert_check.toggled.connect(self.set_invert)
|
|
800
|
+
# channel_layout.addWidget(self.invert_check, 10)
|
|
801
|
+
|
|
781
802
|
self.canvas.layout.addLayout(channel_layout)
|
|
803
|
+
|
|
804
|
+
self.preprocessing = PreprocessingLayout2(fraction=25, parent_window=self)
|
|
805
|
+
self.preprocessing.setContentsMargins(15,0,15,0)
|
|
806
|
+
self.canvas.layout.addLayout(self.preprocessing)
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
# def set_invert(self):
|
|
810
|
+
# if self.invert_check.isChecked():
|
|
811
|
+
# self.invert = True
|
|
812
|
+
# else:
|
|
813
|
+
# self.invert = False
|
|
782
814
|
|
|
783
815
|
def set_detection_channel_index(self, value):
|
|
784
816
|
|
|
785
817
|
self.detection_channel = value
|
|
786
818
|
if self.mode == 'direct':
|
|
787
|
-
self.
|
|
819
|
+
self.target_img = self.stack[-1,:,:,self.detection_channel]
|
|
788
820
|
elif self.mode == 'virtual':
|
|
789
|
-
self.target_img = load_frames(self.img_num_per_channel[self.detection_channel, self.
|
|
790
|
-
self.stack_path,
|
|
791
|
-
normalize_input=False).astype(float)[:,:,0]
|
|
821
|
+
self.target_img = load_frames(self.img_num_per_channel[self.detection_channel, self.frame_slider.value()],
|
|
822
|
+
self.stack_path,normalize_input=False).astype(float)[:,:,0]
|
|
792
823
|
|
|
793
824
|
def generate_spot_detection_params(self):
|
|
794
825
|
|
|
@@ -865,6 +896,12 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
865
896
|
self.parent_diameter_le.setText(self.spot_diam_le.text())
|
|
866
897
|
if self.parent_threshold_le is not None:
|
|
867
898
|
self.parent_threshold_le.setText(self.spot_thresh_le.text())
|
|
899
|
+
if self.parent_preprocessing_list is not None:
|
|
900
|
+
self.parent_preprocessing_list.clear()
|
|
901
|
+
items = self.preprocessing.list.getItems()
|
|
902
|
+
for item in items:
|
|
903
|
+
self.parent_preprocessing_list.addItemToList(item)
|
|
904
|
+
self.parent_preprocessing_list.items = self.preprocessing.list.items
|
|
868
905
|
self.close()
|
|
869
906
|
|
|
870
907
|
class CellSizeViewer(StackVisualizer):
|