mosamatic2 2.0.5__py3-none-any.whl → 2.0.7__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/core/pipelines/__init__.py +1 -1
- mosamatic2/core/pipelines/defaultpipeline/__init__.py +0 -0
- mosamatic2/core/tasks/__init__.py +2 -1
- mosamatic2/core/tasks/createdicomsummarytask/__init__.py +0 -0
- mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py +60 -0
- mosamatic2/ui/mainwindow.py +34 -0
- mosamatic2/ui/resources/VERSION +1 -1
- mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +2 -2
- mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py +6 -0
- mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +1 -1
- mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py +183 -0
- mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +1 -1
- mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +1 -1
- mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +1 -1
- mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +1 -1
- mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py +1 -1
- mosamatic2/ui/widgets/panels/visualizations/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +62 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +98 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +106 -0
- mosamatic2/ui/widgets/panels/visualizations/visualization.py +6 -0
- {mosamatic2-2.0.5.dist-info → mosamatic2-2.0.7.dist-info}/METADATA +3 -1
- {mosamatic2-2.0.5.dist-info → mosamatic2-2.0.7.dist-info}/RECORD +28 -17
- /mosamatic2/core/pipelines/{defaultpipeline.py → defaultpipeline/defaultpipeline.py} +0 -0
- /mosamatic2/ui/widgets/panels/{taskpanel.py → tasks/taskpanel.py} +0 -0
- {mosamatic2-2.0.5.dist-info → mosamatic2-2.0.7.dist-info}/WHEEL +0 -0
- {mosamatic2-2.0.5.dist-info → mosamatic2-2.0.7.dist-info}/entry_points.txt +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
from mosamatic2.core.pipelines.defaultpipeline import DefaultPipeline
|
|
1
|
+
from mosamatic2.core.pipelines.defaultpipeline.defaultpipeline import DefaultPipeline
|
|
File without changes
|
|
@@ -3,4 +3,5 @@ from mosamatic2.core.tasks.segmentmusclefatl3tensorflowtask.segmentmusclefatl3te
|
|
|
3
3
|
from mosamatic2.core.tasks.calculatescorestask.calculatescorestask import CalculateScoresTask
|
|
4
4
|
from mosamatic2.core.tasks.createpngsfromsegmentationstask.createpngsfromsegmentationstask import CreatePngsFromSegmentationsTask
|
|
5
5
|
from mosamatic2.core.tasks.dicom2niftitask.dicom2niftitask import Dicom2NiftiTask
|
|
6
|
-
from mosamatic2.core.tasks.selectslicefromscanstask.selectslicefromscanstask import SelectSliceFromScansTask
|
|
6
|
+
from mosamatic2.core.tasks.selectslicefromscanstask.selectslicefromscanstask import SelectSliceFromScansTask
|
|
7
|
+
from mosamatic2.core.tasks.createdicomsummarytask.createdicomsummarytask import CreateDicomSummaryTask
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from mosamatic2.core.tasks.task import Task
|
|
4
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
5
|
+
from mosamatic2.core.utils import is_dicom, load_dicom
|
|
6
|
+
|
|
7
|
+
LOG = LogManager()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CreateDicomSummaryTask(Task):
|
|
11
|
+
INPUTS = ['directory']
|
|
12
|
+
PARAMS = []
|
|
13
|
+
|
|
14
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
15
|
+
super(CreateDicomSummaryTask, self).__init__(inputs, params, output, overwrite)
|
|
16
|
+
|
|
17
|
+
def run(self):
|
|
18
|
+
nr_steps = len(os.listdir(self.input('directory'))) * 2
|
|
19
|
+
LOG.info('Collecting data...')
|
|
20
|
+
data = {}
|
|
21
|
+
step = 0
|
|
22
|
+
for patient_dir_name in os.listdir(self.input('directory')):
|
|
23
|
+
data[patient_dir_name] = {}
|
|
24
|
+
patient_dir_path = os.path.join(self.input('directory'), patient_dir_name)
|
|
25
|
+
for root, dirs, files in os.walk(patient_dir_path):
|
|
26
|
+
for f in files:
|
|
27
|
+
f_path = os.path.join(root, f)
|
|
28
|
+
if is_dicom(f_path):
|
|
29
|
+
p = load_dicom(f_path, stop_before_pixels=True)
|
|
30
|
+
series_instance_uid = p.SeriesInstanceUID
|
|
31
|
+
if not series_instance_uid in data[patient_dir_name].keys():
|
|
32
|
+
data[patient_dir_name][series_instance_uid] = []
|
|
33
|
+
data[patient_dir_name][series_instance_uid].append(p)
|
|
34
|
+
self.set_progress(step, nr_steps)
|
|
35
|
+
step += 1
|
|
36
|
+
LOG.info('Building summary...')
|
|
37
|
+
summary = {
|
|
38
|
+
'nr_series_per_patient': {},
|
|
39
|
+
}
|
|
40
|
+
# Calculate nr. of series per patient
|
|
41
|
+
for patient_dir_name in data.keys():
|
|
42
|
+
summary['nr_series_per_patient'][patient_dir_name] = len(data[patient_dir_name].keys())
|
|
43
|
+
# Summarize series data per patient
|
|
44
|
+
for patient_dir_name in data.keys():
|
|
45
|
+
summary[patient_dir_name] = {}
|
|
46
|
+
for series_instance_uid in data[patient_dir_name].keys():
|
|
47
|
+
summary[patient_dir_name][series_instance_uid] = {
|
|
48
|
+
'nr_images': len(data[patient_dir_name][series_instance_uid]),
|
|
49
|
+
'modality': data[patient_dir_name][series_instance_uid][0].Modality,
|
|
50
|
+
'image_type': str(data[patient_dir_name][series_instance_uid][0].ImageType),
|
|
51
|
+
'rows': data[patient_dir_name][series_instance_uid][0].Rows,
|
|
52
|
+
'columns': data[patient_dir_name][series_instance_uid][0].Columns,
|
|
53
|
+
'pixel_spacing': str(data[patient_dir_name][series_instance_uid][0].PixelSpacing),
|
|
54
|
+
'slice_thickness': data[patient_dir_name][series_instance_uid][0].SliceThickness,
|
|
55
|
+
}
|
|
56
|
+
self.set_progress(step, nr_steps)
|
|
57
|
+
step += 1
|
|
58
|
+
LOG.info('Exporting summary...')
|
|
59
|
+
with open(os.path.join(self.output(), 'summary.txt'), 'w') as f:
|
|
60
|
+
json.dump(summary, f, indent=4)
|
mosamatic2/ui/mainwindow.py
CHANGED
|
@@ -19,8 +19,10 @@ from mosamatic2.ui.widgets.panels.tasks.segmentmusclefatl3tensorflowtaskpanel im
|
|
|
19
19
|
from mosamatic2.ui.widgets.panels.tasks.createpngsfromsegmentationstaskpanel import CreatePngsFromSegmentationsTaskPanel
|
|
20
20
|
from mosamatic2.ui.widgets.panels.tasks.calculatescorestaskpanel import CalculateScoresTaskPanel
|
|
21
21
|
from mosamatic2.ui.widgets.panels.tasks.dicom2niftitaskpanel import Dicom2NiftiTaskPanel
|
|
22
|
+
from mosamatic2.ui.widgets.panels.tasks.createdicomsummarytaskpanel import CreateDicomSummaryTaskPanel
|
|
22
23
|
from mosamatic2.ui.widgets.panels.tasks.selectslicefromscanstaskpanel import SelectSliceFromScansTaskPanel
|
|
23
24
|
from mosamatic2.ui.widgets.panels.pipelines.defaultpipelinepanel import DefaultPipelinePanel
|
|
25
|
+
from mosamatic2.ui.widgets.panels.visualizations.slicevisualization.slicevisualization import SliceVisualization
|
|
24
26
|
|
|
25
27
|
LOG = LogManager()
|
|
26
28
|
|
|
@@ -37,8 +39,10 @@ class MainWindow(QMainWindow):
|
|
|
37
39
|
self._create_pngs_from_segmentations_task_panel = None
|
|
38
40
|
self._calculate_scores_task_panel = None
|
|
39
41
|
self._dicom2nifti_task_panel = None
|
|
42
|
+
self._create_dicom_summary_task_panel = None
|
|
40
43
|
self._select_slice_from_scans_task_panel = None
|
|
41
44
|
self._default_pipeline_panel = None
|
|
45
|
+
self._slice_visualization = None
|
|
42
46
|
self.init_window()
|
|
43
47
|
|
|
44
48
|
def init_window(self):
|
|
@@ -57,6 +61,7 @@ class MainWindow(QMainWindow):
|
|
|
57
61
|
self.init_app_menu()
|
|
58
62
|
self.init_tasks_menu()
|
|
59
63
|
self.init_pipelines_menu()
|
|
64
|
+
self.init_visualizations_menu()
|
|
60
65
|
if is_macos():
|
|
61
66
|
self.menuBar().setNativeMenuBar(False)
|
|
62
67
|
|
|
@@ -77,6 +82,8 @@ class MainWindow(QMainWindow):
|
|
|
77
82
|
create_pngs_from_segmentations_task_action.triggered.connect(self.handle_create_pngs_from_segmentations_task_action)
|
|
78
83
|
dicom2nifti_task_action = QAction('Dicom2NiftiTask', self)
|
|
79
84
|
dicom2nifti_task_action.triggered.connect(self.handle_dicom2nifti_task_action)
|
|
85
|
+
create_dicom_summary_task_action = QAction('CreateDicomSummaryTask', self)
|
|
86
|
+
create_dicom_summary_task_action.triggered.connect(self.handle_create_dicom_summary_task_action)
|
|
80
87
|
select_slice_from_scans_task_action = QAction('SelectSliceFromScansTask', self)
|
|
81
88
|
select_slice_from_scans_task_action.triggered.connect(self.handle_select_slice_from_scans_task_action)
|
|
82
89
|
tasks_menu = self.menuBar().addMenu('Tasks')
|
|
@@ -85,6 +92,7 @@ class MainWindow(QMainWindow):
|
|
|
85
92
|
tasks_menu.addAction(calculate_scores_task_action)
|
|
86
93
|
tasks_menu.addAction(create_pngs_from_segmentations_task_action)
|
|
87
94
|
tasks_menu.addAction(dicom2nifti_task_action)
|
|
95
|
+
tasks_menu.addAction(create_dicom_summary_task_action)
|
|
88
96
|
tasks_menu.addAction(select_slice_from_scans_task_action)
|
|
89
97
|
|
|
90
98
|
def init_pipelines_menu(self):
|
|
@@ -93,6 +101,12 @@ class MainWindow(QMainWindow):
|
|
|
93
101
|
pipelines_menu = self.menuBar().addMenu('Pipelines')
|
|
94
102
|
pipelines_menu.addAction(default_pipeline_action)
|
|
95
103
|
|
|
104
|
+
def init_visualizations_menu(self):
|
|
105
|
+
slice_visualization_action = QAction('SliceVisualization', self)
|
|
106
|
+
slice_visualization_action.triggered.connect(self.handle_slice_visualization_action)
|
|
107
|
+
visualizations_menu = self.menuBar().addMenu('Visualizations')
|
|
108
|
+
visualizations_menu.addAction(slice_visualization_action)
|
|
109
|
+
|
|
96
110
|
def init_status_bar(self):
|
|
97
111
|
self.set_status('Ready')
|
|
98
112
|
|
|
@@ -111,8 +125,10 @@ class MainWindow(QMainWindow):
|
|
|
111
125
|
self._main_panel.add_panel(self.create_pngs_from_segmentations_task_panel(), 'createpngsfromsegmentationstaskpanel')
|
|
112
126
|
self._main_panel.add_panel(self.calculate_scores_task_panel(), 'calculatescorestaskpanel')
|
|
113
127
|
self._main_panel.add_panel(self.dicom2nifti_task_panel(), 'dicom2niftitaskpanel')
|
|
128
|
+
self._main_panel.add_panel(self.create_dicom_summary_task_panel(), 'createdicomsummarytaskpanel')
|
|
114
129
|
self._main_panel.add_panel(self.select_slice_from_scans_task_panel(), 'selectslicefromscanstaskpanel')
|
|
115
130
|
self._main_panel.add_panel(self.default_pipeline_panel(), 'defaultpipelinepanel')
|
|
131
|
+
self._main_panel.add_panel(self.slice_visualization(), 'slicevisualization')
|
|
116
132
|
self._main_panel.select_panel('defaultpipelinepanel')
|
|
117
133
|
return self._main_panel
|
|
118
134
|
|
|
@@ -148,6 +164,11 @@ class MainWindow(QMainWindow):
|
|
|
148
164
|
if not self._dicom2nifti_task_panel:
|
|
149
165
|
self._dicom2nifti_task_panel = Dicom2NiftiTaskPanel()
|
|
150
166
|
return self._dicom2nifti_task_panel
|
|
167
|
+
|
|
168
|
+
def create_dicom_summary_task_panel(self):
|
|
169
|
+
if not self._create_dicom_summary_task_panel:
|
|
170
|
+
self._create_dicom_summary_task_panel = CreateDicomSummaryTaskPanel()
|
|
171
|
+
return self._create_dicom_summary_task_panel
|
|
151
172
|
|
|
152
173
|
def select_slice_from_scans_task_panel(self):
|
|
153
174
|
if not self._select_slice_from_scans_task_panel:
|
|
@@ -158,6 +179,11 @@ class MainWindow(QMainWindow):
|
|
|
158
179
|
if not self._default_pipeline_panel:
|
|
159
180
|
self._default_pipeline_panel = DefaultPipelinePanel()
|
|
160
181
|
return self._default_pipeline_panel
|
|
182
|
+
|
|
183
|
+
def slice_visualization(self):
|
|
184
|
+
if not self._slice_visualization:
|
|
185
|
+
self._slice_visualization = SliceVisualization()
|
|
186
|
+
return self._slice_visualization
|
|
161
187
|
|
|
162
188
|
# SETTERS
|
|
163
189
|
|
|
@@ -181,12 +207,18 @@ class MainWindow(QMainWindow):
|
|
|
181
207
|
def handle_dicom2nifti_task_action(self):
|
|
182
208
|
self.main_panel().select_panel('dicom2niftitaskpanel')
|
|
183
209
|
|
|
210
|
+
def handle_create_dicom_summary_task_action(self):
|
|
211
|
+
self.main_panel().select_panel('createdicomsummarytaskpanel')
|
|
212
|
+
|
|
184
213
|
def handle_select_slice_from_scans_task_action(self):
|
|
185
214
|
self.main_panel().select_panel('selectslicefromscanstaskpanel')
|
|
186
215
|
|
|
187
216
|
def handle_default_pipeline_action(self):
|
|
188
217
|
self.main_panel().select_panel('defaultpipelinepanel')
|
|
189
218
|
|
|
219
|
+
def handle_slice_visualization_action(self):
|
|
220
|
+
self.main_panel().select_panel('slicevisualization')
|
|
221
|
+
|
|
190
222
|
def showEvent(self, event):
|
|
191
223
|
return super().showEvent(event)
|
|
192
224
|
|
|
@@ -198,8 +230,10 @@ class MainWindow(QMainWindow):
|
|
|
198
230
|
self.create_pngs_from_segmentations_task_panel().save_inputs_and_parameters()
|
|
199
231
|
self.calculate_scores_task_panel().save_inputs_and_parameters()
|
|
200
232
|
self.dicom2nifti_task_panel().save_inputs_and_parameters()
|
|
233
|
+
self.create_dicom_summary_task_panel().save_inputs_and_parameters()
|
|
201
234
|
self.select_slice_from_scans_task_panel().save_inputs_and_parameters()
|
|
202
235
|
self.default_pipeline_panel().save_inputs_and_parameters()
|
|
236
|
+
self.slice_visualization().save_inputs_and_parameters()
|
|
203
237
|
return super().closeEvent(event)
|
|
204
238
|
|
|
205
239
|
def load_geometry_and_state(self):
|
mosamatic2/ui/resources/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.0.
|
|
1
|
+
2.0.7
|
|
@@ -18,7 +18,7 @@ from PySide6.QtCore import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
21
|
-
from mosamatic2.ui.widgets.panels.
|
|
21
|
+
from mosamatic2.ui.widgets.panels.pipelines.pipelinepanel import PipelinePanel
|
|
22
22
|
from mosamatic2.ui.settings import Settings
|
|
23
23
|
from mosamatic2.ui.utils import is_macos
|
|
24
24
|
from mosamatic2.ui.worker import Worker
|
|
@@ -32,7 +32,7 @@ MODEL_TYPE_ITEM_NAMES = ['tensorflow', 'pytorch']
|
|
|
32
32
|
MODEL_VERSION_ITEM_NAMES = ['1.0', '2.2']
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class DefaultPipelinePanel(
|
|
35
|
+
class DefaultPipelinePanel(PipelinePanel):
|
|
36
36
|
def __init__(self):
|
|
37
37
|
super(DefaultPipelinePanel, self).__init__()
|
|
38
38
|
self.set_title(PANEL_TITLE)
|
|
@@ -17,7 +17,7 @@ from PySide6.QtCore import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
-
from mosamatic2.ui.widgets.panels.taskpanel import TaskPanel
|
|
20
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
21
21
|
from mosamatic2.ui.settings import Settings
|
|
22
22
|
from mosamatic2.ui.utils import is_macos
|
|
23
23
|
from mosamatic2.ui.worker import Worker
|
|
@@ -0,0 +1,183 @@
|
|
|
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 CreateDicomSummaryTask
|
|
25
|
+
|
|
26
|
+
LOG = LogManager()
|
|
27
|
+
|
|
28
|
+
PANEL_TITLE = 'CreateDicomSummaryTaskPanel'
|
|
29
|
+
PANEL_NAME = 'createdicomsummarytaskpanel'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CreateDicomSummaryTaskPanel(TaskPanel):
|
|
33
|
+
def __init__(self):
|
|
34
|
+
super(CreateDicomSummaryTaskPanel, self).__init__()
|
|
35
|
+
self.set_title(PANEL_TITLE)
|
|
36
|
+
self._images_dir_line_edit = None
|
|
37
|
+
self._images_dir_select_button = None
|
|
38
|
+
self._output_dir_line_edit = None
|
|
39
|
+
self._output_dir_select_button = None
|
|
40
|
+
self._overwrite_checkbox = None
|
|
41
|
+
self._form_layout = None
|
|
42
|
+
self._run_task_button = None
|
|
43
|
+
self._settings = None
|
|
44
|
+
self._task = None
|
|
45
|
+
self._worker = None
|
|
46
|
+
self._thread = None
|
|
47
|
+
self.init_layout()
|
|
48
|
+
|
|
49
|
+
def images_dir_line_edit(self):
|
|
50
|
+
if not self._images_dir_line_edit:
|
|
51
|
+
self._images_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/images_dir'))
|
|
52
|
+
return self._images_dir_line_edit
|
|
53
|
+
|
|
54
|
+
def images_dir_select_button(self):
|
|
55
|
+
if not self._images_dir_select_button:
|
|
56
|
+
self._images_dir_select_button = QPushButton('Select')
|
|
57
|
+
self._images_dir_select_button.clicked.connect(self.handle_images_dir_select_button)
|
|
58
|
+
return self._images_dir_select_button
|
|
59
|
+
|
|
60
|
+
def output_dir_line_edit(self):
|
|
61
|
+
if not self._output_dir_line_edit:
|
|
62
|
+
self._output_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/output_dir'))
|
|
63
|
+
return self._output_dir_line_edit
|
|
64
|
+
|
|
65
|
+
def output_dir_select_button(self):
|
|
66
|
+
if not self._output_dir_select_button:
|
|
67
|
+
self._output_dir_select_button = QPushButton('Select')
|
|
68
|
+
self._output_dir_select_button.clicked.connect(self.handle_output_dir_select_button)
|
|
69
|
+
return self._output_dir_select_button
|
|
70
|
+
|
|
71
|
+
def overwrite_checkbox(self):
|
|
72
|
+
if not self._overwrite_checkbox:
|
|
73
|
+
self._overwrite_checkbox = QCheckBox('')
|
|
74
|
+
self._overwrite_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/overwrite', True))
|
|
75
|
+
return self._overwrite_checkbox
|
|
76
|
+
|
|
77
|
+
def form_layout(self):
|
|
78
|
+
if not self._form_layout:
|
|
79
|
+
self._form_layout = QFormLayout()
|
|
80
|
+
if is_macos():
|
|
81
|
+
self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
|
|
82
|
+
return self._form_layout
|
|
83
|
+
|
|
84
|
+
def run_task_button(self):
|
|
85
|
+
if not self._run_task_button:
|
|
86
|
+
self._run_task_button = QPushButton('Run task')
|
|
87
|
+
self._run_task_button.clicked.connect(self.handle_run_task_button)
|
|
88
|
+
return self._run_task_button
|
|
89
|
+
|
|
90
|
+
def settings(self):
|
|
91
|
+
if not self._settings:
|
|
92
|
+
self._settings = Settings()
|
|
93
|
+
return self._settings
|
|
94
|
+
|
|
95
|
+
def init_layout(self):
|
|
96
|
+
images_dir_layout = QHBoxLayout()
|
|
97
|
+
images_dir_layout.addWidget(self.images_dir_line_edit())
|
|
98
|
+
images_dir_layout.addWidget(self.images_dir_select_button())
|
|
99
|
+
output_dir_layout = QHBoxLayout()
|
|
100
|
+
output_dir_layout.addWidget(self.output_dir_line_edit())
|
|
101
|
+
output_dir_layout.addWidget(self.output_dir_select_button())
|
|
102
|
+
self.form_layout().addRow('Images directory', images_dir_layout)
|
|
103
|
+
self.form_layout().addRow('Output directory', output_dir_layout)
|
|
104
|
+
self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
|
|
105
|
+
layout = QVBoxLayout()
|
|
106
|
+
layout.addLayout(self.form_layout())
|
|
107
|
+
layout.addWidget(self.run_task_button())
|
|
108
|
+
self.setLayout(layout)
|
|
109
|
+
self.setObjectName(PANEL_NAME)
|
|
110
|
+
|
|
111
|
+
def handle_images_dir_select_button(self):
|
|
112
|
+
last_directory = self.settings().get('last_directory')
|
|
113
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
114
|
+
if directory:
|
|
115
|
+
self.images_dir_line_edit().setText(directory)
|
|
116
|
+
self.settings().set('last_directory', directory)
|
|
117
|
+
|
|
118
|
+
def handle_output_dir_select_button(self):
|
|
119
|
+
last_directory = self.settings().get('last_directory')
|
|
120
|
+
directory = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
121
|
+
if directory:
|
|
122
|
+
self.output_dir_line_edit().setText(directory)
|
|
123
|
+
self.settings().set('last_directory', directory)
|
|
124
|
+
|
|
125
|
+
def handle_run_task_button(self):
|
|
126
|
+
errors = self.check_inputs_and_parameters()
|
|
127
|
+
if len(errors) > 0:
|
|
128
|
+
error_message = 'Following errors were encountered:\n'
|
|
129
|
+
for error in errors:
|
|
130
|
+
error_message += f' - {error}\n'
|
|
131
|
+
QMessageBox.information(self, 'Error', error_message)
|
|
132
|
+
else:
|
|
133
|
+
LOG.info('Running task...')
|
|
134
|
+
self.run_task_button().setEnabled(False)
|
|
135
|
+
self.save_inputs_and_parameters()
|
|
136
|
+
self._task = CreateDicomSummaryTask(
|
|
137
|
+
inputs={'directory': self.images_dir_line_edit().text()},
|
|
138
|
+
params=None,
|
|
139
|
+
output=self.output_dir_line_edit().text(),
|
|
140
|
+
overwrite=self.overwrite_checkbox().isChecked(),
|
|
141
|
+
)
|
|
142
|
+
self._worker = Worker(self._task)
|
|
143
|
+
self._thread = QThread()
|
|
144
|
+
self._worker.moveToThread(self._thread)
|
|
145
|
+
self._thread.started.connect(self._worker.run)
|
|
146
|
+
self._worker.progress.connect(self.handle_progress)
|
|
147
|
+
self._worker.status.connect(self.handle_status)
|
|
148
|
+
self._worker.finished.connect(self.handle_finished)
|
|
149
|
+
self._worker.finished.connect(self._thread.quit)
|
|
150
|
+
self._worker.finished.connect(self._worker.deleteLater)
|
|
151
|
+
self._thread.finished.connect(self._thread.deleteLater)
|
|
152
|
+
self._thread.start()
|
|
153
|
+
|
|
154
|
+
@Slot(int)
|
|
155
|
+
def handle_progress(self, progress):
|
|
156
|
+
LOG.info(f'Progress: {progress} / 100%')
|
|
157
|
+
|
|
158
|
+
@Slot(str)
|
|
159
|
+
def handle_status(self, status):
|
|
160
|
+
LOG.info(f'Status: {status}')
|
|
161
|
+
|
|
162
|
+
@Slot()
|
|
163
|
+
def handle_finished(self):
|
|
164
|
+
self.run_task_button().setEnabled(True)
|
|
165
|
+
|
|
166
|
+
# HELPERS
|
|
167
|
+
|
|
168
|
+
def check_inputs_and_parameters(self):
|
|
169
|
+
errors = []
|
|
170
|
+
if self.images_dir_line_edit().text() == '':
|
|
171
|
+
errors.append('Empty images directory path')
|
|
172
|
+
if not os.path.isdir(self.images_dir_line_edit().text()):
|
|
173
|
+
errors.append('Images directory does not exist')
|
|
174
|
+
if self.output_dir_line_edit().text() == '':
|
|
175
|
+
errors.append('Empty output directory path')
|
|
176
|
+
if os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
|
|
177
|
+
errors.append('Output directory exists but overwrite=False. Please remove output directory first')
|
|
178
|
+
return errors
|
|
179
|
+
|
|
180
|
+
def save_inputs_and_parameters(self):
|
|
181
|
+
self.settings().set(f'{PANEL_NAME}/images_dir', self.images_dir_line_edit().text())
|
|
182
|
+
self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
|
|
183
|
+
self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
|
|
@@ -17,7 +17,7 @@ from PySide6.QtCore import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
-
from mosamatic2.ui.widgets.panels.taskpanel import TaskPanel
|
|
20
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
21
21
|
from mosamatic2.ui.settings import Settings
|
|
22
22
|
from mosamatic2.ui.utils import is_macos
|
|
23
23
|
from mosamatic2.ui.worker import Worker
|
|
@@ -17,7 +17,7 @@ from PySide6.QtCore import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
-
from mosamatic2.ui.widgets.panels.taskpanel import TaskPanel
|
|
20
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
21
21
|
from mosamatic2.ui.settings import Settings
|
|
22
22
|
from mosamatic2.ui.utils import is_macos
|
|
23
23
|
from mosamatic2.ui.worker import Worker
|
|
@@ -17,7 +17,7 @@ from PySide6.QtCore import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
-
from mosamatic2.ui.widgets.panels.taskpanel import TaskPanel
|
|
20
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
21
21
|
from mosamatic2.ui.settings import Settings
|
|
22
22
|
from mosamatic2.ui.utils import is_macos
|
|
23
23
|
from mosamatic2.ui.worker import Worker
|
|
@@ -16,7 +16,7 @@ from PySide6.QtCore import (
|
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
19
|
-
from mosamatic2.ui.widgets.panels.taskpanel import TaskPanel
|
|
19
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
20
20
|
from mosamatic2.ui.settings import Settings
|
|
21
21
|
from mosamatic2.ui.utils import is_macos
|
|
22
22
|
from mosamatic2.ui.worker import Worker
|
|
@@ -17,7 +17,7 @@ from PySide6.QtCore import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
-
from mosamatic2.ui.widgets.panels.taskpanel import TaskPanel
|
|
20
|
+
from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
|
|
21
21
|
from mosamatic2.ui.settings import Settings
|
|
22
22
|
from mosamatic2.ui.utils import is_macos
|
|
23
23
|
from mosamatic2.ui.worker import Worker
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import vtk
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CustomInteractorStyle(vtk.vtkInteractorStyleImage):
|
|
5
|
+
def __init__(self, image_data, slice_mapper, status_actor, slice_obj, orientation="axial"):
|
|
6
|
+
super(CustomInteractorStyle, self).__init__()
|
|
7
|
+
self.AddObserver("MouseWheelForwardEvent", self.move_slice_forward)
|
|
8
|
+
self.AddObserver("MouseWheelBackwardEvent", self.move_slice_backward)
|
|
9
|
+
self.AddObserver("MouseMoveEvent", self.update_overlay, 1.0)
|
|
10
|
+
self.AddObserver("KeyPressEvent", self.key_press_event)
|
|
11
|
+
|
|
12
|
+
self.image_data = image_data
|
|
13
|
+
self.slice_mapper = slice_mapper
|
|
14
|
+
self.status_actor = status_actor
|
|
15
|
+
self.slice_obj = slice_obj
|
|
16
|
+
self.orientation = orientation
|
|
17
|
+
|
|
18
|
+
xmin, xmax, ymin, ymax, zmin, zmax = image_data.GetExtent()
|
|
19
|
+
if orientation == "axial":
|
|
20
|
+
self.min_slice, self.max_slice = zmin, zmax
|
|
21
|
+
elif orientation == "sagittal":
|
|
22
|
+
self.min_slice, self.max_slice = xmin, xmax
|
|
23
|
+
elif orientation == "coronal":
|
|
24
|
+
self.min_slice, self.max_slice = ymin, ymax
|
|
25
|
+
else:
|
|
26
|
+
raise ValueError(f"Unknown orientation: {orientation}")
|
|
27
|
+
|
|
28
|
+
self.slice = (self.min_slice + self.max_slice) // 2
|
|
29
|
+
self.slice_mapper.SetSliceNumber(self.slice)
|
|
30
|
+
self.update_status_message()
|
|
31
|
+
|
|
32
|
+
def update_status_message(self):
|
|
33
|
+
window = int(self.slice_obj.GetProperty().GetColorWindow())
|
|
34
|
+
level = int(self.slice_obj.GetProperty().GetColorLevel())
|
|
35
|
+
message = f'Slice {self.slice + 1}/{self.max_slice + 1} | W: {window} L: {level}'
|
|
36
|
+
self.status_actor.GetMapper().SetInput(message)
|
|
37
|
+
|
|
38
|
+
def move_slice_forward(self, obj, event):
|
|
39
|
+
if self.slice < self.max_slice:
|
|
40
|
+
self.slice += 1
|
|
41
|
+
self.slice_mapper.SetSliceNumber(self.slice)
|
|
42
|
+
self.update_status_message()
|
|
43
|
+
self.GetInteractor().GetRenderWindow().Render()
|
|
44
|
+
|
|
45
|
+
def move_slice_backward(self, obj, event):
|
|
46
|
+
if self.slice > self.min_slice:
|
|
47
|
+
self.slice -= 1
|
|
48
|
+
self.slice_mapper.SetSliceNumber(self.slice)
|
|
49
|
+
self.update_status_message()
|
|
50
|
+
self.GetInteractor().GetRenderWindow().Render()
|
|
51
|
+
|
|
52
|
+
def key_press_event(self, obj, event):
|
|
53
|
+
key = self.GetInteractor().GetKeySym()
|
|
54
|
+
if key == "Up":
|
|
55
|
+
self.move_slice_forward(obj, event)
|
|
56
|
+
elif key == "Down":
|
|
57
|
+
self.move_slice_backward(obj, event)
|
|
58
|
+
|
|
59
|
+
def update_overlay(self, obj, event):
|
|
60
|
+
super(CustomInteractorStyle, self).OnMouseMove()
|
|
61
|
+
self.update_status_message()
|
|
62
|
+
self.GetInteractor().GetRenderWindow().Render()
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import vtk
|
|
3
|
+
import pydicom
|
|
4
|
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QMessageBox
|
|
5
|
+
from mosamatic2.ui.widgets.panels.visualizations.slicevisualization.custominteractorstyle import CustomInteractorStyle
|
|
6
|
+
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
|
|
7
|
+
|
|
8
|
+
COLOR_WINDOW = 400
|
|
9
|
+
COLOR_LEVEL = 40
|
|
10
|
+
IMAGE_SHIFT_SCALE = -1000
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SliceViewer(QWidget):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super(SliceViewer, self).__init__()
|
|
16
|
+
self._nifti_file_or_dicom_dir = None
|
|
17
|
+
self._view_orientation = 'axial'
|
|
18
|
+
self._vtk_widget = QVTKRenderWindowInteractor(self)
|
|
19
|
+
self._render_window = self._vtk_widget.GetRenderWindow()
|
|
20
|
+
self._interactor = self._render_window.GetInteractor()
|
|
21
|
+
self._interactor_style = None
|
|
22
|
+
layout = QVBoxLayout()
|
|
23
|
+
layout.addWidget(self._vtk_widget)
|
|
24
|
+
self.setLayout(layout)
|
|
25
|
+
self._default_renderer = vtk.vtkRenderer()
|
|
26
|
+
self._default_renderer.SetBackground(0.0, 0.0, 0.0) # black
|
|
27
|
+
self._render_window.AddRenderer(self._default_renderer)
|
|
28
|
+
self._render_window.Render()
|
|
29
|
+
|
|
30
|
+
def nifti_file_or_dicom_dir(self):
|
|
31
|
+
return self._nifti_file_or_dicom_dir
|
|
32
|
+
|
|
33
|
+
def set_nifti_file_or_dicom_dir(self, nifti_file_or_dicom_dir):
|
|
34
|
+
self._nifti_file_or_dicom_dir = nifti_file_or_dicom_dir
|
|
35
|
+
|
|
36
|
+
def create_text_actor(self, text, x, y, font_size, align_bottom=False, normalized=False):
|
|
37
|
+
text_prop = vtk.vtkTextProperty()
|
|
38
|
+
text_prop.SetFontFamilyToCourier()
|
|
39
|
+
text_prop.SetFontSize(font_size)
|
|
40
|
+
text_prop.SetVerticalJustificationToBottom() if align_bottom else text_prop.SetVerticalJustificationToTop()
|
|
41
|
+
text_prop.SetJustificationToLeft()
|
|
42
|
+
text_mapper = vtk.vtkTextMapper()
|
|
43
|
+
text_mapper.SetInput(text)
|
|
44
|
+
text_mapper.SetTextProperty(text_prop)
|
|
45
|
+
text_actor = vtk.vtkActor2D()
|
|
46
|
+
text_actor.SetMapper(text_mapper)
|
|
47
|
+
if normalized:
|
|
48
|
+
text_actor.GetPositionCoordinate().SetCoordinateSystemToNormalizedDisplay()
|
|
49
|
+
text_actor.SetPosition(x, y)
|
|
50
|
+
return text_actor
|
|
51
|
+
|
|
52
|
+
def is_nifti_file(self, file_path):
|
|
53
|
+
return file_path.endswith('.nii') or file_path.endswith('.nii.gz')
|
|
54
|
+
|
|
55
|
+
def is_dicom_dir(self, dir_path):
|
|
56
|
+
first_dicom_file = os.path.join(dir_path, os.listdir(dir_path)[0])
|
|
57
|
+
return pydicom.dcmread(first_dicom_file, stop_before_pixels=True)
|
|
58
|
+
|
|
59
|
+
def load_image(self):
|
|
60
|
+
if not self.nifti_file_or_dicom_dir():
|
|
61
|
+
QMessageBox.warning(self, 'Warning', 'No NIFTI file or DICOM directory set')
|
|
62
|
+
return
|
|
63
|
+
if self.is_nifti_file(self.nifti_file_or_dicom_dir()):
|
|
64
|
+
reader = vtk.vtkNIFTIImageReader()
|
|
65
|
+
reader.SetFileName(self.nifti_file_or_dicom_dir())
|
|
66
|
+
elif self.is_dicom_dir(self.nifti_file_or_dicom_dir()):
|
|
67
|
+
reader = vtk.vtkDICOMImageReader()
|
|
68
|
+
reader.SetDirectoryName(self.nifti_file_or_dicom_dir())
|
|
69
|
+
reader.Update()
|
|
70
|
+
image_data = reader.GetOutput()
|
|
71
|
+
xmin, xmax, ymin, ymax, zmin, zmax = image_data.GetExtent()
|
|
72
|
+
axial_index = (zmin + zmax) // 2
|
|
73
|
+
slice_mapper = vtk.vtkImageSliceMapper()
|
|
74
|
+
slice_mapper.SetInputData(image_data)
|
|
75
|
+
slice_mapper.SetOrientationToZ() # axial orientation
|
|
76
|
+
slice_mapper.SetSliceNumber(axial_index)
|
|
77
|
+
slice = vtk.vtkImageSlice()
|
|
78
|
+
slice.GetProperty().SetColorWindow(400)
|
|
79
|
+
slice.GetProperty().SetColorLevel(40)
|
|
80
|
+
slice.SetMapper(slice_mapper)
|
|
81
|
+
slice_text_actor = self.create_text_actor("", 0.01, 0.01, 12, align_bottom=True, normalized=True)
|
|
82
|
+
usage_text_actor = self.create_text_actor(
|
|
83
|
+
"- Slice with mouse wheel or Up/Down-Key (first click inside viewer)\n"
|
|
84
|
+
"- Zoom with pressed right mouse button while dragging\n"
|
|
85
|
+
"- Pan with middle mouse button while dragging\n"
|
|
86
|
+
"- Change contrast/brightness with pressed left mouse while dragging",
|
|
87
|
+
0.01, 0.99, 12, normalized=True)
|
|
88
|
+
ren = vtk.vtkRenderer()
|
|
89
|
+
ren.AddActor2D(slice_text_actor)
|
|
90
|
+
ren.AddActor2D(usage_text_actor)
|
|
91
|
+
ren.AddViewProp(slice)
|
|
92
|
+
ren.ResetCamera()
|
|
93
|
+
self._render_window.RemoveRenderer(self._default_renderer)
|
|
94
|
+
self._render_window.AddRenderer(ren)
|
|
95
|
+
self._interactor_style = CustomInteractorStyle(image_data, slice_mapper, slice_text_actor, slice)
|
|
96
|
+
self._interactor.SetInteractorStyle(self._interactor_style)
|
|
97
|
+
self._interactor.Initialize()
|
|
98
|
+
self._render_window.Render()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from PySide6.QtWidgets import (
|
|
2
|
+
QLineEdit,
|
|
3
|
+
QHBoxLayout,
|
|
4
|
+
QVBoxLayout,
|
|
5
|
+
QFormLayout,
|
|
6
|
+
QPushButton,
|
|
7
|
+
QFileDialog,
|
|
8
|
+
)
|
|
9
|
+
from mosamatic2.ui.widgets.panels.visualizations.visualization import Visualization
|
|
10
|
+
from mosamatic2.ui.widgets.panels.visualizations.slicevisualization.sliceviewer import SliceViewer
|
|
11
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
12
|
+
from mosamatic2.ui.settings import Settings
|
|
13
|
+
from mosamatic2.ui.utils import is_macos
|
|
14
|
+
|
|
15
|
+
LOG = LogManager()
|
|
16
|
+
PANEL_TITLE = 'SliceVisualization'
|
|
17
|
+
PANEL_NAME = 'slicevisualization'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SliceVisualization(Visualization):
|
|
21
|
+
def __init__(self):
|
|
22
|
+
super(SliceVisualization, self).__init__()
|
|
23
|
+
self.set_title(PANEL_TITLE)
|
|
24
|
+
self._image_line_edit = None
|
|
25
|
+
self._image_select_button = None
|
|
26
|
+
self._image_dir_select_button = None
|
|
27
|
+
self._load_image_button = None
|
|
28
|
+
self._slice_viewer = None
|
|
29
|
+
self._form_layout = None
|
|
30
|
+
self._settings = None
|
|
31
|
+
self.init_layout()
|
|
32
|
+
|
|
33
|
+
def image_line_edit(self):
|
|
34
|
+
if not self._image_line_edit:
|
|
35
|
+
self._image_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/image'))
|
|
36
|
+
return self._image_line_edit
|
|
37
|
+
|
|
38
|
+
def image_select_button(self):
|
|
39
|
+
if not self._image_select_button:
|
|
40
|
+
self._image_select_button = QPushButton('Select Image')
|
|
41
|
+
self._image_select_button.clicked.connect(self.handle_image_select_button)
|
|
42
|
+
return self._image_select_button
|
|
43
|
+
|
|
44
|
+
def image_dir_select_button(self):
|
|
45
|
+
if not self._image_dir_select_button:
|
|
46
|
+
self._image_dir_select_button = QPushButton('Select Directory')
|
|
47
|
+
self._image_dir_select_button.clicked.connect(self.handle_image_dir_select_button)
|
|
48
|
+
return self._image_dir_select_button
|
|
49
|
+
|
|
50
|
+
def load_image_button(self):
|
|
51
|
+
if not self._load_image_button:
|
|
52
|
+
self._load_image_button = QPushButton('Load')
|
|
53
|
+
self._load_image_button.clicked.connect(self.handle_load_image_button)
|
|
54
|
+
return self._load_image_button
|
|
55
|
+
|
|
56
|
+
def slice_viewer(self):
|
|
57
|
+
if not self._slice_viewer:
|
|
58
|
+
self._slice_viewer = SliceViewer()
|
|
59
|
+
return self._slice_viewer
|
|
60
|
+
|
|
61
|
+
def form_layout(self):
|
|
62
|
+
if not self._form_layout:
|
|
63
|
+
self._form_layout = QFormLayout()
|
|
64
|
+
if is_macos():
|
|
65
|
+
self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
|
|
66
|
+
return self._form_layout
|
|
67
|
+
|
|
68
|
+
def settings(self):
|
|
69
|
+
if not self._settings:
|
|
70
|
+
self._settings = Settings()
|
|
71
|
+
return self._settings
|
|
72
|
+
|
|
73
|
+
def init_layout(self):
|
|
74
|
+
image_layout = QHBoxLayout()
|
|
75
|
+
image_layout.addWidget(self.image_line_edit())
|
|
76
|
+
image_layout.addWidget(self.image_select_button())
|
|
77
|
+
image_layout.addWidget(self.image_dir_select_button())
|
|
78
|
+
self.form_layout().addRow('NIFTI file or DICOM directory', image_layout)
|
|
79
|
+
layout = QVBoxLayout()
|
|
80
|
+
layout.addLayout(self.form_layout())
|
|
81
|
+
layout.addWidget(self.load_image_button())
|
|
82
|
+
layout.addWidget(self.slice_viewer())
|
|
83
|
+
self.setLayout(layout)
|
|
84
|
+
self.setObjectName(PANEL_NAME)
|
|
85
|
+
|
|
86
|
+
def handle_image_select_button(self):
|
|
87
|
+
last_directory = self.settings().get('last_directory')
|
|
88
|
+
file_path, _ = QFileDialog.getOpenFileName(dir=last_directory)
|
|
89
|
+
if file_path:
|
|
90
|
+
self.image_line_edit().setText(file_path)
|
|
91
|
+
self.settings().set('last_directory', file_path)
|
|
92
|
+
|
|
93
|
+
def handle_image_dir_select_button(self):
|
|
94
|
+
last_directory = self.settings().get('last_directory')
|
|
95
|
+
dir_path = QFileDialog.getExistingDirectory(dir=last_directory)
|
|
96
|
+
if dir_path:
|
|
97
|
+
self.image_line_edit().setText(dir_path)
|
|
98
|
+
self.settings().set('last_directory', dir_path)
|
|
99
|
+
|
|
100
|
+
def handle_load_image_button(self):
|
|
101
|
+
self.slice_viewer().set_nifti_file_or_dicom_dir(self.image_line_edit().text())
|
|
102
|
+
# self.slice_viewer().set_view_orientation('axial')
|
|
103
|
+
self.slice_viewer().load_image()
|
|
104
|
+
|
|
105
|
+
def save_inputs_and_parameters(self):
|
|
106
|
+
self.settings().set(f'{PANEL_NAME}/image', self.image_line_edit().text())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mosamatic2
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.7
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Ralph Brecheisen
|
|
6
6
|
Author-email: r.brecheisen@maastrichtuniversity.nl
|
|
@@ -17,6 +17,7 @@ Requires-Dist: pandas (>=2.3.2)
|
|
|
17
17
|
Requires-Dist: pendulum (>=3.1.0)
|
|
18
18
|
Requires-Dist: pillow (>=11.3.0)
|
|
19
19
|
Requires-Dist: pydicom (>=3.0.1)
|
|
20
|
+
Requires-Dist: pyqtgraph (>=0.13.7)
|
|
20
21
|
Requires-Dist: pyside6-essentials (>=6.9)
|
|
21
22
|
Requires-Dist: python-gdcm (>=3.0.26)
|
|
22
23
|
Requires-Dist: scipy (>=1.15.3)
|
|
@@ -30,6 +31,7 @@ Requires-Dist: tensorflow-macos (==2.15.0) ; platform_system == "Darwin" and pla
|
|
|
30
31
|
Requires-Dist: torch (>=2.8.0)
|
|
31
32
|
Requires-Dist: torchvision (>=0.23.0)
|
|
32
33
|
Requires-Dist: totalsegmentator (>=2.11.0)
|
|
34
|
+
Requires-Dist: vtk (>=9.5.1)
|
|
33
35
|
Description-Content-Type: text/markdown
|
|
34
36
|
|
|
35
37
|
|
|
@@ -22,13 +22,16 @@ mosamatic2/core/data/numpyimage.py,sha256=ZbJ9JRAVOgvXzTZ6kscEuGZJqKT-MPdt42IAlm
|
|
|
22
22
|
mosamatic2/core/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
mosamatic2/core/managers/logmanager.py,sha256=NEaXvhl0aILjBbK710GaWanVuuNvB51HpHhE5rgYvng,1391
|
|
24
24
|
mosamatic2/core/managers/logmanagerlistener.py,sha256=Gaig07yjBnyQq9I8sN85olTEeDCDyCFQnEJdwzvmgvc,99
|
|
25
|
-
mosamatic2/core/pipelines/__init__.py,sha256=
|
|
26
|
-
mosamatic2/core/pipelines/defaultpipeline.py,sha256=
|
|
25
|
+
mosamatic2/core/pipelines/__init__.py,sha256=Esb4TQFvY2soU2dfQPgVlhfTx1LdxYrPBiuAKDosHqA,85
|
|
26
|
+
mosamatic2/core/pipelines/defaultpipeline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
mosamatic2/core/pipelines/defaultpipeline/defaultpipeline.py,sha256=EUblRhLZXhslz9bdBmx8v9yjpgOdsHviqQZ5elK8gNQ,2901
|
|
27
28
|
mosamatic2/core/pipelines/pipeline.py,sha256=mRxKXLKwgKDpc8R9mCI6gDKGJ2lKVxRQ__Sf0Mfn_Qc,384
|
|
28
29
|
mosamatic2/core/singleton.py,sha256=FV0k_LlOCmFhlWN6gf1c2x7YXWyd8-7DsIMvOKrI6NY,224
|
|
29
|
-
mosamatic2/core/tasks/__init__.py,sha256=
|
|
30
|
+
mosamatic2/core/tasks/__init__.py,sha256=w03AyCk2BQ6gGPBEKhtad6lt9WuwHC-nMztv7wQNwb4,759
|
|
30
31
|
mosamatic2/core/tasks/calculatescorestask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
32
|
mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py,sha256=jEbogrEnEtMbmfQ8KOGSUdGMhdeX69Bv01-82faRIlY,6617
|
|
33
|
+
mosamatic2/core/tasks/createdicomsummarytask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py,sha256=TebP3bI_TAUa93mp5XtnPW5NSVFMvbvb-CQAQnh6RfY,2932
|
|
32
35
|
mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
36
|
mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py,sha256=1UpOsp1CH668BQ0g4tALou_tFgisC306VcvqOKSDuTo,1884
|
|
34
37
|
mosamatic2/core/tasks/dicom2niftitask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -44,12 +47,12 @@ mosamatic2/core/tasks/task.py,sha256=APPnid6dpSGkPuDqU1vm2RIMR5vkpvbP1CPHUMjympg
|
|
|
44
47
|
mosamatic2/core/utils.py,sha256=zh44FNOWxNKuZ4FcM7VIfMvdlzOr4AA8_PJ1r-6_83k,10855
|
|
45
48
|
mosamatic2/server.py,sha256=jiUc7-aFaDMSY2hg6BhMC9C2FU4V2OrMBSoiIAP8Zcs,4158
|
|
46
49
|
mosamatic2/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
|
-
mosamatic2/ui/mainwindow.py,sha256=
|
|
50
|
+
mosamatic2/ui/mainwindow.py,sha256=nV9RqqSZVoVbU2opJIk7E3n4GvZWtuDETxmzwzmx4mg,13099
|
|
48
51
|
mosamatic2/ui/resources/icons/mosamatic2.icns,sha256=OfhC-diJTIgaNMOezxKKilGsY7mRkaGdU5dGr0MOjIA,2994125
|
|
49
52
|
mosamatic2/ui/resources/icons/mosamatic2.ico,sha256=ySD3RYluHK3pgS0Eas7eKrVk_AskdLQ4qs_IT-wNhq4,12229
|
|
50
53
|
mosamatic2/ui/resources/icons/spinner.gif,sha256=rvaac6GUZauHSPFSOLWr0RmLfjmtZih2Q8knQ2WP3Po,16240
|
|
51
54
|
mosamatic2/ui/resources/images/body-composition.jpg,sha256=KD-BudbXwThB4lJOZZN-ad5-TZRaaZ5cKTH0Ar1TOZs,21227
|
|
52
|
-
mosamatic2/ui/resources/VERSION,sha256=
|
|
55
|
+
mosamatic2/ui/resources/VERSION,sha256=KwH-299m3oBSV3-QDLRQdLqL7fYkxUJhV8qlxIIJb74,8
|
|
53
56
|
mosamatic2/ui/settings.py,sha256=YEVHYJIfNsqMO3v1pjzgh7Pih9GGoUX7S9s8S-sBNUk,2121
|
|
54
57
|
mosamatic2/ui/utils.py,sha256=6bbPIrh4RJ_yhQKNZrgPbL4XeUEogjIjbk_e5c3QS5g,853
|
|
55
58
|
mosamatic2/ui/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -61,19 +64,27 @@ mosamatic2/ui/widgets/panels/defaultpanel.py,sha256=Ry32Xv6ibxm6NdZ7eBOCcisWNcmn
|
|
|
61
64
|
mosamatic2/ui/widgets/panels/logpanel.py,sha256=ogswJ6_ryb6u7JeVnOsh2Ez8KWg6jtCFZwij8s87xO4,1861
|
|
62
65
|
mosamatic2/ui/widgets/panels/mainpanel.py,sha256=KqI8dA7GpLFd2unqVRTBkNxdnh6AWGpVPwQuaEg8PmI,2431
|
|
63
66
|
mosamatic2/ui/widgets/panels/pipelines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
-
mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py,sha256=
|
|
67
|
+
mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py,sha256=qkfI4BnLIXqE_YvSQj4sO_FjnK0eVdIMqAZ8sktgI-8,13727
|
|
68
|
+
mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py,sha256=SlkKme8Wv2Bvp2Alen98mFjv3F5eZCwJylj294gd5uU,178
|
|
65
69
|
mosamatic2/ui/widgets/panels/stackedpanel.py,sha256=dK1YWuHUzxRhVb5gP0Lu9rAiW4XagjcHmGF__5Lpufk,657
|
|
66
|
-
mosamatic2/ui/widgets/panels/taskpanel.py,sha256=t8lIx1P8sS1Fa-aNm6eEha6297pJQNbBRizkobBexz8,170
|
|
67
70
|
mosamatic2/ui/widgets/panels/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
|
-
mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py,sha256=
|
|
69
|
-
mosamatic2/ui/widgets/panels/tasks/
|
|
70
|
-
mosamatic2/ui/widgets/panels/tasks/
|
|
71
|
-
mosamatic2/ui/widgets/panels/tasks/
|
|
72
|
-
mosamatic2/ui/widgets/panels/tasks/
|
|
73
|
-
mosamatic2/ui/widgets/panels/tasks/
|
|
71
|
+
mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py,sha256=NmPLQizj4x9jgf9UA7VZSjARNHYJB_jrfB0kvaVncdw,9387
|
|
72
|
+
mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py,sha256=cRCaEmjjZP6RwEhezj2Axxuv8uAGe7ZzHoU67asoZ5s,7530
|
|
73
|
+
mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py,sha256=JFnmYjPemRtXPXV2fk2cjB45fseN5BZ8gI_T1zVLGV8,7879
|
|
74
|
+
mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py,sha256=gIv5xo0z2EEfFzHI4Q6H87maf-nPIeC8yIxLL5iWuCw,7480
|
|
75
|
+
mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py,sha256=ds7JxynpMeNvqfHKtg1LQR23rb3_Y7xoDLbZ2wl-TMw,7597
|
|
76
|
+
mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py,sha256=QCEZs9lqaE-XAJuyyrfZVnFkNRyjMw6Cfa-6qP9WaV8,9630
|
|
77
|
+
mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py,sha256=iZu_li_7Zr3n8nVp-FI1GFV_HuCf_I4NY9PNr9aX-oY,8077
|
|
78
|
+
mosamatic2/ui/widgets/panels/tasks/taskpanel.py,sha256=t8lIx1P8sS1Fa-aNm6eEha6297pJQNbBRizkobBexz8,170
|
|
79
|
+
mosamatic2/ui/widgets/panels/visualizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
|
+
mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
81
|
+
mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py,sha256=GaXmO5SvXwLAnrVtAGglTXgPx6mD0Q6f8gYrGN5WRO4,2644
|
|
82
|
+
mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py,sha256=DLvTQ-VboPqjViA9EzGTSm4eGmrgYPH0degpxprEWI4,4600
|
|
83
|
+
mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py,sha256=2cLTHsGrdMMt_dbnq0EUAFSSW7naR6B1neB7RnUOmjQ,4214
|
|
84
|
+
mosamatic2/ui/widgets/panels/visualizations/visualization.py,sha256=JvqTJi7cCGYK1-wrN2oURdCOBoPS2clVUyYglhkoVJg,178
|
|
74
85
|
mosamatic2/ui/widgets/splashscreen.py,sha256=MS-OczOWfwwEQNQd-JWe9_Mh57css0cSQgbu973rwQo,4056
|
|
75
86
|
mosamatic2/ui/worker.py,sha256=v7e3gq7MUudgpB1BJW-P7j5wurzu6-HG5m7I6WHgJp0,699
|
|
76
|
-
mosamatic2-2.0.
|
|
77
|
-
mosamatic2-2.0.
|
|
78
|
-
mosamatic2-2.0.
|
|
79
|
-
mosamatic2-2.0.
|
|
87
|
+
mosamatic2-2.0.7.dist-info/entry_points.txt,sha256=MCUpKkgbej1clgp8EqlLQGs0BIKwGPcBPiVWLfGz9Gw,126
|
|
88
|
+
mosamatic2-2.0.7.dist-info/METADATA,sha256=aX9U18Z7h4eR4CR3bRliqeGp8VIwSiBGvw9d42MpowU,1467
|
|
89
|
+
mosamatic2-2.0.7.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
90
|
+
mosamatic2-2.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|