mosamatic2 2.0.15__py3-none-any.whl → 2.0.17__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.
Potentially problematic release.
This version of mosamatic2 might be problematic. Click here for more details.
- mosamatic2/cli.py +6 -0
- mosamatic2/commands/calculatemaskstatistics.py +59 -0
- mosamatic2/commands/defaultdockerpipeline.py +12 -1
- mosamatic2/commands/liveranalysispipeline.py +61 -0
- mosamatic2/commands/totalsegmentator.py +11 -2
- mosamatic2/core/pipelines/__init__.py +2 -1
- mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py +2 -9
- mosamatic2/core/pipelines/liveranalysispipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/liveranalysispipeline/liveranalysispipeline.py +48 -0
- mosamatic2/core/tasks/__init__.py +2 -1
- mosamatic2/core/tasks/calculatemaskstatisticstask/__init__.py +0 -0
- mosamatic2/core/tasks/calculatemaskstatisticstask/calculatemaskstatisticstask.py +104 -0
- mosamatic2/core/tasks/totalsegmentatortask/totalsegmentatortask.py +35 -10
- mosamatic2/ui/mainwindow.py +30 -0
- mosamatic2/ui/resources/VERSION +1 -1
- mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +1 -1
- mosamatic2/ui/widgets/panels/pipelines/liveranalysispipelinepanel.py +187 -0
- mosamatic2/ui/widgets/panels/tasks/calculatemaskstatisticstaskpanel.py +215 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +18 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +21 -3
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +36 -1
- {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/METADATA +4 -4
- {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/RECORD +25 -17
- {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/WHEEL +0 -0
- {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from PySide6.QtWidgets import (
|
|
4
|
+
QLineEdit,
|
|
5
|
+
QSpinBox,
|
|
6
|
+
QComboBox,
|
|
7
|
+
QCheckBox,
|
|
8
|
+
QHBoxLayout,
|
|
9
|
+
QVBoxLayout,
|
|
10
|
+
QFormLayout,
|
|
11
|
+
QPushButton,
|
|
12
|
+
QFileDialog,
|
|
13
|
+
QMessageBox,
|
|
14
|
+
)
|
|
15
|
+
from PySide6.QtCore import (
|
|
16
|
+
QThread,
|
|
17
|
+
Slot,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
21
|
+
from mosamatic2.ui.widgets.panels.pipelines.pipelinepanel import PipelinePanel
|
|
22
|
+
from mosamatic2.ui.settings import Settings
|
|
23
|
+
from mosamatic2.ui.utils import is_macos
|
|
24
|
+
from mosamatic2.ui.worker import Worker
|
|
25
|
+
from mosamatic2.core.pipelines import LiverAnalysisPipeline
|
|
26
|
+
|
|
27
|
+
LOG = LogManager()
|
|
28
|
+
|
|
29
|
+
PANEL_TITLE = 'LiverAnalysisPipeline'
|
|
30
|
+
PANEL_NAME = 'liveranalysispipelinepanel'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LiverAnalysisPipelinePanel(PipelinePanel):
|
|
34
|
+
def __init__(self):
|
|
35
|
+
super(LiverAnalysisPipelinePanel, self).__init__()
|
|
36
|
+
self.set_title(PANEL_TITLE)
|
|
37
|
+
self._scans_dir_line_edit = None
|
|
38
|
+
self._scans_dir_select_button = None
|
|
39
|
+
self._output_dir_line_edit = None
|
|
40
|
+
self._output_dir_select_button = None
|
|
41
|
+
self._overwrite_checkbox = None
|
|
42
|
+
self._form_layout = None
|
|
43
|
+
self._run_pipeline_button = None
|
|
44
|
+
self._settings = None
|
|
45
|
+
self._task = None
|
|
46
|
+
self._worker = None
|
|
47
|
+
self._thread = None
|
|
48
|
+
self.init_layout()
|
|
49
|
+
|
|
50
|
+
def scans_dir_line_edit(self):
|
|
51
|
+
if not self._scans_dir_line_edit:
|
|
52
|
+
self._scans_dir_line_edit = QLineEdit()
|
|
53
|
+
self._scans_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/scans_dir'))
|
|
54
|
+
return self._scans_dir_line_edit
|
|
55
|
+
|
|
56
|
+
def scans_dir_select_button(self):
|
|
57
|
+
if not self._scans_dir_select_button:
|
|
58
|
+
self._scans_dir_select_button = QPushButton('Select')
|
|
59
|
+
self._scans_dir_select_button.clicked.connect(self.handle_scans_dir_select_button)
|
|
60
|
+
return self._scans_dir_select_button
|
|
61
|
+
|
|
62
|
+
def output_dir_line_edit(self):
|
|
63
|
+
if not self._output_dir_line_edit:
|
|
64
|
+
self._output_dir_line_edit = QLineEdit()
|
|
65
|
+
self._output_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/output_dir'))
|
|
66
|
+
return self._output_dir_line_edit
|
|
67
|
+
|
|
68
|
+
def output_dir_select_button(self):
|
|
69
|
+
if not self._output_dir_select_button:
|
|
70
|
+
self._output_dir_select_button = QPushButton('Select')
|
|
71
|
+
self._output_dir_select_button.clicked.connect(self.handle_output_dir_select_button)
|
|
72
|
+
return self._output_dir_select_button
|
|
73
|
+
|
|
74
|
+
def overwrite_checkbox(self):
|
|
75
|
+
if not self._overwrite_checkbox:
|
|
76
|
+
self._overwrite_checkbox = QCheckBox('')
|
|
77
|
+
self._overwrite_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/overwrite', True))
|
|
78
|
+
return self._overwrite_checkbox
|
|
79
|
+
|
|
80
|
+
def form_layout(self):
|
|
81
|
+
if not self._form_layout:
|
|
82
|
+
self._form_layout = QFormLayout()
|
|
83
|
+
if is_macos():
|
|
84
|
+
self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
|
|
85
|
+
return self._form_layout
|
|
86
|
+
|
|
87
|
+
def run_pipeline_button(self):
|
|
88
|
+
if not self._run_pipeline_button:
|
|
89
|
+
self._run_pipeline_button = QPushButton('Run pipeline')
|
|
90
|
+
self._run_pipeline_button.clicked.connect(self.handle_run_pipeline_button)
|
|
91
|
+
return self._run_pipeline_button
|
|
92
|
+
|
|
93
|
+
def settings(self):
|
|
94
|
+
if not self._settings:
|
|
95
|
+
self._settings = Settings()
|
|
96
|
+
return self._settings
|
|
97
|
+
|
|
98
|
+
def init_help_dialog(self):
|
|
99
|
+
self.help_dialog().set_text('Show some help information')
|
|
100
|
+
|
|
101
|
+
def init_layout(self):
|
|
102
|
+
scans_dir_layout = QHBoxLayout()
|
|
103
|
+
scans_dir_layout.addWidget(self.scans_dir_line_edit())
|
|
104
|
+
scans_dir_layout.addWidget(self.scans_dir_select_button())
|
|
105
|
+
output_dir_layout = QHBoxLayout()
|
|
106
|
+
output_dir_layout.addWidget(self.output_dir_line_edit())
|
|
107
|
+
output_dir_layout.addWidget(self.output_dir_select_button())
|
|
108
|
+
self.form_layout().addRow('Scans directory', scans_dir_layout)
|
|
109
|
+
self.form_layout().addRow('Output directory', output_dir_layout)
|
|
110
|
+
self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
|
|
111
|
+
layout = QVBoxLayout()
|
|
112
|
+
layout.addLayout(self.form_layout())
|
|
113
|
+
layout.addWidget(self.run_pipeline_button())
|
|
114
|
+
self.setLayout(layout)
|
|
115
|
+
self.setObjectName(PANEL_NAME)
|
|
116
|
+
|
|
117
|
+
def handle_scans_dir_select_button(self):
|
|
118
|
+
last_directory = self.settings().get('last_directory')
|
|
119
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
120
|
+
if directory:
|
|
121
|
+
self.scans_dir_line_edit().setText(directory)
|
|
122
|
+
self.settings().set('last_directory', directory)
|
|
123
|
+
|
|
124
|
+
def handle_output_dir_select_button(self):
|
|
125
|
+
last_directory = self.settings().get('last_directory')
|
|
126
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
127
|
+
if directory:
|
|
128
|
+
self.output_dir_line_edit().setText(directory)
|
|
129
|
+
self.settings().set('last_directory', directory)
|
|
130
|
+
|
|
131
|
+
def handle_run_pipeline_button(self):
|
|
132
|
+
errors = self.check_inputs_and_parameters()
|
|
133
|
+
if len(errors) > 0:
|
|
134
|
+
error_message = 'Following errors were encountered:\n'
|
|
135
|
+
for error in errors:
|
|
136
|
+
error_message += f' - {error}\n'
|
|
137
|
+
QMessageBox.information(self, 'Error', error_message)
|
|
138
|
+
else:
|
|
139
|
+
LOG.info('Running pipeline...')
|
|
140
|
+
self.run_pipeline_button().setEnabled(False)
|
|
141
|
+
self.save_inputs_and_parameters()
|
|
142
|
+
self._task = LiverAnalysisPipeline(
|
|
143
|
+
inputs={'scans': self.scans_dir_line_edit().text()},
|
|
144
|
+
params={'compressed': True},
|
|
145
|
+
output=self.output_dir_line_edit().text(),
|
|
146
|
+
overwrite=self.overwrite_checkbox().isChecked(),
|
|
147
|
+
)
|
|
148
|
+
self._worker = Worker(self._task)
|
|
149
|
+
self._thread = QThread()
|
|
150
|
+
self._worker.moveToThread(self._thread)
|
|
151
|
+
self._thread.started.connect(self._worker.run)
|
|
152
|
+
self._worker.progress.connect(self.handle_progress)
|
|
153
|
+
self._worker.status.connect(self.handle_status)
|
|
154
|
+
self._worker.finished.connect(self.handle_finished)
|
|
155
|
+
self._worker.finished.connect(self._thread.quit)
|
|
156
|
+
self._worker.finished.connect(self._worker.deleteLater)
|
|
157
|
+
self._thread.finished.connect(self._thread.deleteLater)
|
|
158
|
+
self._thread.start()
|
|
159
|
+
|
|
160
|
+
@Slot(int)
|
|
161
|
+
def handle_progress(self, progress):
|
|
162
|
+
LOG.info(f'Progress: {progress} / 100%')
|
|
163
|
+
|
|
164
|
+
@Slot(str)
|
|
165
|
+
def handle_status(self, status):
|
|
166
|
+
LOG.info(f'Status: {status}')
|
|
167
|
+
|
|
168
|
+
@Slot()
|
|
169
|
+
def handle_finished(self):
|
|
170
|
+
self.run_pipeline_button().setEnabled(True)
|
|
171
|
+
|
|
172
|
+
def check_inputs_and_parameters(self):
|
|
173
|
+
errors = []
|
|
174
|
+
if self.scans_dir_line_edit().text() == '':
|
|
175
|
+
errors.append('Empty scans directory path')
|
|
176
|
+
elif not os.path.isdir(self.scans_dir_line_edit().text()):
|
|
177
|
+
errors.append('Scans directory does not exist')
|
|
178
|
+
if self.output_dir_line_edit().text() == '':
|
|
179
|
+
errors.append('Empty output directory path')
|
|
180
|
+
elif os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
|
|
181
|
+
errors.append('Output directory exists but overwrite=False. Please remove output directory first')
|
|
182
|
+
return errors
|
|
183
|
+
|
|
184
|
+
def save_inputs_and_parameters(self):
|
|
185
|
+
self.settings().set(f'{PANEL_NAME}/scans_dir', self.scans_dir_line_edit().text())
|
|
186
|
+
self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
|
|
187
|
+
self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from PySide6.QtWidgets import (
|
|
4
|
+
QLineEdit,
|
|
5
|
+
QCheckBox,
|
|
6
|
+
QSpinBox,
|
|
7
|
+
QHBoxLayout,
|
|
8
|
+
QVBoxLayout,
|
|
9
|
+
QFormLayout,
|
|
10
|
+
QPushButton,
|
|
11
|
+
QFileDialog,
|
|
12
|
+
QMessageBox,
|
|
13
|
+
)
|
|
14
|
+
from PySide6.QtCore import (
|
|
15
|
+
QThread,
|
|
16
|
+
Slot,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
21
|
+
from mosamatic2.ui.settings import Settings
|
|
22
|
+
from mosamatic2.ui.utils import is_macos
|
|
23
|
+
from mosamatic2.ui.worker import Worker
|
|
24
|
+
from mosamatic2.core.tasks import CalculateMaskStatisticsTask
|
|
25
|
+
|
|
26
|
+
LOG = LogManager()
|
|
27
|
+
|
|
28
|
+
PANEL_TITLE = 'CalculateMaskStatisticsTask'
|
|
29
|
+
PANEL_NAME = 'calculatemaskstatisticstaskpanel'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CalculateMaskStatisticsTaskPanel(TaskPanel):
|
|
33
|
+
def __init__(self):
|
|
34
|
+
super(CalculateMaskStatisticsTaskPanel, self).__init__()
|
|
35
|
+
self.set_title(PANEL_TITLE)
|
|
36
|
+
self._scans_dir_line_edit = None
|
|
37
|
+
self._scans_dir_select_button = None
|
|
38
|
+
self._masks_dir_line_edit = None
|
|
39
|
+
self._masks_dir_select_button = None
|
|
40
|
+
self._output_dir_line_edit = None
|
|
41
|
+
self._output_dir_select_button = None
|
|
42
|
+
self._overwrite_checkbox = None
|
|
43
|
+
self._form_layout = None
|
|
44
|
+
self._run_task_button = None
|
|
45
|
+
self._settings = None
|
|
46
|
+
self._task = None
|
|
47
|
+
self._worker = None
|
|
48
|
+
self._thread = None
|
|
49
|
+
self.init_layout()
|
|
50
|
+
|
|
51
|
+
def scans_dir_line_edit(self):
|
|
52
|
+
if not self._scans_dir_line_edit:
|
|
53
|
+
self._scans_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/scans_dir'))
|
|
54
|
+
return self._scans_dir_line_edit
|
|
55
|
+
|
|
56
|
+
def scans_dir_select_button(self):
|
|
57
|
+
if not self._scans_dir_select_button:
|
|
58
|
+
self._scans_dir_select_button = QPushButton('Select')
|
|
59
|
+
self._scans_dir_select_button.clicked.connect(self.handle_scans_dir_select_button)
|
|
60
|
+
return self._scans_dir_select_button
|
|
61
|
+
|
|
62
|
+
def masks_dir_line_edit(self):
|
|
63
|
+
if not self._masks_dir_line_edit:
|
|
64
|
+
self._masks_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/masks_dir'))
|
|
65
|
+
return self._masks_dir_line_edit
|
|
66
|
+
|
|
67
|
+
def masks_dir_select_button(self):
|
|
68
|
+
if not self._masks_dir_select_button:
|
|
69
|
+
self._masks_dir_select_button = QPushButton('Select')
|
|
70
|
+
self._masks_dir_select_button.clicked.connect(self.handle_masks_dir_select_button)
|
|
71
|
+
return self._masks_dir_select_button
|
|
72
|
+
|
|
73
|
+
def output_dir_line_edit(self):
|
|
74
|
+
if not self._output_dir_line_edit:
|
|
75
|
+
self._output_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/output_dir'))
|
|
76
|
+
return self._output_dir_line_edit
|
|
77
|
+
|
|
78
|
+
def output_dir_select_button(self):
|
|
79
|
+
if not self._output_dir_select_button:
|
|
80
|
+
self._output_dir_select_button = QPushButton('Select')
|
|
81
|
+
self._output_dir_select_button.clicked.connect(self.handle_output_dir_select_button)
|
|
82
|
+
return self._output_dir_select_button
|
|
83
|
+
|
|
84
|
+
def overwrite_checkbox(self):
|
|
85
|
+
if not self._overwrite_checkbox:
|
|
86
|
+
self._overwrite_checkbox = QCheckBox('')
|
|
87
|
+
self._overwrite_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/overwrite', True))
|
|
88
|
+
return self._overwrite_checkbox
|
|
89
|
+
|
|
90
|
+
def form_layout(self):
|
|
91
|
+
if not self._form_layout:
|
|
92
|
+
self._form_layout = QFormLayout()
|
|
93
|
+
if is_macos():
|
|
94
|
+
self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
|
|
95
|
+
return self._form_layout
|
|
96
|
+
|
|
97
|
+
def run_task_button(self):
|
|
98
|
+
if not self._run_task_button:
|
|
99
|
+
self._run_task_button = QPushButton('Run task')
|
|
100
|
+
self._run_task_button.clicked.connect(self.handle_run_task_button)
|
|
101
|
+
return self._run_task_button
|
|
102
|
+
|
|
103
|
+
def settings(self):
|
|
104
|
+
if not self._settings:
|
|
105
|
+
self._settings = Settings()
|
|
106
|
+
return self._settings
|
|
107
|
+
|
|
108
|
+
def init_layout(self):
|
|
109
|
+
scans_dir_layout = QHBoxLayout()
|
|
110
|
+
scans_dir_layout.addWidget(self.scans_dir_line_edit())
|
|
111
|
+
scans_dir_layout.addWidget(self.scans_dir_select_button())
|
|
112
|
+
masks_dir_layout = QHBoxLayout()
|
|
113
|
+
masks_dir_layout.addWidget(self.masks_dir_line_edit())
|
|
114
|
+
masks_dir_layout.addWidget(self.masks_dir_select_button())
|
|
115
|
+
output_dir_layout = QHBoxLayout()
|
|
116
|
+
output_dir_layout.addWidget(self.output_dir_line_edit())
|
|
117
|
+
output_dir_layout.addWidget(self.output_dir_select_button())
|
|
118
|
+
self.form_layout().addRow('Scans directory', scans_dir_layout)
|
|
119
|
+
self.form_layout().addRow('Masks directory', masks_dir_layout)
|
|
120
|
+
self.form_layout().addRow('Output directory', output_dir_layout)
|
|
121
|
+
self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
|
|
122
|
+
layout = QVBoxLayout()
|
|
123
|
+
layout.addLayout(self.form_layout())
|
|
124
|
+
layout.addWidget(self.run_task_button())
|
|
125
|
+
self.setLayout(layout)
|
|
126
|
+
self.setObjectName(PANEL_NAME)
|
|
127
|
+
|
|
128
|
+
def handle_scans_dir_select_button(self):
|
|
129
|
+
last_directory = self.settings().get('last_directory')
|
|
130
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
131
|
+
if directory:
|
|
132
|
+
self.scans_dir_line_edit().setText(directory)
|
|
133
|
+
self.settings().set('last_directory', directory)
|
|
134
|
+
|
|
135
|
+
def handle_masks_dir_select_button(self):
|
|
136
|
+
last_directory = self.settings().get('last_directory')
|
|
137
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
138
|
+
if directory:
|
|
139
|
+
self.masks_dir_line_edit().setText(directory)
|
|
140
|
+
self.settings().set('last_directory', directory)
|
|
141
|
+
|
|
142
|
+
def handle_output_dir_select_button(self):
|
|
143
|
+
last_directory = self.settings().get('last_directory')
|
|
144
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
145
|
+
if directory:
|
|
146
|
+
self.output_dir_line_edit().setText(directory)
|
|
147
|
+
self.settings().set('last_directory', directory)
|
|
148
|
+
|
|
149
|
+
def handle_run_task_button(self):
|
|
150
|
+
errors = self.check_inputs_and_parameters()
|
|
151
|
+
if len(errors) > 0:
|
|
152
|
+
error_message = 'Following errors were encountered:\n'
|
|
153
|
+
for error in errors:
|
|
154
|
+
error_message += f' - {error}\n'
|
|
155
|
+
QMessageBox.information(self, 'Error', error_message)
|
|
156
|
+
else:
|
|
157
|
+
LOG.info('Running task...')
|
|
158
|
+
self.run_task_button().setEnabled(False)
|
|
159
|
+
self.save_inputs_and_parameters()
|
|
160
|
+
self._task = CalculateMaskStatisticsTask(
|
|
161
|
+
inputs={
|
|
162
|
+
'scans': self.scans_dir_line_edit().text(),
|
|
163
|
+
'masks': self.masks_dir_line_edit().text(),
|
|
164
|
+
},
|
|
165
|
+
params=None,
|
|
166
|
+
output=self.output_dir_line_edit().text(),
|
|
167
|
+
overwrite=self.overwrite_checkbox().isChecked(),
|
|
168
|
+
)
|
|
169
|
+
self._worker = Worker(self._task)
|
|
170
|
+
self._thread = QThread()
|
|
171
|
+
self._worker.moveToThread(self._thread)
|
|
172
|
+
self._thread.started.connect(self._worker.run)
|
|
173
|
+
self._worker.progress.connect(self.handle_progress)
|
|
174
|
+
self._worker.status.connect(self.handle_status)
|
|
175
|
+
self._worker.finished.connect(self.handle_finished)
|
|
176
|
+
self._worker.finished.connect(self._thread.quit)
|
|
177
|
+
self._worker.finished.connect(self._worker.deleteLater)
|
|
178
|
+
self._thread.finished.connect(self._thread.deleteLater)
|
|
179
|
+
self._thread.start()
|
|
180
|
+
|
|
181
|
+
@Slot(int)
|
|
182
|
+
def handle_progress(self, progress):
|
|
183
|
+
LOG.info(f'Progress: {progress} / 100%')
|
|
184
|
+
|
|
185
|
+
@Slot(str)
|
|
186
|
+
def handle_status(self, status):
|
|
187
|
+
LOG.info(f'Status: {status}')
|
|
188
|
+
|
|
189
|
+
@Slot()
|
|
190
|
+
def handle_finished(self):
|
|
191
|
+
self.run_task_button().setEnabled(True)
|
|
192
|
+
|
|
193
|
+
# HELPERS
|
|
194
|
+
|
|
195
|
+
def check_inputs_and_parameters(self):
|
|
196
|
+
errors = []
|
|
197
|
+
if self.scans_dir_line_edit().text() == '':
|
|
198
|
+
errors.append('Empty scans directory path')
|
|
199
|
+
if not os.path.isdir(self.scans_dir_line_edit().text()):
|
|
200
|
+
errors.append('Scans directory does not exist')
|
|
201
|
+
if self.masks_dir_line_edit().text() == '':
|
|
202
|
+
errors.append('Empty masks directory path')
|
|
203
|
+
if not os.path.isdir(self.masks_dir_line_edit().text()):
|
|
204
|
+
errors.append('Masks directory does not exist')
|
|
205
|
+
if self.output_dir_line_edit().text() == '':
|
|
206
|
+
errors.append('Empty output directory path')
|
|
207
|
+
if os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
|
|
208
|
+
errors.append('Output directory exists but overwrite=False. Please remove output directory first')
|
|
209
|
+
return errors
|
|
210
|
+
|
|
211
|
+
def save_inputs_and_parameters(self):
|
|
212
|
+
self.settings().set(f'{PANEL_NAME}/scans_dir', self.scans_dir_line_edit().text())
|
|
213
|
+
self.settings().set(f'{PANEL_NAME}/masks_dir', self.masks_dir_line_edit().text())
|
|
214
|
+
self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
|
|
215
|
+
self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
|
|
@@ -14,6 +14,8 @@ class CustomInteractorStyle(vtk.vtkInteractorStyleImage):
|
|
|
14
14
|
self.status_actor = status_actor
|
|
15
15
|
self.slice_obj = slice_obj
|
|
16
16
|
self.orientation = orientation
|
|
17
|
+
self._color_window = 0
|
|
18
|
+
self._color_level = 0
|
|
17
19
|
|
|
18
20
|
xmin, xmax, ymin, ymax, zmin, zmax = image_data.GetExtent()
|
|
19
21
|
if orientation == "axial":
|
|
@@ -29,6 +31,22 @@ class CustomInteractorStyle(vtk.vtkInteractorStyleImage):
|
|
|
29
31
|
self.slice_mapper.SetSliceNumber(self.slice)
|
|
30
32
|
self.update_status_message()
|
|
31
33
|
|
|
34
|
+
def color_window(self):
|
|
35
|
+
return self._color_window
|
|
36
|
+
|
|
37
|
+
def set_color_window(self, color_window):
|
|
38
|
+
self._color_window = color_window
|
|
39
|
+
self.slice_obj.GetProperty().SetColorWindow(self._color_window)
|
|
40
|
+
self.GetInteractor().GetRenderWindow().Render()
|
|
41
|
+
|
|
42
|
+
def color_level(self):
|
|
43
|
+
return self._color_level
|
|
44
|
+
|
|
45
|
+
def set_color_level(self, color_level):
|
|
46
|
+
self._color_level = color_level
|
|
47
|
+
self.slice_obj.GetProperty().SetColorLevel(self._color_level)
|
|
48
|
+
self.GetInteractor().GetRenderWindow().Render()
|
|
49
|
+
|
|
32
50
|
def update_status_message(self):
|
|
33
51
|
window = int(self.slice_obj.GetProperty().GetColorWindow())
|
|
34
52
|
level = int(self.slice_obj.GetProperty().GetColorLevel())
|
|
@@ -11,10 +11,12 @@ IMAGE_SHIFT_SCALE = -1000
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class SliceViewer(QWidget):
|
|
14
|
-
def __init__(self):
|
|
14
|
+
def __init__(self, color_window=COLOR_WINDOW, color_level=COLOR_LEVEL):
|
|
15
15
|
super(SliceViewer, self).__init__()
|
|
16
16
|
self._nifti_file_or_dicom_dir = None
|
|
17
17
|
self._view_orientation = 'axial'
|
|
18
|
+
self._color_window = color_window
|
|
19
|
+
self._color_level = color_level
|
|
18
20
|
self._vtk_widget = QVTKRenderWindowInteractor(self)
|
|
19
21
|
self._render_window = self._vtk_widget.GetRenderWindow()
|
|
20
22
|
self._interactor = self._render_window.GetInteractor()
|
|
@@ -27,6 +29,22 @@ class SliceViewer(QWidget):
|
|
|
27
29
|
self._render_window.AddRenderer(self._default_renderer)
|
|
28
30
|
self._render_window.Render()
|
|
29
31
|
|
|
32
|
+
def color_window(self):
|
|
33
|
+
return self._color_window
|
|
34
|
+
|
|
35
|
+
def set_color_window(self, color_window):
|
|
36
|
+
self._color_window = color_window
|
|
37
|
+
if self._interactor_style:
|
|
38
|
+
self._interactor_style.set_color_window(self._color_window)
|
|
39
|
+
|
|
40
|
+
def color_level(self):
|
|
41
|
+
return self._color_level
|
|
42
|
+
|
|
43
|
+
def set_color_level(self, color_level):
|
|
44
|
+
self._color_level = color_level
|
|
45
|
+
if self._interactor_style:
|
|
46
|
+
self._interactor_style.set_color_level(self._color_level)
|
|
47
|
+
|
|
30
48
|
def nifti_file_or_dicom_dir(self):
|
|
31
49
|
return self._nifti_file_or_dicom_dir
|
|
32
50
|
|
|
@@ -75,8 +93,8 @@ class SliceViewer(QWidget):
|
|
|
75
93
|
slice_mapper.SetOrientationToZ() # axial orientation
|
|
76
94
|
slice_mapper.SetSliceNumber(axial_index)
|
|
77
95
|
slice = vtk.vtkImageSlice()
|
|
78
|
-
slice.GetProperty().SetColorWindow(
|
|
79
|
-
slice.GetProperty().SetColorLevel(
|
|
96
|
+
slice.GetProperty().SetColorWindow(self.color_window())
|
|
97
|
+
slice.GetProperty().SetColorLevel(self.color_level())
|
|
80
98
|
slice.SetMapper(slice_mapper)
|
|
81
99
|
slice_text_actor = self.create_text_actor("", 0.01, 0.01, 12, align_bottom=True, normalized=True)
|
|
82
100
|
usage_text_actor = self.create_text_actor(
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from PySide6.QtWidgets import (
|
|
2
2
|
QLineEdit,
|
|
3
|
+
QLabel,
|
|
4
|
+
QSpinBox,
|
|
3
5
|
QHBoxLayout,
|
|
4
6
|
QVBoxLayout,
|
|
5
7
|
QFormLayout,
|
|
@@ -24,6 +26,9 @@ class SliceVisualization(Visualization):
|
|
|
24
26
|
self._image_line_edit = None
|
|
25
27
|
self._image_select_button = None
|
|
26
28
|
self._image_dir_select_button = None
|
|
29
|
+
self._color_window_spinbox = None
|
|
30
|
+
self._color_level_spinbox = None
|
|
31
|
+
self._color_window_and_level_reset_button = None
|
|
27
32
|
self._load_image_button = None
|
|
28
33
|
self._slice_viewer = None
|
|
29
34
|
self._form_layout = None
|
|
@@ -47,6 +52,22 @@ class SliceVisualization(Visualization):
|
|
|
47
52
|
self._image_dir_select_button.clicked.connect(self.handle_image_dir_select_button)
|
|
48
53
|
return self._image_dir_select_button
|
|
49
54
|
|
|
55
|
+
def color_window_spinbox(self):
|
|
56
|
+
if not self._color_window_spinbox:
|
|
57
|
+
self._color_window_spinbox = QSpinBox(self, minimum=0, maximum=1024, value=400)
|
|
58
|
+
return self._color_window_spinbox
|
|
59
|
+
|
|
60
|
+
def color_level_spinbox(self):
|
|
61
|
+
if not self._color_level_spinbox:
|
|
62
|
+
self._color_level_spinbox = QSpinBox(self, minimum=0, maximum=100, value=40)
|
|
63
|
+
return self._color_level_spinbox
|
|
64
|
+
|
|
65
|
+
def color_window_and_level_reset_button(self):
|
|
66
|
+
if not self._color_window_and_level_reset_button:
|
|
67
|
+
self._color_window_and_level_reset_button = QPushButton('Reset')
|
|
68
|
+
self._color_window_and_level_reset_button.clicked.connect(self.handle_color_window_and_level_reset_button)
|
|
69
|
+
return self._color_window_and_level_reset_button
|
|
70
|
+
|
|
50
71
|
def load_image_button(self):
|
|
51
72
|
if not self._load_image_button:
|
|
52
73
|
self._load_image_button = QPushButton('Load')
|
|
@@ -55,7 +76,10 @@ class SliceVisualization(Visualization):
|
|
|
55
76
|
|
|
56
77
|
def slice_viewer(self):
|
|
57
78
|
if not self._slice_viewer:
|
|
58
|
-
self._slice_viewer = SliceViewer(
|
|
79
|
+
self._slice_viewer = SliceViewer(
|
|
80
|
+
color_window=self.color_window_spinbox().value(),
|
|
81
|
+
color_level=self.color_level_spinbox().value(),
|
|
82
|
+
)
|
|
59
83
|
return self._slice_viewer
|
|
60
84
|
|
|
61
85
|
def form_layout(self):
|
|
@@ -75,7 +99,14 @@ class SliceVisualization(Visualization):
|
|
|
75
99
|
image_layout.addWidget(self.image_line_edit())
|
|
76
100
|
image_layout.addWidget(self.image_select_button())
|
|
77
101
|
image_layout.addWidget(self.image_dir_select_button())
|
|
102
|
+
color_window_and_level_layout = QHBoxLayout()
|
|
103
|
+
color_window_and_level_layout.addWidget(QLabel('Color window'))
|
|
104
|
+
color_window_and_level_layout.addWidget(self.color_window_spinbox())
|
|
105
|
+
color_window_and_level_layout.addWidget(QLabel('Color level'))
|
|
106
|
+
color_window_and_level_layout.addWidget(self.color_level_spinbox())
|
|
107
|
+
color_window_and_level_layout.addWidget(self.color_window_and_level_reset_button())
|
|
78
108
|
self.form_layout().addRow('NIFTI file or DICOM directory', image_layout)
|
|
109
|
+
self.form_layout().addRow('', color_window_and_level_layout)
|
|
79
110
|
layout = QVBoxLayout()
|
|
80
111
|
layout.addLayout(self.form_layout())
|
|
81
112
|
layout.addWidget(self.load_image_button())
|
|
@@ -97,6 +128,10 @@ class SliceVisualization(Visualization):
|
|
|
97
128
|
self.image_line_edit().setText(dir_path)
|
|
98
129
|
self.settings().set('last_directory', dir_path)
|
|
99
130
|
|
|
131
|
+
def handle_color_window_and_level_reset_button(self):
|
|
132
|
+
self.slice_viewer().set_color_window(self.color_window_spinbox().value())
|
|
133
|
+
self.slice_viewer().set_color_level(self.color_level_spinbox().value())
|
|
134
|
+
|
|
100
135
|
def handle_load_image_button(self):
|
|
101
136
|
self.slice_viewer().set_nifti_file_or_dicom_dir(self.image_line_edit().text())
|
|
102
137
|
# self.slice_viewer().set_view_orientation('axial')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mosamatic2
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.17
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Ralph Brecheisen
|
|
6
6
|
Author-email: r.brecheisen@maastrichtuniversity.nl
|
|
@@ -9,9 +9,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
10
|
Requires-Dist: antspyx (>=0.5.4)
|
|
11
11
|
Requires-Dist: dicom2nifti (>=2.6.2)
|
|
12
|
-
Requires-Dist: docker
|
|
12
|
+
Requires-Dist: docker (>=7.1.0)
|
|
13
13
|
Requires-Dist: flask (>=3.1.2)
|
|
14
|
-
Requires-Dist: moosez
|
|
14
|
+
Requires-Dist: moosez (>=3.0.29)
|
|
15
15
|
Requires-Dist: nibabel (>=5.3.2)
|
|
16
16
|
Requires-Dist: numpy (>=1.26.4)
|
|
17
17
|
Requires-Dist: openpyxl (>=3.1.5)
|
|
@@ -23,7 +23,7 @@ Requires-Dist: pyqtgraph (>=0.13.7)
|
|
|
23
23
|
Requires-Dist: pyside6-essentials (>=6.9)
|
|
24
24
|
Requires-Dist: python-gdcm (>=3.0.26)
|
|
25
25
|
Requires-Dist: scipy (>=1.15.3)
|
|
26
|
-
Requires-Dist: slicer
|
|
26
|
+
Requires-Dist: slicer (>=0.0.8)
|
|
27
27
|
Requires-Dist: tensorboard (==2.15.2)
|
|
28
28
|
Requires-Dist: tensorboard-data-server (==0.7.2)
|
|
29
29
|
Requires-Dist: tensorflow (==2.15.*) ; platform_system == "Linux"
|