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.
Files changed (136) hide show
  1. models.py +259 -0
  2. mosamatic2/__init__.py +0 -0
  3. mosamatic2/app.py +32 -0
  4. mosamatic2/cli.py +50 -0
  5. mosamatic2/commands/__init__.py +0 -0
  6. mosamatic2/commands/boadockerpipeline.py +48 -0
  7. mosamatic2/commands/calculatemaskstatistics.py +59 -0
  8. mosamatic2/commands/calculatescores.py +73 -0
  9. mosamatic2/commands/createdicomsummary.py +61 -0
  10. mosamatic2/commands/createpngsfromsegmentations.py +65 -0
  11. mosamatic2/commands/defaultdockerpipeline.py +84 -0
  12. mosamatic2/commands/defaultpipeline.py +70 -0
  13. mosamatic2/commands/dicom2nifti.py +55 -0
  14. mosamatic2/commands/liveranalysispipeline.py +61 -0
  15. mosamatic2/commands/rescaledicomimages.py +54 -0
  16. mosamatic2/commands/segmentmusclefatl3tensorflow.py +55 -0
  17. mosamatic2/commands/selectslicefromscans.py +66 -0
  18. mosamatic2/commands/totalsegmentator.py +77 -0
  19. mosamatic2/constants.py +27 -0
  20. mosamatic2/core/__init__.py +0 -0
  21. mosamatic2/core/data/__init__.py +5 -0
  22. mosamatic2/core/data/dicomimage.py +27 -0
  23. mosamatic2/core/data/dicomimageseries.py +26 -0
  24. mosamatic2/core/data/dixonseries.py +22 -0
  25. mosamatic2/core/data/filedata.py +26 -0
  26. mosamatic2/core/data/multidicomimage.py +30 -0
  27. mosamatic2/core/data/multiniftiimage.py +26 -0
  28. mosamatic2/core/data/multinumpyimage.py +26 -0
  29. mosamatic2/core/data/niftiimage.py +13 -0
  30. mosamatic2/core/data/numpyimage.py +13 -0
  31. mosamatic2/core/managers/__init__.py +0 -0
  32. mosamatic2/core/managers/logmanager.py +45 -0
  33. mosamatic2/core/managers/logmanagerlistener.py +3 -0
  34. mosamatic2/core/pipelines/__init__.py +4 -0
  35. mosamatic2/core/pipelines/boadockerpipeline/__init__.py +0 -0
  36. mosamatic2/core/pipelines/boadockerpipeline/boadockerpipeline.py +70 -0
  37. mosamatic2/core/pipelines/defaultdockerpipeline/__init__.py +0 -0
  38. mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py +28 -0
  39. mosamatic2/core/pipelines/defaultpipeline/__init__.py +0 -0
  40. mosamatic2/core/pipelines/defaultpipeline/defaultpipeline.py +90 -0
  41. mosamatic2/core/pipelines/liveranalysispipeline/__init__.py +0 -0
  42. mosamatic2/core/pipelines/liveranalysispipeline/liveranalysispipeline.py +48 -0
  43. mosamatic2/core/pipelines/pipeline.py +14 -0
  44. mosamatic2/core/singleton.py +9 -0
  45. mosamatic2/core/tasks/__init__.py +13 -0
  46. mosamatic2/core/tasks/applythresholdtosegmentationstask/__init__.py +0 -0
  47. mosamatic2/core/tasks/applythresholdtosegmentationstask/applythresholdtosegmentationstask.py +117 -0
  48. mosamatic2/core/tasks/calculatemaskstatisticstask/__init__.py +0 -0
  49. mosamatic2/core/tasks/calculatemaskstatisticstask/calculatemaskstatisticstask.py +104 -0
  50. mosamatic2/core/tasks/calculatescorestask/__init__.py +0 -0
  51. mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +152 -0
  52. mosamatic2/core/tasks/createdicomsummarytask/__init__.py +0 -0
  53. mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py +88 -0
  54. mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py +0 -0
  55. mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +101 -0
  56. mosamatic2/core/tasks/dicom2niftitask/__init__.py +0 -0
  57. mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +45 -0
  58. mosamatic2/core/tasks/rescaledicomimagestask/__init__.py +0 -0
  59. mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +64 -0
  60. mosamatic2/core/tasks/segmentationnifti2numpytask/__init__.py +0 -0
  61. mosamatic2/core/tasks/segmentationnifti2numpytask/segmentationnifti2numpytask.py +57 -0
  62. mosamatic2/core/tasks/segmentationnumpy2niftitask/__init__.py +0 -0
  63. mosamatic2/core/tasks/segmentationnumpy2niftitask/segmentationnumpy2niftitask.py +86 -0
  64. mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py +0 -0
  65. mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +39 -0
  66. mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +122 -0
  67. mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/__init__.py +0 -0
  68. mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/paramloader.py +39 -0
  69. mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/segmentmusclefatt4pytorchtask.py +128 -0
  70. mosamatic2/core/tasks/selectslicefromscanstask/__init__.py +0 -0
  71. mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py +249 -0
  72. mosamatic2/core/tasks/task.py +50 -0
  73. mosamatic2/core/tasks/totalsegmentatortask/__init__.py +0 -0
  74. mosamatic2/core/tasks/totalsegmentatortask/totalsegmentatortask.py +75 -0
  75. mosamatic2/core/utils.py +405 -0
  76. mosamatic2/server.py +146 -0
  77. mosamatic2/ui/__init__.py +0 -0
  78. mosamatic2/ui/mainwindow.py +426 -0
  79. mosamatic2/ui/resources/VERSION +1 -0
  80. mosamatic2/ui/resources/icons/mosamatic2.icns +0 -0
  81. mosamatic2/ui/resources/icons/mosamatic2.ico +0 -0
  82. mosamatic2/ui/resources/icons/spinner.gif +0 -0
  83. mosamatic2/ui/resources/images/body-composition.jpg +0 -0
  84. mosamatic2/ui/settings.py +62 -0
  85. mosamatic2/ui/utils.py +36 -0
  86. mosamatic2/ui/widgets/__init__.py +0 -0
  87. mosamatic2/ui/widgets/dialogs/__init__.py +0 -0
  88. mosamatic2/ui/widgets/dialogs/dialog.py +16 -0
  89. mosamatic2/ui/widgets/dialogs/helpdialog.py +9 -0
  90. mosamatic2/ui/widgets/panels/__init__.py +0 -0
  91. mosamatic2/ui/widgets/panels/defaultpanel.py +31 -0
  92. mosamatic2/ui/widgets/panels/logpanel.py +65 -0
  93. mosamatic2/ui/widgets/panels/mainpanel.py +82 -0
  94. mosamatic2/ui/widgets/panels/pipelines/__init__.py +0 -0
  95. mosamatic2/ui/widgets/panels/pipelines/boadockerpipelinepanel.py +195 -0
  96. mosamatic2/ui/widgets/panels/pipelines/defaultdockerpipelinepanel.py +314 -0
  97. mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +302 -0
  98. mosamatic2/ui/widgets/panels/pipelines/liveranalysispipelinepanel.py +187 -0
  99. mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py +6 -0
  100. mosamatic2/ui/widgets/panels/settingspanel.py +16 -0
  101. mosamatic2/ui/widgets/panels/stackedpanel.py +22 -0
  102. mosamatic2/ui/widgets/panels/tasks/__init__.py +0 -0
  103. mosamatic2/ui/widgets/panels/tasks/applythresholdtosegmentationstaskpanel.py +271 -0
  104. mosamatic2/ui/widgets/panels/tasks/calculatemaskstatisticstaskpanel.py +215 -0
  105. mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +238 -0
  106. mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py +206 -0
  107. mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +247 -0
  108. mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +183 -0
  109. mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +184 -0
  110. mosamatic2/ui/widgets/panels/tasks/segmentationnifti2numpytaskpanel.py +192 -0
  111. mosamatic2/ui/widgets/panels/tasks/segmentationnumpy2niftitaskpanel.py +213 -0
  112. mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +216 -0
  113. mosamatic2/ui/widgets/panels/tasks/segmentmusclefatt4pytorchtaskpanel.py +217 -0
  114. mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py +193 -0
  115. mosamatic2/ui/widgets/panels/tasks/taskpanel.py +6 -0
  116. mosamatic2/ui/widgets/panels/tasks/totalsegmentatortaskpanel.py +195 -0
  117. mosamatic2/ui/widgets/panels/visualizations/__init__.py +0 -0
  118. mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/__init__.py +0 -0
  119. mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentpicker.py +96 -0
  120. mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentviewer.py +130 -0
  121. mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentvisualization.py +120 -0
  122. mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/__init__.py +0 -0
  123. mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/sliceselectionviewer.py +61 -0
  124. mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/sliceselectionvisualization.py +133 -0
  125. mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/slicetile.py +63 -0
  126. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py +0 -0
  127. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +80 -0
  128. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +116 -0
  129. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +141 -0
  130. mosamatic2/ui/widgets/panels/visualizations/visualization.py +6 -0
  131. mosamatic2/ui/widgets/splashscreen.py +101 -0
  132. mosamatic2/ui/worker.py +29 -0
  133. mosamatic2-2.0.24.dist-info/METADATA +43 -0
  134. mosamatic2-2.0.24.dist-info/RECORD +136 -0
  135. mosamatic2-2.0.24.dist-info/WHEEL +4 -0
  136. 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,3 @@
1
+ class LogManagerListener:
2
+ def new_message(self, message):
3
+ raise NotImplementedError()
@@ -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
@@ -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)}')
@@ -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
+ )
@@ -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,9 @@
1
+ def singleton(cls):
2
+ _instances = {}
3
+
4
+ def instance(*args, **kwargs):
5
+ if cls not in _instances:
6
+ _instances[cls] = cls(*args, **kwargs)
7
+ return _instances[cls]
8
+
9
+ return instance
@@ -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
@@ -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)