celldetective 1.3.5__py3-none-any.whl → 1.3.6.post1__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 +11 -0
- 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 +136 -6
- celldetective/gui/measurement_options.py +84 -24
- celldetective/gui/signal_annotator.py +6 -1
- celldetective/gui/signal_annotator2.py +5 -2
- celldetective/gui/survival_ui.py +0 -2
- celldetective/gui/thresholds_gui.py +9 -52
- celldetective/gui/viewers.py +58 -21
- celldetective/io.py +30 -7
- celldetective/measure.py +132 -157
- celldetective/scripts/track_cells.py +25 -1
- celldetective/tracking.py +78 -53
- celldetective/utils.py +5 -2
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post1.dist-info}/METADATA +2 -1
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post1.dist-info}/RECORD +24 -24
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post1.dist-info}/LICENSE +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post1.dist-info}/WHEEL +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.5.dist-info → celldetective-1.3.6.post1.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,
|
|
@@ -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"
|
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):
|
celldetective/io.py
CHANGED
|
@@ -25,7 +25,7 @@ from magicgui import magicgui
|
|
|
25
25
|
from pathlib import Path, PurePath
|
|
26
26
|
from shutil import copyfile, rmtree
|
|
27
27
|
|
|
28
|
-
from celldetective.utils import ConfigSectionMap, extract_experiment_channels, _extract_labels_from_config, get_zenodo_files, download_zenodo_file
|
|
28
|
+
from celldetective.utils import ConfigSectionMap, extract_experiment_channels, _extract_labels_from_config, get_zenodo_files, download_zenodo_file
|
|
29
29
|
from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
|
|
30
30
|
|
|
31
31
|
from stardist import fill_label_holes
|
|
@@ -211,7 +211,15 @@ def collect_experiment_metadata(pos_path=None, well_path=None):
|
|
|
211
211
|
antibodies = get_experiment_antibodies(experiment)
|
|
212
212
|
pharmaceutical_agents = get_experiment_pharmaceutical_agents(experiment)
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
dico = {"pos_path": pos_path, "position": pos_path, "pos_name": pos_name, "well_path": well_path, "well_name": well_name, "well_nbr": well_nbr, "experiment": experiment, "antibody": antibodies[idx], "concentration": concentrations[idx], "cell_type": cell_types[idx], "pharmaceutical_agent": pharmaceutical_agents[idx]}
|
|
215
|
+
|
|
216
|
+
meta = get_experiment_metadata(experiment) # None or dict of metadata
|
|
217
|
+
if meta is not None:
|
|
218
|
+
keys = list(meta.keys())
|
|
219
|
+
for k in keys:
|
|
220
|
+
dico.update({k: meta[k]})
|
|
221
|
+
|
|
222
|
+
return dico
|
|
215
223
|
|
|
216
224
|
|
|
217
225
|
def get_experiment_wells(experiment):
|
|
@@ -379,6 +387,12 @@ def get_temporal_calibration(experiment):
|
|
|
379
387
|
|
|
380
388
|
return FrameToMin
|
|
381
389
|
|
|
390
|
+
def get_experiment_metadata(experiment):
|
|
391
|
+
|
|
392
|
+
config = get_config(experiment)
|
|
393
|
+
metadata = ConfigSectionMap(config, "Metadata")
|
|
394
|
+
return metadata
|
|
395
|
+
|
|
382
396
|
|
|
383
397
|
def get_experiment_concentrations(experiment, dtype=str):
|
|
384
398
|
|
|
@@ -957,6 +971,7 @@ def load_experiment_tables(experiment, population='targets', well_option='*', po
|
|
|
957
971
|
cell_types = get_experiment_cell_types(experiment)
|
|
958
972
|
antibodies = get_experiment_antibodies(experiment)
|
|
959
973
|
pharmaceutical_agents = get_experiment_pharmaceutical_agents(experiment)
|
|
974
|
+
metadata = get_experiment_metadata(experiment) # None or dict of metadata
|
|
960
975
|
well_labels = _extract_labels_from_config(config, len(wells))
|
|
961
976
|
|
|
962
977
|
well_indices, position_indices = interpret_wells_and_positions(experiment, well_option, position_option)
|
|
@@ -1011,15 +1026,23 @@ def load_experiment_tables(experiment, population='targets', well_option='*', po
|
|
|
1011
1026
|
df_pos['antibody'] = well_antibody
|
|
1012
1027
|
df_pos['cell_type'] = well_cell_type
|
|
1013
1028
|
df_pos['pharmaceutical_agent'] = well_pharmaceutical_agent
|
|
1029
|
+
if metadata is not None:
|
|
1030
|
+
keys = list(metadata.keys())
|
|
1031
|
+
for k in keys:
|
|
1032
|
+
df_pos[k] = metadata[k]
|
|
1014
1033
|
|
|
1015
1034
|
df.append(df_pos)
|
|
1016
1035
|
any_table = True
|
|
1017
1036
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
'
|
|
1021
|
-
|
|
1022
|
-
|
|
1037
|
+
pos_dict = {'pos_path': pos_path, 'pos_index': real_pos_index, 'pos_name': pos_name, 'table_path': table,
|
|
1038
|
+
'stack_path': stack_path,'well_path': well_path, 'well_index': real_well_index, 'well_name': well_name,
|
|
1039
|
+
'well_number': well_number, 'well_alias': well_alias}
|
|
1040
|
+
# if metadata is not None:
|
|
1041
|
+
# keys = list(metadata.keys())
|
|
1042
|
+
# for k in keys:
|
|
1043
|
+
# pos_dict.update({k: metadata[k]})
|
|
1044
|
+
|
|
1045
|
+
df_pos_info.append(pos_dict)
|
|
1023
1046
|
|
|
1024
1047
|
real_pos_index += 1
|
|
1025
1048
|
|