celldetective 1.4.2__py3-none-any.whl → 1.5.0b0__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/__init__.py +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +304 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +197 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
- celldetective-1.5.0b0.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- /celldetective/{gui/processes → processes}/downloader.py +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
from ._settings_measurements import SettingsMeasurements
|
|
2
|
-
from ._settings_segmentation import SettingsSegmentation
|
|
3
|
-
from ._settings_tracking import SettingsTracking
|
|
4
|
-
from ._settings_segmentation_model_training import SettingsSegmentationModelTraining
|
|
5
|
-
from ._settings_event_model_training import SettingsEventDetectionModelTraining
|
|
6
|
-
from ._settings_signal_annotator import SettingsSignalAnnotator
|
|
7
|
-
from ._settings_neighborhood import SettingsNeighborhood
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from PyQt5.QtCore import QSize, Qt
|
|
5
|
+
from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QComboBox, QHBoxLayout, QLabel
|
|
6
|
+
from fonticon_mdi6 import MDI6
|
|
7
|
+
from superqt import QLabeledDoubleSlider
|
|
8
|
+
from superqt.fonticon import icon
|
|
9
|
+
|
|
10
|
+
from celldetective.gui.base.components import CelldetectiveWidget
|
|
11
|
+
from celldetective.gui.base.utils import center_window
|
|
12
|
+
from celldetective.gui.gui_utils import ThresholdLineEdit
|
|
13
|
+
from celldetective.gui.viewers.size_viewer import CellSizeViewer
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CellposeParamsWidget(CelldetectiveWidget):
|
|
17
|
+
"""
|
|
18
|
+
A widget to configure parameters for Cellpose segmentation, allowing users to set the cell diameter,
|
|
19
|
+
select imaging channels, and adjust flow and cell probability thresholds for cell detection.
|
|
20
|
+
|
|
21
|
+
This widget is designed for estimating cell diameters and configuring parameters for Cellpose,
|
|
22
|
+
a deep learning-based segmentation tool. It also provides functionality to preview the image stack with a scale bar.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
parent_window : QWidget, optional
|
|
27
|
+
The parent window that hosts the widget (default is None).
|
|
28
|
+
model_name : str, optional
|
|
29
|
+
The name of the Cellpose model being used, typically 'CP_cyto2' for cytoplasm or 'CP_nuclei' for nuclei segmentation
|
|
30
|
+
(default is 'CP_cyto2').
|
|
31
|
+
|
|
32
|
+
Notes
|
|
33
|
+
-----
|
|
34
|
+
- This widget assumes that the parent window or one of its ancestor windows has access to the experiment channels
|
|
35
|
+
and can locate the current image stack via `locate_image()`.
|
|
36
|
+
- This class integrates sliders for flow and cell probability thresholds, as well as a channel selection for running
|
|
37
|
+
Cellpose segmentation.
|
|
38
|
+
- The `view_current_stack_with_scale_bar()` method opens a new window where the user can visually inspect the
|
|
39
|
+
image stack with a superimposed scale bar, to better estimate the cell diameter.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
view_diameter_btn: QPushButton = QPushButton()
|
|
44
|
+
diameter_le: ThresholdLineEdit
|
|
45
|
+
viewer: CellSizeViewer
|
|
46
|
+
cellpose_channel_cb: List[QComboBox]
|
|
47
|
+
cellpose_channel_template: List[str]
|
|
48
|
+
flow_slider: QLabeledDoubleSlider = QLabeledDoubleSlider()
|
|
49
|
+
set_cellpose_scale_btn: QPushButton = QPushButton("set")
|
|
50
|
+
cellprob_slider: QLabeledDoubleSlider = QLabeledDoubleSlider()
|
|
51
|
+
|
|
52
|
+
def __init__(self, parent_window=None, model_name="CP_cyto2", *args):
|
|
53
|
+
|
|
54
|
+
super().__init__(*args)
|
|
55
|
+
self.setWindowTitle("Estimate diameter")
|
|
56
|
+
self.parent_window = parent_window
|
|
57
|
+
self.model_name = model_name
|
|
58
|
+
|
|
59
|
+
# Setting up references to parent window attributes
|
|
60
|
+
if hasattr(self.parent_window.parent_window, "locate_image"):
|
|
61
|
+
self.attr_parent = self.parent_window.parent_window
|
|
62
|
+
elif hasattr(self.parent_window.parent_window.parent_window, "locate_image"):
|
|
63
|
+
self.attr_parent = self.parent_window.parent_window.parent_window
|
|
64
|
+
else:
|
|
65
|
+
self.attr_parent = (
|
|
66
|
+
self.parent_window.parent_window.parent_window.parent_window
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Layout and widgets setup
|
|
70
|
+
self.layout = QVBoxLayout()
|
|
71
|
+
self.populate_widgets()
|
|
72
|
+
self.setLayout(self.layout)
|
|
73
|
+
center_window(self)
|
|
74
|
+
|
|
75
|
+
def populate_widgets(self):
|
|
76
|
+
"""
|
|
77
|
+
Populates the widget with UI elements such as buttons, sliders, and comboboxes to allow configuration
|
|
78
|
+
of Cellpose segmentation parameters.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Button to view the current stack with a scale bar
|
|
82
|
+
self.view_diameter_btn.setStyleSheet(self.button_select_all)
|
|
83
|
+
self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
|
|
84
|
+
self.view_diameter_btn.setToolTip("View stack.")
|
|
85
|
+
self.view_diameter_btn.setIconSize(QSize(20, 20))
|
|
86
|
+
self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
|
|
87
|
+
|
|
88
|
+
# Line edit for entering cell diameter
|
|
89
|
+
self.diameter_le = ThresholdLineEdit(
|
|
90
|
+
init_value=40,
|
|
91
|
+
connected_buttons=[self.view_diameter_btn],
|
|
92
|
+
placeholder="cell diameter in pixels",
|
|
93
|
+
value_type="float",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Comboboxes for selecting imaging channels
|
|
97
|
+
self.cellpose_channel_cb = [QComboBox() for _ in range(2)]
|
|
98
|
+
self.cellpose_channel_template = ["brightfield_channel", "live_nuclei_channel"]
|
|
99
|
+
if self.model_name == "CP_nuclei":
|
|
100
|
+
self.cellpose_channel_template = ["live_nuclei_channel", "None"]
|
|
101
|
+
|
|
102
|
+
for k in range(2):
|
|
103
|
+
hbox_channel = QHBoxLayout()
|
|
104
|
+
hbox_channel.addWidget(QLabel(f"channel {k+1}: "))
|
|
105
|
+
hbox_channel.addWidget(self.cellpose_channel_cb[k])
|
|
106
|
+
if k == 1:
|
|
107
|
+
self.cellpose_channel_cb[k].addItems(
|
|
108
|
+
list(self.attr_parent.exp_channels) + ["None"]
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
self.cellpose_channel_cb[k].addItems(
|
|
112
|
+
list(self.attr_parent.exp_channels)
|
|
113
|
+
)
|
|
114
|
+
idx = self.cellpose_channel_cb[k].findText(
|
|
115
|
+
self.cellpose_channel_template[k]
|
|
116
|
+
)
|
|
117
|
+
if idx > 0:
|
|
118
|
+
self.cellpose_channel_cb[k].setCurrentIndex(idx)
|
|
119
|
+
else:
|
|
120
|
+
self.cellpose_channel_cb[k].setCurrentIndex(0)
|
|
121
|
+
|
|
122
|
+
if k == 1:
|
|
123
|
+
idx = self.cellpose_channel_cb[k].findText("None")
|
|
124
|
+
self.cellpose_channel_cb[k].setCurrentIndex(idx)
|
|
125
|
+
|
|
126
|
+
self.layout.addLayout(hbox_channel)
|
|
127
|
+
|
|
128
|
+
# Layout for diameter input and button
|
|
129
|
+
hbox = QHBoxLayout()
|
|
130
|
+
hbox.addWidget(QLabel("diameter [px]: "), 33)
|
|
131
|
+
hbox.addWidget(self.diameter_le, 61)
|
|
132
|
+
hbox.addWidget(self.view_diameter_btn)
|
|
133
|
+
self.layout.addLayout(hbox)
|
|
134
|
+
|
|
135
|
+
# Flow threshold slider
|
|
136
|
+
self.flow_slider.setOrientation(Qt.Horizontal)
|
|
137
|
+
self.flow_slider.setRange(-6, 6)
|
|
138
|
+
self.flow_slider.setValue(0.4)
|
|
139
|
+
hbox = QHBoxLayout()
|
|
140
|
+
hbox.addWidget(QLabel("flow threshold: "), 33)
|
|
141
|
+
hbox.addWidget(self.flow_slider, 66)
|
|
142
|
+
self.layout.addLayout(hbox)
|
|
143
|
+
|
|
144
|
+
# Cell probability threshold slider
|
|
145
|
+
self.cellprob_slider.setOrientation(Qt.Horizontal)
|
|
146
|
+
self.cellprob_slider.setRange(-6, 6)
|
|
147
|
+
self.cellprob_slider.setValue(0.0)
|
|
148
|
+
hbox = QHBoxLayout()
|
|
149
|
+
hbox.addWidget(QLabel("cellprob threshold: "), 33)
|
|
150
|
+
hbox.addWidget(self.cellprob_slider, 66)
|
|
151
|
+
self.layout.addLayout(hbox)
|
|
152
|
+
|
|
153
|
+
# Button to set the scale for Cellpose segmentation
|
|
154
|
+
self.set_cellpose_scale_btn.setStyleSheet(self.button_style_sheet)
|
|
155
|
+
self.set_cellpose_scale_btn.clicked.connect(
|
|
156
|
+
self.parent_window.set_cellpose_scale
|
|
157
|
+
)
|
|
158
|
+
self.layout.addWidget(self.set_cellpose_scale_btn)
|
|
159
|
+
|
|
160
|
+
def view_current_stack_with_scale_bar(self):
|
|
161
|
+
"""
|
|
162
|
+
Displays the current image stack with a scale bar, allowing users to visually estimate cell diameters.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
self.attr_parent.locate_image()
|
|
166
|
+
if self.attr_parent.current_stack is not None:
|
|
167
|
+
max_size = np.amax([self.attr_parent.shape_x, self.attr_parent.shape_y])
|
|
168
|
+
self.viewer = CellSizeViewer(
|
|
169
|
+
initial_diameter=float(self.diameter_le.text().replace(",", ".")),
|
|
170
|
+
parent_le=self.diameter_le,
|
|
171
|
+
stack_path=self.attr_parent.current_stack,
|
|
172
|
+
window_title=f"Position {self.attr_parent.position_list.currentText()}",
|
|
173
|
+
diameter_slider_range=(0, max_size),
|
|
174
|
+
frame_slider=True,
|
|
175
|
+
contrast_slider=True,
|
|
176
|
+
channel_cb=True,
|
|
177
|
+
channel_names=self.attr_parent.exp_channels,
|
|
178
|
+
n_channels=self.attr_parent.nbr_channels,
|
|
179
|
+
PxToUm=1,
|
|
180
|
+
)
|
|
181
|
+
self.viewer.show()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from PyQt5.QtCore import Qt
|
|
5
|
+
from PyQt5.QtGui import QDoubleValidator
|
|
6
|
+
from PyQt5.QtWidgets import QVBoxLayout, QComboBox, QHBoxLayout, QLabel, QPushButton
|
|
7
|
+
|
|
8
|
+
from celldetective.gui.base.components import CelldetectiveWidget
|
|
9
|
+
from celldetective.gui.base.utils import center_window
|
|
10
|
+
from celldetective.utils.model_loaders import locate_signal_model
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SignalModelParamsWidget(CelldetectiveWidget):
|
|
14
|
+
|
|
15
|
+
def __init__(self, parent_window=None, model_name=None, *args, **kwargs):
|
|
16
|
+
|
|
17
|
+
super().__init__(*args)
|
|
18
|
+
self.setWindowTitle("Signals")
|
|
19
|
+
self.parent_window = parent_window
|
|
20
|
+
self.model_name = model_name
|
|
21
|
+
self.locate_model_path()
|
|
22
|
+
self.required_channels = self.input_config["channels"]
|
|
23
|
+
self.onlyFloat = QDoubleValidator()
|
|
24
|
+
|
|
25
|
+
# Setting up references to parent window attributes
|
|
26
|
+
if hasattr(self.parent_window.parent_window, "locate_image"):
|
|
27
|
+
self.attr_parent = self.parent_window.parent_window
|
|
28
|
+
elif hasattr(self.parent_window.parent_window.parent_window, "locate_image"):
|
|
29
|
+
self.attr_parent = self.parent_window.parent_window.parent_window
|
|
30
|
+
else:
|
|
31
|
+
self.attr_parent = (
|
|
32
|
+
self.parent_window.parent_window.parent_window.parent_window
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Set up layout and widgets
|
|
36
|
+
self.layout = QVBoxLayout()
|
|
37
|
+
self.populate_widgets()
|
|
38
|
+
self.setLayout(self.layout)
|
|
39
|
+
center_window(self)
|
|
40
|
+
|
|
41
|
+
def locate_model_path(self):
|
|
42
|
+
|
|
43
|
+
self.model_complete_path = locate_signal_model(self.model_name)
|
|
44
|
+
if self.model_complete_path is None:
|
|
45
|
+
raise ValueError(f"Model {self.model_name} could not be found.")
|
|
46
|
+
else:
|
|
47
|
+
print(f"Model path: {self.model_complete_path}...")
|
|
48
|
+
|
|
49
|
+
config_path = os.path.join(self.model_complete_path, "config_input.json")
|
|
50
|
+
if not os.path.exists(config_path):
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"The configuration for the inputs to the model could not be located at {config_path}."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
with open(config_path) as config_file:
|
|
56
|
+
self.input_config = json.load(config_file)
|
|
57
|
+
|
|
58
|
+
def populate_widgets(self):
|
|
59
|
+
|
|
60
|
+
self.n_channels = len(self.required_channels)
|
|
61
|
+
self.channel_cbs = [QComboBox() for i in range(self.n_channels)]
|
|
62
|
+
|
|
63
|
+
self.parent_window.load_available_tables()
|
|
64
|
+
available_channels = list(self.parent_window.signals) + ["None"]
|
|
65
|
+
# Populate the comboboxes with available channels from the experiment
|
|
66
|
+
for k in range(self.n_channels):
|
|
67
|
+
hbox_channel = QHBoxLayout()
|
|
68
|
+
hbox_channel.addWidget(QLabel(f"channel {k+1}: "), 33)
|
|
69
|
+
|
|
70
|
+
ch_vbox = QVBoxLayout()
|
|
71
|
+
ch_vbox.addWidget(
|
|
72
|
+
QLabel(f"Req: {self.required_channels[k]}"), alignment=Qt.AlignLeft
|
|
73
|
+
)
|
|
74
|
+
ch_vbox.addWidget(self.channel_cbs[k])
|
|
75
|
+
|
|
76
|
+
self.channel_cbs[k].addItems(
|
|
77
|
+
available_channels
|
|
78
|
+
) # Give none option for more than one channel input
|
|
79
|
+
idx = self.channel_cbs[k].findText(self.required_channels[k])
|
|
80
|
+
|
|
81
|
+
if idx >= 0:
|
|
82
|
+
self.channel_cbs[k].setCurrentIndex(idx)
|
|
83
|
+
else:
|
|
84
|
+
self.channel_cbs[k].setCurrentIndex(len(available_channels) - 1)
|
|
85
|
+
|
|
86
|
+
hbox_channel.addLayout(ch_vbox, 66)
|
|
87
|
+
self.layout.addLayout(hbox_channel)
|
|
88
|
+
|
|
89
|
+
# Button to apply the StarDist settings
|
|
90
|
+
self.set_btn = QPushButton("set")
|
|
91
|
+
self.set_btn.setStyleSheet(self.button_style_sheet)
|
|
92
|
+
self.set_btn.clicked.connect(
|
|
93
|
+
self.parent_window.set_selected_signals_for_event_detection
|
|
94
|
+
)
|
|
95
|
+
self.layout.addWidget(self.set_btn)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from PyQt5.QtCore import QSize, Qt
|
|
6
|
+
from PyQt5.QtGui import QDoubleValidator
|
|
7
|
+
from PyQt5.QtWidgets import QVBoxLayout, QComboBox, QPushButton, QHBoxLayout, QLabel
|
|
8
|
+
from fonticon_mdi6 import MDI6
|
|
9
|
+
from superqt.fonticon import icon
|
|
10
|
+
|
|
11
|
+
from celldetective.gui.base.components import CelldetectiveWidget
|
|
12
|
+
from celldetective.gui.base.utils import center_window
|
|
13
|
+
from celldetective.gui.gui_utils import ThresholdLineEdit
|
|
14
|
+
from celldetective.gui.viewers.size_viewer import CellSizeViewer
|
|
15
|
+
from celldetective.utils.model_loaders import locate_segmentation_model
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SegModelParamsWidget(CelldetectiveWidget):
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self, parent_window=None, model_name="SD_versatile_fluo", *args, **kwargs
|
|
22
|
+
):
|
|
23
|
+
|
|
24
|
+
super().__init__(*args)
|
|
25
|
+
self.setWindowTitle("Channels")
|
|
26
|
+
self.parent_window = parent_window
|
|
27
|
+
self.model_name = model_name
|
|
28
|
+
self.locate_model_path()
|
|
29
|
+
self.required_channels = self.input_config["channels"]
|
|
30
|
+
self.onlyFloat = QDoubleValidator()
|
|
31
|
+
|
|
32
|
+
# Setting up references to parent window attributes
|
|
33
|
+
if hasattr(self.parent_window.parent_window, "locate_image"):
|
|
34
|
+
self.attr_parent = self.parent_window.parent_window
|
|
35
|
+
elif hasattr(self.parent_window.parent_window.parent_window, "locate_image"):
|
|
36
|
+
self.attr_parent = self.parent_window.parent_window.parent_window
|
|
37
|
+
else:
|
|
38
|
+
self.attr_parent = (
|
|
39
|
+
self.parent_window.parent_window.parent_window.parent_window
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Set up layout and widgets
|
|
43
|
+
self.layout = QVBoxLayout()
|
|
44
|
+
self.populate_widgets()
|
|
45
|
+
self.setLayout(self.layout)
|
|
46
|
+
center_window(self)
|
|
47
|
+
|
|
48
|
+
def locate_model_path(self):
|
|
49
|
+
|
|
50
|
+
self.model_complete_path = locate_segmentation_model(self.model_name)
|
|
51
|
+
if self.model_complete_path is None:
|
|
52
|
+
print("Model could not be found. Abort.")
|
|
53
|
+
self.abort_process()
|
|
54
|
+
else:
|
|
55
|
+
print(f"Model path: {self.model_complete_path}...")
|
|
56
|
+
|
|
57
|
+
if not os.path.exists(self.model_complete_path + "config_input.json"):
|
|
58
|
+
print(
|
|
59
|
+
"The configuration for the inputs to the model could not be located. Abort."
|
|
60
|
+
)
|
|
61
|
+
self.abort_process()
|
|
62
|
+
|
|
63
|
+
with open(self.model_complete_path + "config_input.json") as config_file:
|
|
64
|
+
self.input_config = json.load(config_file)
|
|
65
|
+
|
|
66
|
+
def populate_widgets(self):
|
|
67
|
+
|
|
68
|
+
self.n_channels = len(self.required_channels)
|
|
69
|
+
self.channel_cbs = [QComboBox() for i in range(self.n_channels)]
|
|
70
|
+
|
|
71
|
+
# Button to view the current stack with a scale bar
|
|
72
|
+
self.view_diameter_btn = QPushButton()
|
|
73
|
+
self.view_diameter_btn.setStyleSheet(self.button_select_all)
|
|
74
|
+
self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
|
|
75
|
+
self.view_diameter_btn.setToolTip("View stack.")
|
|
76
|
+
self.view_diameter_btn.setIconSize(QSize(20, 20))
|
|
77
|
+
self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
|
|
78
|
+
|
|
79
|
+
# Line edit for entering cell diameter
|
|
80
|
+
self.diameter_le = ThresholdLineEdit(
|
|
81
|
+
init_value=40,
|
|
82
|
+
connected_buttons=[self.view_diameter_btn],
|
|
83
|
+
placeholder="cell diameter in µm",
|
|
84
|
+
value_type="float",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
available_channels = list(self.attr_parent.exp_channels) + ["None"]
|
|
88
|
+
# Populate the comboboxes with available channels from the experiment
|
|
89
|
+
for k in range(self.n_channels):
|
|
90
|
+
hbox_channel = QHBoxLayout()
|
|
91
|
+
hbox_channel.addWidget(QLabel(f"channel {k+1}: "), 33)
|
|
92
|
+
|
|
93
|
+
ch_vbox = QVBoxLayout()
|
|
94
|
+
ch_vbox.addWidget(
|
|
95
|
+
QLabel(f"Req: {self.required_channels[k]}"), alignment=Qt.AlignLeft
|
|
96
|
+
)
|
|
97
|
+
ch_vbox.addWidget(self.channel_cbs[k])
|
|
98
|
+
|
|
99
|
+
self.channel_cbs[k].addItems(
|
|
100
|
+
available_channels
|
|
101
|
+
) # Give none option for more than one channel input
|
|
102
|
+
idx = self.channel_cbs[k].findText(self.required_channels[k])
|
|
103
|
+
|
|
104
|
+
if idx >= 0:
|
|
105
|
+
self.channel_cbs[k].setCurrentIndex(idx)
|
|
106
|
+
else:
|
|
107
|
+
self.channel_cbs[k].setCurrentIndex(len(available_channels) - 1)
|
|
108
|
+
|
|
109
|
+
hbox_channel.addLayout(ch_vbox, 66)
|
|
110
|
+
self.layout.addLayout(hbox_channel)
|
|
111
|
+
|
|
112
|
+
if "cell_size_um" in self.input_config:
|
|
113
|
+
|
|
114
|
+
# Layout for diameter input and button
|
|
115
|
+
hbox = QHBoxLayout()
|
|
116
|
+
hbox.addWidget(QLabel("cell size [µm]: "), 33)
|
|
117
|
+
hbox.addWidget(self.diameter_le, 61)
|
|
118
|
+
hbox.addWidget(self.view_diameter_btn)
|
|
119
|
+
self.layout.addLayout(hbox)
|
|
120
|
+
|
|
121
|
+
self.diameter_le.set_threshold(self.input_config["cell_size_um"])
|
|
122
|
+
|
|
123
|
+
# size_hbox = QHBoxLayout()
|
|
124
|
+
# size_hbox.addWidget(QLabel('cell size [µm]: '), 33)
|
|
125
|
+
# self.size_le = QLineEdit(str(self.input_config['cell_size_um']).replace('.',','))
|
|
126
|
+
# self.size_le.setValidator(self.onlyFloat)
|
|
127
|
+
# size_hbox.addWidget(self.size_le, 66)
|
|
128
|
+
# self.layout.addLayout(size_hbox)
|
|
129
|
+
|
|
130
|
+
# Button to apply the StarDist settings
|
|
131
|
+
self.set_btn = QPushButton("set")
|
|
132
|
+
self.set_btn.setStyleSheet(self.button_style_sheet)
|
|
133
|
+
self.set_btn.clicked.connect(
|
|
134
|
+
self.parent_window.set_selected_channels_for_segmentation
|
|
135
|
+
)
|
|
136
|
+
self.layout.addWidget(self.set_btn)
|
|
137
|
+
|
|
138
|
+
def view_current_stack_with_scale_bar(self):
|
|
139
|
+
"""
|
|
140
|
+
Displays the current image stack with a scale bar, allowing users to visually estimate cell diameters.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
self.attr_parent.locate_image()
|
|
144
|
+
if self.attr_parent.current_stack is not None:
|
|
145
|
+
max_size = np.amax([self.attr_parent.shape_x, self.attr_parent.shape_y])
|
|
146
|
+
self.viewer = CellSizeViewer(
|
|
147
|
+
initial_diameter=float(self.diameter_le.text().replace(",", ".")),
|
|
148
|
+
parent_le=self.diameter_le,
|
|
149
|
+
stack_path=self.attr_parent.current_stack,
|
|
150
|
+
window_title=f"Position {self.attr_parent.position_list.currentText()}",
|
|
151
|
+
diameter_slider_range=(0, max_size * self.attr_parent.PxToUm),
|
|
152
|
+
frame_slider=True,
|
|
153
|
+
contrast_slider=True,
|
|
154
|
+
channel_cb=True,
|
|
155
|
+
channel_names=self.attr_parent.exp_channels,
|
|
156
|
+
n_channels=self.attr_parent.nbr_channels,
|
|
157
|
+
PxToUm=self.attr_parent.PxToUm,
|
|
158
|
+
)
|
|
159
|
+
self.viewer.show()
|
|
@@ -1,70 +1,82 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from PyQt5.QtWidgets import
|
|
2
|
+
from PyQt5.QtWidgets import (
|
|
3
|
+
QApplication,
|
|
4
|
+
QScrollArea,
|
|
5
|
+
QSizePolicy,
|
|
6
|
+
QVBoxLayout,
|
|
7
|
+
QPushButton,
|
|
8
|
+
)
|
|
3
9
|
from PyQt5.QtCore import Qt
|
|
4
|
-
from celldetective
|
|
5
|
-
from celldetective.gui.
|
|
6
|
-
from celldetective.gui import
|
|
10
|
+
from celldetective import get_software_location
|
|
11
|
+
from celldetective.gui.base.utils import center_window
|
|
12
|
+
from celldetective.gui.base.components import (
|
|
13
|
+
CelldetectiveMainWindow,
|
|
14
|
+
CelldetectiveWidget,
|
|
15
|
+
)
|
|
7
16
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator
|
|
8
17
|
|
|
18
|
+
|
|
9
19
|
class CelldetectiveSettingsPanel(CelldetectiveMainWindow):
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
20
|
+
|
|
21
|
+
def __init__(self, title=""):
|
|
22
|
+
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.setWindowTitle(title)
|
|
25
|
+
|
|
26
|
+
self._get_screen_height()
|
|
27
|
+
# self.setMinimumWidth(500)
|
|
28
|
+
self.setMaximumHeight(int(0.8 * self._screen_height))
|
|
29
|
+
self._scroll_area = QScrollArea(self)
|
|
30
|
+
self._floatValidator = QDoubleValidator()
|
|
31
|
+
self._intValidator = QIntValidator()
|
|
32
|
+
self._software_path = get_software_location()
|
|
33
|
+
|
|
34
|
+
self._create_widgets()
|
|
35
|
+
self._build_layouts()
|
|
36
|
+
self.center_window()
|
|
37
|
+
|
|
38
|
+
def _create_widgets(self):
|
|
39
|
+
self.submit_btn: QPushButton = QPushButton("Save")
|
|
40
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
41
|
+
self.submit_btn.clicked.connect(self._write_instructions)
|
|
42
|
+
|
|
43
|
+
def center_window(self):
|
|
44
|
+
return center_window(self)
|
|
45
|
+
|
|
46
|
+
def _get_screen_height(self):
|
|
47
|
+
app = QApplication.instance()
|
|
48
|
+
screen = app.primaryScreen()
|
|
49
|
+
geometry = screen.availableGeometry()
|
|
50
|
+
self._screen_width, self._screen_height = geometry.getRect()[-2:]
|
|
51
|
+
|
|
52
|
+
def _adjust_size(self):
|
|
53
|
+
self._widget.adjustSize()
|
|
54
|
+
self._scroll_area.adjustSize()
|
|
55
|
+
self.adjustSize()
|
|
56
|
+
|
|
57
|
+
def _build_layouts(self):
|
|
58
|
+
|
|
59
|
+
self._layout: QVBoxLayout = QVBoxLayout()
|
|
60
|
+
self._widget: CelldetectiveWidget = CelldetectiveWidget()
|
|
61
|
+
self._widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
62
|
+
|
|
63
|
+
# Create button widget and layout
|
|
64
|
+
self._widget.setLayout(self._layout)
|
|
65
|
+
self._layout.setContentsMargins(30, 30, 30, 30)
|
|
66
|
+
|
|
67
|
+
self._scroll_area.setAlignment(Qt.AlignCenter)
|
|
68
|
+
self._scroll_area.setWidget(self._widget)
|
|
69
|
+
self._scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
70
|
+
self._scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
71
|
+
self._scroll_area.setWidgetResizable(True)
|
|
72
|
+
self.setCentralWidget(self._scroll_area)
|
|
73
|
+
|
|
74
|
+
QApplication.processEvents()
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def _load_previous_instructions(self):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
@abstractmethod
|
|
81
|
+
def _write_instructions(self):
|
|
82
|
+
pass
|