mosamatic2 2.0.24__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.
- models.py +259 -0
- mosamatic2/__init__.py +0 -0
- mosamatic2/app.py +32 -0
- mosamatic2/cli.py +50 -0
- mosamatic2/commands/__init__.py +0 -0
- mosamatic2/commands/boadockerpipeline.py +48 -0
- mosamatic2/commands/calculatemaskstatistics.py +59 -0
- mosamatic2/commands/calculatescores.py +73 -0
- mosamatic2/commands/createdicomsummary.py +61 -0
- mosamatic2/commands/createpngsfromsegmentations.py +65 -0
- mosamatic2/commands/defaultdockerpipeline.py +84 -0
- mosamatic2/commands/defaultpipeline.py +70 -0
- mosamatic2/commands/dicom2nifti.py +55 -0
- mosamatic2/commands/liveranalysispipeline.py +61 -0
- mosamatic2/commands/rescaledicomimages.py +54 -0
- mosamatic2/commands/segmentmusclefatl3tensorflow.py +55 -0
- mosamatic2/commands/selectslicefromscans.py +66 -0
- mosamatic2/commands/totalsegmentator.py +77 -0
- mosamatic2/constants.py +27 -0
- mosamatic2/core/__init__.py +0 -0
- mosamatic2/core/data/__init__.py +5 -0
- mosamatic2/core/data/dicomimage.py +27 -0
- mosamatic2/core/data/dicomimageseries.py +26 -0
- mosamatic2/core/data/dixonseries.py +22 -0
- mosamatic2/core/data/filedata.py +26 -0
- mosamatic2/core/data/multidicomimage.py +30 -0
- mosamatic2/core/data/multiniftiimage.py +26 -0
- mosamatic2/core/data/multinumpyimage.py +26 -0
- mosamatic2/core/data/niftiimage.py +13 -0
- mosamatic2/core/data/numpyimage.py +13 -0
- mosamatic2/core/managers/__init__.py +0 -0
- mosamatic2/core/managers/logmanager.py +45 -0
- mosamatic2/core/managers/logmanagerlistener.py +3 -0
- mosamatic2/core/pipelines/__init__.py +4 -0
- mosamatic2/core/pipelines/boadockerpipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/boadockerpipeline/boadockerpipeline.py +70 -0
- mosamatic2/core/pipelines/defaultdockerpipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py +28 -0
- mosamatic2/core/pipelines/defaultpipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/defaultpipeline/defaultpipeline.py +90 -0
- mosamatic2/core/pipelines/liveranalysispipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/liveranalysispipeline/liveranalysispipeline.py +48 -0
- mosamatic2/core/pipelines/pipeline.py +14 -0
- mosamatic2/core/singleton.py +9 -0
- mosamatic2/core/tasks/__init__.py +13 -0
- mosamatic2/core/tasks/applythresholdtosegmentationstask/__init__.py +0 -0
- mosamatic2/core/tasks/applythresholdtosegmentationstask/applythresholdtosegmentationstask.py +117 -0
- mosamatic2/core/tasks/calculatemaskstatisticstask/__init__.py +0 -0
- mosamatic2/core/tasks/calculatemaskstatisticstask/calculatemaskstatisticstask.py +104 -0
- mosamatic2/core/tasks/calculatescorestask/__init__.py +0 -0
- mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +152 -0
- mosamatic2/core/tasks/createdicomsummarytask/__init__.py +0 -0
- mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py +88 -0
- mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py +0 -0
- mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +101 -0
- mosamatic2/core/tasks/dicom2niftitask/__init__.py +0 -0
- mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +45 -0
- mosamatic2/core/tasks/rescaledicomimagestask/__init__.py +0 -0
- mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +64 -0
- mosamatic2/core/tasks/segmentationnifti2numpytask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentationnifti2numpytask/segmentationnifti2numpytask.py +57 -0
- mosamatic2/core/tasks/segmentationnumpy2niftitask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentationnumpy2niftitask/segmentationnumpy2niftitask.py +86 -0
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +39 -0
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +122 -0
- mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/paramloader.py +39 -0
- mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/segmentmusclefatt4pytorchtask.py +128 -0
- mosamatic2/core/tasks/selectslicefromscanstask/__init__.py +0 -0
- mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py +249 -0
- mosamatic2/core/tasks/task.py +50 -0
- mosamatic2/core/tasks/totalsegmentatortask/__init__.py +0 -0
- mosamatic2/core/tasks/totalsegmentatortask/totalsegmentatortask.py +75 -0
- mosamatic2/core/utils.py +405 -0
- mosamatic2/server.py +146 -0
- mosamatic2/ui/__init__.py +0 -0
- mosamatic2/ui/mainwindow.py +426 -0
- mosamatic2/ui/resources/VERSION +1 -0
- mosamatic2/ui/resources/icons/mosamatic2.icns +0 -0
- mosamatic2/ui/resources/icons/mosamatic2.ico +0 -0
- mosamatic2/ui/resources/icons/spinner.gif +0 -0
- mosamatic2/ui/resources/images/body-composition.jpg +0 -0
- mosamatic2/ui/settings.py +62 -0
- mosamatic2/ui/utils.py +36 -0
- mosamatic2/ui/widgets/__init__.py +0 -0
- mosamatic2/ui/widgets/dialogs/__init__.py +0 -0
- mosamatic2/ui/widgets/dialogs/dialog.py +16 -0
- mosamatic2/ui/widgets/dialogs/helpdialog.py +9 -0
- mosamatic2/ui/widgets/panels/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/defaultpanel.py +31 -0
- mosamatic2/ui/widgets/panels/logpanel.py +65 -0
- mosamatic2/ui/widgets/panels/mainpanel.py +82 -0
- mosamatic2/ui/widgets/panels/pipelines/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/pipelines/boadockerpipelinepanel.py +195 -0
- mosamatic2/ui/widgets/panels/pipelines/defaultdockerpipelinepanel.py +314 -0
- mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +302 -0
- mosamatic2/ui/widgets/panels/pipelines/liveranalysispipelinepanel.py +187 -0
- mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py +6 -0
- mosamatic2/ui/widgets/panels/settingspanel.py +16 -0
- mosamatic2/ui/widgets/panels/stackedpanel.py +22 -0
- mosamatic2/ui/widgets/panels/tasks/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/tasks/applythresholdtosegmentationstaskpanel.py +271 -0
- mosamatic2/ui/widgets/panels/tasks/calculatemaskstatisticstaskpanel.py +215 -0
- mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +238 -0
- mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py +206 -0
- mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +247 -0
- mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +183 -0
- mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +184 -0
- mosamatic2/ui/widgets/panels/tasks/segmentationnifti2numpytaskpanel.py +192 -0
- mosamatic2/ui/widgets/panels/tasks/segmentationnumpy2niftitaskpanel.py +213 -0
- mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +216 -0
- mosamatic2/ui/widgets/panels/tasks/segmentmusclefatt4pytorchtaskpanel.py +217 -0
- mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py +193 -0
- mosamatic2/ui/widgets/panels/tasks/taskpanel.py +6 -0
- mosamatic2/ui/widgets/panels/tasks/totalsegmentatortaskpanel.py +195 -0
- mosamatic2/ui/widgets/panels/visualizations/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentpicker.py +96 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentviewer.py +130 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentvisualization.py +120 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/sliceselectionviewer.py +61 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/sliceselectionvisualization.py +133 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/slicetile.py +63 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +80 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +116 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +141 -0
- mosamatic2/ui/widgets/panels/visualizations/visualization.py +6 -0
- mosamatic2/ui/widgets/splashscreen.py +101 -0
- mosamatic2/ui/worker.py +29 -0
- mosamatic2-2.0.24.dist-info/METADATA +43 -0
- mosamatic2-2.0.24.dist-info/RECORD +136 -0
- mosamatic2-2.0.24.dist-info/WHEEL +4 -0
- mosamatic2-2.0.24.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
3
|
+
from mosamatic2.core.data.filedata import FileData
|
|
4
|
+
from mosamatic2.core.data.dicomimage import DicomImage
|
|
5
|
+
|
|
6
|
+
LOG = LogManager()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MultiDicomImage(FileData):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super(MultiDicomImage, self).__init__()
|
|
12
|
+
self._images = []
|
|
13
|
+
|
|
14
|
+
def images(self):
|
|
15
|
+
return self._images
|
|
16
|
+
|
|
17
|
+
def load(self):
|
|
18
|
+
series_instance_uids = []
|
|
19
|
+
if self.path():
|
|
20
|
+
for f in os.listdir(self.path()):
|
|
21
|
+
f_path = os.path.join(self.path(), f)
|
|
22
|
+
image = DicomImage()
|
|
23
|
+
image.set_path(f_path)
|
|
24
|
+
if image.load():
|
|
25
|
+
series_instance_uid = image.object().SeriesInstanceUID
|
|
26
|
+
if series_instance_uid in series_instance_uids:
|
|
27
|
+
RuntimeError('Cannot load DICOM images with identical series instance UID')
|
|
28
|
+
self._images.append(image)
|
|
29
|
+
return True
|
|
30
|
+
return False
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
3
|
+
from mosamatic2.core.data.filedata import FileData
|
|
4
|
+
from mosamatic2.core.data.niftiimage import NiftiImage
|
|
5
|
+
|
|
6
|
+
LOG = LogManager()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MultiNiftiImage(FileData):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super(MultiNiftiImage, self).__init__()
|
|
12
|
+
self._images = []
|
|
13
|
+
|
|
14
|
+
def images(self):
|
|
15
|
+
return self._images
|
|
16
|
+
|
|
17
|
+
def load(self):
|
|
18
|
+
if self.path():
|
|
19
|
+
for f in os.listdir(self.path()):
|
|
20
|
+
f_path = os.path.join(self.path(), f)
|
|
21
|
+
image = NiftiImage()
|
|
22
|
+
image.set_path(f_path)
|
|
23
|
+
if image.load():
|
|
24
|
+
self._images.append(image)
|
|
25
|
+
return True
|
|
26
|
+
return False
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
3
|
+
from mosamatic2.core.data.filedata import FileData
|
|
4
|
+
from mosamatic2.core.data.numpyimage import NumpyImage
|
|
5
|
+
|
|
6
|
+
LOG = LogManager()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MultiNumpyImage(FileData):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super(MultiNumpyImage, self).__init__()
|
|
12
|
+
self._images = []
|
|
13
|
+
|
|
14
|
+
def images(self):
|
|
15
|
+
return self._images
|
|
16
|
+
|
|
17
|
+
def load(self):
|
|
18
|
+
if self.path():
|
|
19
|
+
for f in os.listdir(self.path()):
|
|
20
|
+
f_path = os.path.join(self.path(), f)
|
|
21
|
+
image = NumpyImage()
|
|
22
|
+
image.set_path(f_path)
|
|
23
|
+
if image.load():
|
|
24
|
+
self._images.append(image)
|
|
25
|
+
return True
|
|
26
|
+
return False
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from mosamatic2.core.data.filedata import FileData
|
|
2
|
+
from mosamatic2.core.utils import (
|
|
3
|
+
is_nifti,
|
|
4
|
+
load_nifti,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
class NiftiImage(FileData):
|
|
8
|
+
def load(self):
|
|
9
|
+
if self.path():
|
|
10
|
+
if is_nifti(self.path()):
|
|
11
|
+
self.set_object(load_nifti(self.path()))
|
|
12
|
+
return True
|
|
13
|
+
return False
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from mosamatic2.core.data.filedata import FileData
|
|
2
|
+
from mosamatic2.core.utils import (
|
|
3
|
+
is_numpy,
|
|
4
|
+
load_numpy_array,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
class NumpyImage(FileData):
|
|
8
|
+
def load(self):
|
|
9
|
+
if self.path():
|
|
10
|
+
if is_numpy(self.path()):
|
|
11
|
+
self.set_object(load_numpy_array(self.path()))
|
|
12
|
+
return True
|
|
13
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import atexit
|
|
3
|
+
import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from mosamatic2.core.singleton import singleton
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@singleton
|
|
9
|
+
class LogManager:
|
|
10
|
+
def __init__(self, suppress_print=False):
|
|
11
|
+
self._suppress_print = suppress_print
|
|
12
|
+
self._listeners = []
|
|
13
|
+
file_path = os.path.join(Path.home(), 'mosamatic2.log')
|
|
14
|
+
self._file_handle = open(file_path, 'w', buffering=1)
|
|
15
|
+
atexit.register(self.close_file)
|
|
16
|
+
|
|
17
|
+
def _log(self, level, message):
|
|
18
|
+
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
19
|
+
message = f'[{timestamp}] {level} : {message}'
|
|
20
|
+
if not self._suppress_print:
|
|
21
|
+
print(message)
|
|
22
|
+
self._file_handle.write(message + '\n')
|
|
23
|
+
self.notify_listeners(message)
|
|
24
|
+
return message
|
|
25
|
+
|
|
26
|
+
def info(self, message):
|
|
27
|
+
return self._log('INFO', message)
|
|
28
|
+
|
|
29
|
+
def warning(self, message):
|
|
30
|
+
return self._log('WARNING', message)
|
|
31
|
+
|
|
32
|
+
def error(self, message):
|
|
33
|
+
return self._log('ERROR', message)
|
|
34
|
+
|
|
35
|
+
def add_listener(self, listener):
|
|
36
|
+
if listener not in self._listeners:
|
|
37
|
+
self._listeners.append(listener)
|
|
38
|
+
|
|
39
|
+
def notify_listeners(self, message):
|
|
40
|
+
for listener in self._listeners:
|
|
41
|
+
listener.new_message(message)
|
|
42
|
+
|
|
43
|
+
def close_file(self):
|
|
44
|
+
if self._file_handle:
|
|
45
|
+
self._file_handle.close()
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
from mosamatic2.core.pipelines.defaultpipeline.defaultpipeline import DefaultPipeline
|
|
2
|
+
from mosamatic2.core.pipelines.defaultdockerpipeline.defaultdockerpipeline import DefaultDockerPipeline
|
|
3
|
+
from mosamatic2.core.pipelines.boadockerpipeline.boadockerpipeline import BoaDockerPipeline
|
|
4
|
+
from mosamatic2.core.pipelines.liveranalysispipeline.liveranalysispipeline import LiverAnalysisPipeline
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.pipelines.pipeline import Pipeline
|
|
3
|
+
from mosamatic2.core.tasks import Dicom2NiftiTask
|
|
4
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
5
|
+
from mosamatic2.core.utils import (
|
|
6
|
+
is_docker_running,
|
|
7
|
+
to_unix_path,
|
|
8
|
+
current_time_in_seconds,
|
|
9
|
+
elapsed_time_in_seconds,
|
|
10
|
+
duration,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
LOG = LogManager()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BoaDockerPipeline(Pipeline):
|
|
17
|
+
INPUTS = ['scans']
|
|
18
|
+
PARAMS = []
|
|
19
|
+
|
|
20
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
21
|
+
super(BoaDockerPipeline, self).__init__(inputs, params, output, overwrite)
|
|
22
|
+
|
|
23
|
+
def load_nifti_files(self):
|
|
24
|
+
nifti_files_dir = os.path.join(self.output(), 'dicom2niftitask')
|
|
25
|
+
nifti_files = []
|
|
26
|
+
for f in os.listdir(nifti_files_dir):
|
|
27
|
+
f_path = os.path.join(nifti_files_dir, f)
|
|
28
|
+
if f.endswith('.nii') or f.endswith('.nii.gz'):
|
|
29
|
+
nifti_files.append(f_path)
|
|
30
|
+
return nifti_files
|
|
31
|
+
|
|
32
|
+
def run(self):
|
|
33
|
+
assert is_docker_running()
|
|
34
|
+
# First convert DICOM series to NIFTI format
|
|
35
|
+
dicom2nifti_task = Dicom2NiftiTask(
|
|
36
|
+
inputs={'scans': self.input('scans')},
|
|
37
|
+
params={'compressed': True},
|
|
38
|
+
output=self.output(),
|
|
39
|
+
overwrite=self.overwrite(),
|
|
40
|
+
)
|
|
41
|
+
dicom2nifti_task.run()
|
|
42
|
+
# Load NIFTI file paths
|
|
43
|
+
nifti_files = self.load_nifti_files()
|
|
44
|
+
workspaces_dir = to_unix_path(os.path.join(self.output(), 'workspaces'))
|
|
45
|
+
os.makedirs(workspaces_dir, exist_ok=True)
|
|
46
|
+
weights_dir = to_unix_path(os.path.join(self.output(), 'weights'))
|
|
47
|
+
start_time = current_time_in_seconds()
|
|
48
|
+
for f in nifti_files:
|
|
49
|
+
start_time_f = current_time_in_seconds()
|
|
50
|
+
f = to_unix_path(f)
|
|
51
|
+
dir_name = os.path.split(f)[1][:-7]
|
|
52
|
+
workspace = to_unix_path(os.path.join(workspaces_dir, dir_name))
|
|
53
|
+
# Build Docker script
|
|
54
|
+
docker_script = 'docker run --rm ' + \
|
|
55
|
+
'-v "{}":/image.nii.gz '.format(f) + \
|
|
56
|
+
'-v "{}":/workspace '.format(workspace) + \
|
|
57
|
+
'-v "{}":/app/weights '.format(weights_dir) + \
|
|
58
|
+
'--gpus all ' + \
|
|
59
|
+
'--network host ' + \
|
|
60
|
+
'--shm-size=8g --ulimit memlock=-1 --ulimit stack=67108864 ' + \
|
|
61
|
+
'--entrypoint /bin/sh ' + \
|
|
62
|
+
'shipai/boa-cli -c ' + \
|
|
63
|
+
'"python body_organ_analysis --input-image /image.nii.gz --output-dir /workspace/ --models all --verbose"'
|
|
64
|
+
LOG.info(f'Running BOA Docker script: {docker_script}')
|
|
65
|
+
# Run Docker script
|
|
66
|
+
os.system(docker_script)
|
|
67
|
+
elapsed_f = elapsed_time_in_seconds(start_time_f)
|
|
68
|
+
LOG.info(f'Elapsed {dir_name}: {duration(elapsed_f)}')
|
|
69
|
+
elapsed_total = elapsed_time_in_seconds(start_time)
|
|
70
|
+
LOG.info(f'Elapsed total: {duration(elapsed_total)}')
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.pipelines.pipeline import Pipeline
|
|
3
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
4
|
+
from mosamatic2.core.utils import is_docker_running, to_unix_path
|
|
5
|
+
|
|
6
|
+
LOG = LogManager()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DefaultDockerPipeline(Pipeline):
|
|
10
|
+
INPUTS = [
|
|
11
|
+
'images',
|
|
12
|
+
'model_files',
|
|
13
|
+
]
|
|
14
|
+
PARAMS = ['version']
|
|
15
|
+
|
|
16
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
17
|
+
super(DefaultDockerPipeline, self).__init__(inputs, params, output, overwrite)
|
|
18
|
+
|
|
19
|
+
def run(self):
|
|
20
|
+
assert is_docker_running()
|
|
21
|
+
docker_script = 'docker run --rm ' + \
|
|
22
|
+
'-v "{}":/data/images '.format(to_unix_path(self.input('images'))) + \
|
|
23
|
+
'-v "{}":/data/model_files '.format(to_unix_path(self.input('model_files'))) + \
|
|
24
|
+
'-v "{}":/data/output '.format(to_unix_path(self.output())) + \
|
|
25
|
+
'brecheisen/mosamatic2-cli:{} defaultpipeline '.format(self.param('version')) + \
|
|
26
|
+
'--images /data/images --model_files /data/model_files --output /data/output --overwrite true'
|
|
27
|
+
LOG.info(f'Running Docker script: {docker_script}')
|
|
28
|
+
os.system(docker_script)
|
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.pipelines.pipeline import Pipeline
|
|
4
|
+
from mosamatic2.core.tasks import (
|
|
5
|
+
RescaleDicomImagesTask,
|
|
6
|
+
SegmentMuscleFatL3TensorFlowTask,
|
|
7
|
+
SegmentMuscleFatL3TensorFlowTask,
|
|
8
|
+
CreatePngsFromSegmentationsTask,
|
|
9
|
+
CalculateScoresTask,
|
|
10
|
+
)
|
|
11
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
12
|
+
|
|
13
|
+
LOG = LogManager()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DefaultPipeline(Pipeline):
|
|
17
|
+
INPUTS = [
|
|
18
|
+
'images',
|
|
19
|
+
'model_files',
|
|
20
|
+
]
|
|
21
|
+
PARAMS = [
|
|
22
|
+
'target_size',
|
|
23
|
+
'file_type',
|
|
24
|
+
'fig_width',
|
|
25
|
+
'fig_height',
|
|
26
|
+
'model_type',
|
|
27
|
+
'model_version',
|
|
28
|
+
'hu_low',
|
|
29
|
+
'hu_high',
|
|
30
|
+
'alpha',
|
|
31
|
+
]
|
|
32
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
33
|
+
super(DefaultPipeline, self).__init__(inputs, params, output, overwrite)
|
|
34
|
+
LOG.info('Found {} images to process'.format(len(os.listdir(self.input('images')))))
|
|
35
|
+
model_type = self.param('model_type')
|
|
36
|
+
# segmentation_task_class = SegmentMuscleFatL3Task if model_type == 'pytorch' else SegmentMuscleFatL3TensorFlowTask
|
|
37
|
+
segmentation_task_class = SegmentMuscleFatL3TensorFlowTask
|
|
38
|
+
self.add_task(
|
|
39
|
+
RescaleDicomImagesTask(
|
|
40
|
+
inputs={'images': self.input('images')},
|
|
41
|
+
params={'target_size': self.param('target_size')},
|
|
42
|
+
output=self.output(),
|
|
43
|
+
overwrite=self.overwrite(),
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
self.add_task(
|
|
47
|
+
segmentation_task_class(
|
|
48
|
+
inputs={
|
|
49
|
+
'images': os.path.join(self.output(), 'rescaledicomimagestask'),
|
|
50
|
+
'model_files': self.input('model_files'),
|
|
51
|
+
},
|
|
52
|
+
params={'model_version': self.param('model_version')},
|
|
53
|
+
output=self.output(),
|
|
54
|
+
overwrite=self.overwrite(),
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
self.add_task(
|
|
58
|
+
CalculateScoresTask(
|
|
59
|
+
inputs={
|
|
60
|
+
'images': os.path.join(self.output(), 'rescaledicomimagestask'),
|
|
61
|
+
'segmentations': os.path.join(
|
|
62
|
+
self.output(),
|
|
63
|
+
'segmentmusclefatl3pytorchtask' if model_type == 'pytorch' else 'segmentmusclefatl3tensorflowtask',
|
|
64
|
+
)
|
|
65
|
+
},
|
|
66
|
+
params={'file_type': self.param('file_type')},
|
|
67
|
+
output=self.output(),
|
|
68
|
+
overwrite=self.overwrite(),
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
self.add_task(
|
|
72
|
+
CreatePngsFromSegmentationsTask(
|
|
73
|
+
inputs={
|
|
74
|
+
'images': os.path.join(self.output(), 'rescaledicomimagestask'),
|
|
75
|
+
'segmentations': os.path.join(
|
|
76
|
+
self.output(),
|
|
77
|
+
'segmentmusclefatl3pytorchtask' if model_type == 'pytorch' else 'segmentmusclefatl3tensorflowtask',
|
|
78
|
+
)
|
|
79
|
+
},
|
|
80
|
+
params={
|
|
81
|
+
'fig_width': self.param('fig_width'),
|
|
82
|
+
'fig_height': self.param('fig_height'),
|
|
83
|
+
'hu_low': self.param('hu_low'),
|
|
84
|
+
'hu_high': self.param('hu_high'),
|
|
85
|
+
'alpha': self.param('alpha'),
|
|
86
|
+
},
|
|
87
|
+
output=self.output(),
|
|
88
|
+
overwrite=self.overwrite(),
|
|
89
|
+
)
|
|
90
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.pipelines.pipeline import Pipeline
|
|
3
|
+
from mosamatic2.core.tasks import (
|
|
4
|
+
Dicom2NiftiTask,
|
|
5
|
+
TotalSegmentatorTask,
|
|
6
|
+
CalculateMaskStatisticsTask,
|
|
7
|
+
)
|
|
8
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
9
|
+
|
|
10
|
+
LOG = LogManager()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LiverAnalysisPipeline(Pipeline):
|
|
14
|
+
INPUTS = ['scans']
|
|
15
|
+
PARAMS = ['compressed']
|
|
16
|
+
|
|
17
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
18
|
+
super(LiverAnalysisPipeline, self).__init__(inputs, params, output, overwrite)
|
|
19
|
+
self.add_task(
|
|
20
|
+
Dicom2NiftiTask(
|
|
21
|
+
inputs={'scans': self.input('scans')},
|
|
22
|
+
params={'compressed': self.param('compressed')},
|
|
23
|
+
output=self.output(),
|
|
24
|
+
overwrite=self.overwrite(),
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
self.add_task(
|
|
28
|
+
TotalSegmentatorTask(
|
|
29
|
+
inputs={'scans': os.path.join(self.output(), 'dicom2niftitask')},
|
|
30
|
+
params={
|
|
31
|
+
'tasks': 'liver_segments',
|
|
32
|
+
'format': 'nifti',
|
|
33
|
+
},
|
|
34
|
+
output=self.output(),
|
|
35
|
+
overwrite=self.overwrite(),
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
self.add_task(
|
|
39
|
+
CalculateMaskStatisticsTask(
|
|
40
|
+
inputs={
|
|
41
|
+
'scans': os.path.join(self.output(), 'dicom2niftitask'),
|
|
42
|
+
'masks': os.path.join(self.output(), 'totalsegmentatortask'),
|
|
43
|
+
},
|
|
44
|
+
params=None,
|
|
45
|
+
output=self.output(),
|
|
46
|
+
overwrite=self.overwrite(),
|
|
47
|
+
)
|
|
48
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from mosamatic2.core.tasks.task import Task
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Pipeline(Task):
|
|
5
|
+
def __init__(self, inputs, params, output, overwrite=False):
|
|
6
|
+
super(Pipeline, self).__init__(inputs, params, output, overwrite)
|
|
7
|
+
self._tasks = []
|
|
8
|
+
|
|
9
|
+
def add_task(self, task):
|
|
10
|
+
self._tasks.append(task)
|
|
11
|
+
|
|
12
|
+
def run(self):
|
|
13
|
+
for task in self._tasks:
|
|
14
|
+
task.run()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from mosamatic2.core.tasks.rescaledicomimagestask.rescaledicomimagestask import RescaleDicomImagesTask
|
|
2
|
+
from mosamatic2.core.tasks.segmentmusclefatl3tensorflowtask.segmentmusclefatl3tensorflowtask import SegmentMuscleFatL3TensorFlowTask
|
|
3
|
+
from mosamatic2.core.tasks.calculatescorestask.calculatescorestask import CalculateScoresTask
|
|
4
|
+
from mosamatic2.core.tasks.createpngsfromsegmentationstask.createpngsfromsegmentationstask import CreatePngsFromSegmentationsTask
|
|
5
|
+
from mosamatic2.core.tasks.dicom2niftitask.dicom2niftitask import Dicom2NiftiTask
|
|
6
|
+
from mosamatic2.core.tasks.selectslicefromscanstask.selectslicefromscanstask import SelectSliceFromScansTask
|
|
7
|
+
from mosamatic2.core.tasks.createdicomsummarytask.createdicomsummarytask import CreateDicomSummaryTask
|
|
8
|
+
from mosamatic2.core.tasks.totalsegmentatortask.totalsegmentatortask import TotalSegmentatorTask
|
|
9
|
+
from mosamatic2.core.tasks.calculatemaskstatisticstask.calculatemaskstatisticstask import CalculateMaskStatisticsTask
|
|
10
|
+
from mosamatic2.core.tasks.segmentmusclefatt4pytorchtask.segmentmusclefatt4pytorchtask import SegmentMuscleFatT4PyTorchTask
|
|
11
|
+
from mosamatic2.core.tasks.segmentationnumpy2niftitask.segmentationnumpy2niftitask import SegmentationNumpy2NiftiTask
|
|
12
|
+
from mosamatic2.core.tasks.segmentationnifti2numpytask.segmentationnifti2numpytask import SegmentationNifti2NumpyTask
|
|
13
|
+
from mosamatic2.core.tasks.applythresholdtosegmentationstask.applythresholdtosegmentationstask import ApplyThresholdToSegmentationsTask
|
|
File without changes
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from mosamatic2.core.tasks.task import Task
|
|
5
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
6
|
+
from mosamatic2.core.data.multidicomimage import MultiDicomImage
|
|
7
|
+
from mosamatic2.core.data.numpyimage import NumpyImage
|
|
8
|
+
from mosamatic2.core.utils import (
|
|
9
|
+
get_pixels_from_dicom_object,
|
|
10
|
+
calculate_area,
|
|
11
|
+
calculate_mean_radiation_attenuation,
|
|
12
|
+
calculate_lama_percentage,
|
|
13
|
+
get_pixels_from_tag_file,
|
|
14
|
+
MUSCLE,
|
|
15
|
+
SAT,
|
|
16
|
+
VAT,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
LOG = LogManager()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ApplyThresholdToSegmentationsTask(Task):
|
|
23
|
+
INPUTS = [
|
|
24
|
+
'images',
|
|
25
|
+
'segmentations'
|
|
26
|
+
]
|
|
27
|
+
PARAMS = [
|
|
28
|
+
'label',
|
|
29
|
+
'threshold_low',
|
|
30
|
+
'threshold_high'
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
def __init__(self, inputs, params, output, overwrite=True):
|
|
34
|
+
super(ApplyThresholdToSegmentationsTask, self).__init__(inputs, params, output, overwrite)
|
|
35
|
+
|
|
36
|
+
def collect_image_segmentation_pairs(self, images, segmentations, file_type='npy'):
|
|
37
|
+
file_type = '.tag' if file_type == 'tag' else '.seg.npy'
|
|
38
|
+
img_seg_pairs = []
|
|
39
|
+
for image in images:
|
|
40
|
+
f_img_path = image.path()
|
|
41
|
+
f_img_name = os.path.split(f_img_path)[1]
|
|
42
|
+
for f_seg_path in segmentations:
|
|
43
|
+
f_seg_name = os.path.split(f_seg_path)[1]
|
|
44
|
+
if file_type == '.seg.npy':
|
|
45
|
+
f_seg_name = f_seg_name.removesuffix(file_type)
|
|
46
|
+
if f_seg_name == f_img_name:
|
|
47
|
+
img_seg_pairs.append((image, f_seg_path))
|
|
48
|
+
elif file_type == '.tag':
|
|
49
|
+
f_seg_name = f_seg_name.removesuffix(file_type).removesuffix('.dcm')
|
|
50
|
+
f_img_name = f_img_name.removesuffix('.dcm')
|
|
51
|
+
if f_seg_name == f_img_name:
|
|
52
|
+
img_seg_pairs.append((image, f_seg_path))
|
|
53
|
+
else:
|
|
54
|
+
raise RuntimeError('Unknown file type')
|
|
55
|
+
return img_seg_pairs
|
|
56
|
+
|
|
57
|
+
def load_images(self):
|
|
58
|
+
image_data = MultiDicomImage()
|
|
59
|
+
image_data.set_path(self.input('images'))
|
|
60
|
+
if image_data.load():
|
|
61
|
+
return image_data
|
|
62
|
+
raise RuntimeError('Could not load images')
|
|
63
|
+
|
|
64
|
+
def load_pixels_and_spacing(self, image):
|
|
65
|
+
p = image.object()
|
|
66
|
+
pixels = get_pixels_from_dicom_object(p, normalize=True)
|
|
67
|
+
return pixels, p.PixelSpacing
|
|
68
|
+
|
|
69
|
+
def load_segmentations(self, file_type='npy'):
|
|
70
|
+
file_type = '.tag' if file_type == 'tag' else '.seg.npy'
|
|
71
|
+
segmentations = []
|
|
72
|
+
for f in os.listdir(self.input('segmentations')):
|
|
73
|
+
f_path = os.path.join(self.input('segmentations'), f)
|
|
74
|
+
if f.endswith(file_type):
|
|
75
|
+
segmentations.append(f_path)
|
|
76
|
+
if len(segmentations) == 0:
|
|
77
|
+
raise RuntimeError('Input directory has no segmentation files')
|
|
78
|
+
return segmentations
|
|
79
|
+
|
|
80
|
+
def load_segmentation(self, f, file_type='npy'):
|
|
81
|
+
if file_type == 'npy':
|
|
82
|
+
segmentation = NumpyImage()
|
|
83
|
+
segmentation.set_path(f)
|
|
84
|
+
if segmentation.load():
|
|
85
|
+
return segmentation.object()
|
|
86
|
+
LOG.error(f'Could not load segmentation file {f}')
|
|
87
|
+
if file_type == 'tag':
|
|
88
|
+
pixels = get_pixels_from_tag_file(f)
|
|
89
|
+
try:
|
|
90
|
+
pixels = pixels.reshape(512, 512)
|
|
91
|
+
return pixels
|
|
92
|
+
except Exception:
|
|
93
|
+
LOG.warning(f'Could not reshape TAG pixels to (512, 512), skipping...')
|
|
94
|
+
return None
|
|
95
|
+
raise RuntimeError('Unknown file type')
|
|
96
|
+
|
|
97
|
+
def run(self):
|
|
98
|
+
images = self.load_images().images()
|
|
99
|
+
segmentations = self.load_segmentations()
|
|
100
|
+
image_segmentation_pairs = self.collect_image_segmentation_pairs(images, segmentations)
|
|
101
|
+
threshold_low = self.param('threshold_low')
|
|
102
|
+
threshold_high = self.param('threshold_high')
|
|
103
|
+
label = self.param('label')
|
|
104
|
+
nr_steps = len(image_segmentation_pairs)
|
|
105
|
+
for step in range(nr_steps):
|
|
106
|
+
image, pixel_spacing = self.load_pixels_and_spacing(image_segmentation_pairs[step][0])
|
|
107
|
+
segmentation = self.load_segmentation(image_segmentation_pairs[step][1])
|
|
108
|
+
area = calculate_area(segmentation, label, pixel_spacing)
|
|
109
|
+
LOG.info(f'area: {area} mm')
|
|
110
|
+
segmentation_new = segmentation.copy()
|
|
111
|
+
segmentation_new[(segmentation_new == label) & (image < threshold_low)] = 0
|
|
112
|
+
area_new = calculate_area(segmentation_new, label, pixel_spacing)
|
|
113
|
+
LOG.info(f'area_new: {area_new} mm')
|
|
114
|
+
segmentation_new_file_name = os.path.split(image_segmentation_pairs[step][0].path())[1]
|
|
115
|
+
segmentation_new_file_path = os.path.join(self.output(), f'{segmentation_new_file_name}.seg.npy')
|
|
116
|
+
np.save(segmentation_new_file_path, segmentation_new)
|
|
117
|
+
self.set_progress(step, nr_steps)
|
|
File without changes
|