mosamatic2 2.0.4__tar.gz → 2.0.5__tar.gz
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-2.0.4 → mosamatic2-2.0.5}/PKG-INFO +1 -1
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/pyproject.toml +1 -1
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/cli.py +2 -0
- mosamatic2-2.0.5/src/mosamatic2/commands/selectslicefromscans.py +65 -0
- mosamatic2-2.0.5/src/mosamatic2/core/data/multinumpyimage.py +26 -0
- mosamatic2-2.0.5/src/mosamatic2/core/data/numpyimage.py +13 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/__init__.py +2 -1
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +6 -6
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +1 -3
- mosamatic2-2.0.5/src/mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py +109 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/utils.py +10 -2
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/server.py +17 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/mainwindow.py +15 -0
- mosamatic2-2.0.5/src/mosamatic2/ui/resources/VERSION +1 -0
- mosamatic2-2.0.5/src/mosamatic2/ui/widgets/panels/tasks/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/selectslicefromscantaskpanel.py → mosamatic2-2.0.5/src/mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py +26 -17
- mosamatic2-2.0.4/src/mosamatic2/ui/resources/VERSION +0 -1
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/README.md +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/models.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/app.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/calculatescores.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/createpngsfromsegmentations.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/dicom2nifti.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/rescaledicomimages.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/segmentmusclefatl3tensorflow.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/constants.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/data/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/data/dicomimage.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/data/dicomimageseries.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/data/dixonseries.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/data/filedata.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/data/multidicomimage.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/managers/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/managers/logmanager.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/managers/logmanagerlistener.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/pipelines/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/pipelines/defaultpipeline.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/pipelines/pipeline.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/singleton.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/calculatescorestask/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/dicom2niftitask/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/rescaledicomimagestask/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +0 -0
- {mosamatic2-2.0.4/src/mosamatic2/ui → mosamatic2-2.0.5/src/mosamatic2/core/tasks/selectslicefromscanstask}/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/task.py +0 -0
- {mosamatic2-2.0.4/src/mosamatic2/ui/widgets → mosamatic2-2.0.5/src/mosamatic2/ui}/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/resources/icons/mosamatic2.icns +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/resources/icons/mosamatic2.ico +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/resources/icons/spinner.gif +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/resources/images/body-composition.jpg +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/settings.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/utils.py +0 -0
- {mosamatic2-2.0.4/src/mosamatic2/ui/widgets/dialogs → mosamatic2-2.0.5/src/mosamatic2/ui/widgets}/__init__.py +0 -0
- {mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels → mosamatic2-2.0.5/src/mosamatic2/ui/widgets/dialogs}/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/dialogs/dialog.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/dialogs/helpdialog.py +0 -0
- {mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/pipelines → mosamatic2-2.0.5/src/mosamatic2/ui/widgets/panels}/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/defaultpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/logpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/mainpanel.py +0 -0
- {mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks → mosamatic2-2.0.5/src/mosamatic2/ui/widgets/panels/pipelines}/__init__.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/stackedpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/taskpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/splashscreen.py +0 -0
- {mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/worker.py +0 -0
|
@@ -5,6 +5,7 @@ from mosamatic2.commands import (
|
|
|
5
5
|
segmentmusclefatl3tensorflow,
|
|
6
6
|
createpngsfromsegmentations,
|
|
7
7
|
dicom2nifti,
|
|
8
|
+
selectslicefromscans,
|
|
8
9
|
)
|
|
9
10
|
from mosamatic2.core.utils import show_doc_command
|
|
10
11
|
|
|
@@ -31,4 +32,5 @@ main.add_command(rescaledicomimages.rescaledicomimages)
|
|
|
31
32
|
main.add_command(segmentmusclefatl3tensorflow.segmentmusclefatl3tensorflow)
|
|
32
33
|
main.add_command(createpngsfromsegmentations.createpngsfromsegmentations)
|
|
33
34
|
main.add_command(dicom2nifti.dicom2nifti)
|
|
35
|
+
main.add_command(selectslicefromscans.selectslicefromscans)
|
|
34
36
|
main.add_command(show_doc_command(main)) # Special command to show long description for command
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import SelectSliceFromScansTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Selects specific slice from CT scans')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--scans',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Directory with scans (each patient separate subdirectory)',
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--output',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(),
|
|
17
|
+
help='Output directory'
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--vertebra',
|
|
21
|
+
required=True,
|
|
22
|
+
help='Vertebral level for selecting slice (default: "L3")'
|
|
23
|
+
)
|
|
24
|
+
@click.option(
|
|
25
|
+
'--overwrite',
|
|
26
|
+
type=click.BOOL,
|
|
27
|
+
default=False,
|
|
28
|
+
help='Overwrite [true|false]'
|
|
29
|
+
)
|
|
30
|
+
def selectslicefromscans(scans, vertebra, output, overwrite):
|
|
31
|
+
"""
|
|
32
|
+
Selects specific slice from list of CT scans
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
--scans : str
|
|
37
|
+
Directory to scans. Each patient's scan should be in a separate
|
|
38
|
+
subdirectory. For example:
|
|
39
|
+
|
|
40
|
+
/scans
|
|
41
|
+
/scans/patient1
|
|
42
|
+
/scans/patient1/file1.dcm
|
|
43
|
+
/scans/patient1/file2.dcm
|
|
44
|
+
...
|
|
45
|
+
/scans/patient2
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
--output : str
|
|
49
|
+
Path to output directory where selected slices will be placed. Each
|
|
50
|
+
slice's file name will be the same as the scan directory name, so in
|
|
51
|
+
the example above that would be "patient1", "patient2", etc.
|
|
52
|
+
|
|
53
|
+
--vertebra : str
|
|
54
|
+
Vertebral level where to take slice [L3|T4]
|
|
55
|
+
|
|
56
|
+
--overwrite : bool
|
|
57
|
+
Overwrite contents output directory [true|false]
|
|
58
|
+
"""
|
|
59
|
+
task = SelectSliceFromScansTask(
|
|
60
|
+
inputs={'scans': scans},
|
|
61
|
+
params={'vertebra': vertebra},
|
|
62
|
+
output=output,
|
|
63
|
+
overwrite=overwrite,
|
|
64
|
+
)
|
|
65
|
+
task.run()
|
|
@@ -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_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
|
|
@@ -2,4 +2,5 @@ from mosamatic2.core.tasks.rescaledicomimagestask.rescaledicomimagestask import
|
|
|
2
2
|
from mosamatic2.core.tasks.segmentmusclefatl3tensorflowtask.segmentmusclefatl3tensorflowtask import SegmentMuscleFatL3TensorFlowTask
|
|
3
3
|
from mosamatic2.core.tasks.calculatescorestask.calculatescorestask import CalculateScoresTask
|
|
4
4
|
from mosamatic2.core.tasks.createpngsfromsegmentationstask.createpngsfromsegmentationstask import CreatePngsFromSegmentationsTask
|
|
5
|
-
from mosamatic2.core.tasks.dicom2niftitask.dicom2niftitask import Dicom2NiftiTask
|
|
5
|
+
from mosamatic2.core.tasks.dicom2niftitask.dicom2niftitask import Dicom2NiftiTask
|
|
6
|
+
from mosamatic2.core.tasks.selectslicefromscanstask.selectslicefromscanstask import SelectSliceFromScansTask
|
|
@@ -4,10 +4,8 @@ import pandas as pd
|
|
|
4
4
|
from mosamatic2.core.tasks.task import Task
|
|
5
5
|
from mosamatic2.core.managers.logmanager import LogManager
|
|
6
6
|
from mosamatic2.core.data.multidicomimage import MultiDicomImage
|
|
7
|
+
from mosamatic2.core.data.numpyimage import NumPyImage
|
|
7
8
|
from mosamatic2.core.utils import (
|
|
8
|
-
is_dicom,
|
|
9
|
-
load_dicom,
|
|
10
|
-
is_jpeg2000_compressed,
|
|
11
9
|
get_pixels_from_dicom_object,
|
|
12
10
|
calculate_area,
|
|
13
11
|
calculate_mean_radiation_attenuation,
|
|
@@ -41,13 +39,11 @@ class CalculateScoresTask(Task):
|
|
|
41
39
|
if file_type == '.seg.npy':
|
|
42
40
|
f_seg_name = f_seg_name.removesuffix(file_type)
|
|
43
41
|
if f_seg_name == f_img_name:
|
|
44
|
-
# img_seg_pairs.append((f_img_path, f_seg_path))
|
|
45
42
|
img_seg_pairs.append((image, f_seg_path))
|
|
46
43
|
elif file_type == '.tag':
|
|
47
44
|
f_seg_name = f_seg_name.removesuffix(file_type).removesuffix('.dcm')
|
|
48
45
|
f_img_name = f_img_name.removesuffix('.dcm')
|
|
49
46
|
if f_seg_name == f_img_name:
|
|
50
|
-
# img_seg_pairs.append((f_img_path, f_seg_path))
|
|
51
47
|
img_seg_pairs.append((image, f_seg_path))
|
|
52
48
|
else:
|
|
53
49
|
raise RuntimeError('Unknown file type')
|
|
@@ -78,7 +74,11 @@ class CalculateScoresTask(Task):
|
|
|
78
74
|
|
|
79
75
|
def load_segmentation(self, f, file_type='npy'):
|
|
80
76
|
if file_type == 'npy':
|
|
81
|
-
|
|
77
|
+
segmentation = NumPyImage()
|
|
78
|
+
segmentation.set_path(f)
|
|
79
|
+
if segmentation.load():
|
|
80
|
+
return segmentation.object()
|
|
81
|
+
LOG.error(f'Could not load segmentation file {f}')
|
|
82
82
|
if file_type == 'tag':
|
|
83
83
|
pixels = get_pixels_from_tag_file(f)
|
|
84
84
|
try:
|
|
@@ -28,7 +28,7 @@ class SegmentMuscleFatL3TensorFlowTask(Task):
|
|
|
28
28
|
def __init__(self, inputs, params, output, overwrite=True):
|
|
29
29
|
super(SegmentMuscleFatL3TensorFlowTask, self).__init__(inputs, params, output, overwrite)
|
|
30
30
|
|
|
31
|
-
def load_images(self):
|
|
31
|
+
def load_images(self):
|
|
32
32
|
image_data = MultiDicomImage()
|
|
33
33
|
image_data.set_path(self.input('images'))
|
|
34
34
|
if image_data.load():
|
|
@@ -55,7 +55,6 @@ class SegmentMuscleFatL3TensorFlowTask(Task):
|
|
|
55
55
|
import tensorflow as tf
|
|
56
56
|
tfLoaded = True
|
|
57
57
|
with tempfile.TemporaryDirectory() as model_dir_unzipped:
|
|
58
|
-
# model_dir_unzipped = os.path.join(os.path.split(f_path)[0], 'model_unzipped')
|
|
59
58
|
os.makedirs(model_dir_unzipped, exist_ok=True)
|
|
60
59
|
with zipfile.ZipFile(f_path) as zipObj:
|
|
61
60
|
zipObj.extractall(path=model_dir_unzipped)
|
|
@@ -65,7 +64,6 @@ class SegmentMuscleFatL3TensorFlowTask(Task):
|
|
|
65
64
|
import tensorflow as tf
|
|
66
65
|
tfLoaded = True
|
|
67
66
|
with tempfile.TemporaryDirectory() as contour_model_dir_unzipped:
|
|
68
|
-
# contour_model_dir_unzipped = os.path.join(os.path.split(f_path)[0], 'contour_model_unzipped')
|
|
69
67
|
os.makedirs(contour_model_dir_unzipped, exist_ok=True)
|
|
70
68
|
with zipfile.ZipFile(f_path) as zipObj:
|
|
71
69
|
zipObj.extractall(path=contour_model_dir_unzipped)
|
mosamatic2-2.0.5/src/mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import math
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
import nibabel as nib
|
|
6
|
+
import numpy as np
|
|
7
|
+
from totalsegmentator.python_api import totalsegmentator
|
|
8
|
+
from mosamatic2.core.tasks.task import Task
|
|
9
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
10
|
+
from mosamatic2.core.utils import load_dicom
|
|
11
|
+
|
|
12
|
+
LOG = LogManager()
|
|
13
|
+
|
|
14
|
+
TOTAL_SEGMENTATOR_OUTPUT_DIR = os.path.join(tempfile.gettempdir(), 'total_segmentator_output')
|
|
15
|
+
TOTAL_SEGMENTATOR_TASK = 'total'
|
|
16
|
+
Z_DELTA_OFFSETS = {
|
|
17
|
+
'vertebrae_L3': 0.333,
|
|
18
|
+
'vertebrae_T4': 0.5,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SelectSliceFromScansTask(Task):
|
|
23
|
+
INPUTS = ['scans']
|
|
24
|
+
PARAMS = ['vertebra']
|
|
25
|
+
|
|
26
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
27
|
+
super(SelectSliceFromScansTask, self).__init__(inputs, params, output, overwrite)
|
|
28
|
+
|
|
29
|
+
def load_scan_dirs(self):
|
|
30
|
+
scan_dirs = []
|
|
31
|
+
for d in os.listdir(self.input('scans')):
|
|
32
|
+
scan_dir = os.path.join(self.input('scans'), d)
|
|
33
|
+
if os.path.isdir(scan_dir):
|
|
34
|
+
scan_dirs.append(scan_dir)
|
|
35
|
+
return scan_dirs
|
|
36
|
+
|
|
37
|
+
def extract_masks(self, scan_dir):
|
|
38
|
+
os.makedirs(TOTAL_SEGMENTATOR_OUTPUT_DIR, exist_ok=True)
|
|
39
|
+
totalsegmentator(input=scan_dir, output=TOTAL_SEGMENTATOR_OUTPUT_DIR, fast=True)
|
|
40
|
+
# os.system(f'TotalSegmentator -i {scan_dir} -o {TOTAL_SEGMENTATOR_OUTPUT_DIR} --fast')
|
|
41
|
+
|
|
42
|
+
def delete_total_segmentator_output(self):
|
|
43
|
+
if os.path.exists(TOTAL_SEGMENTATOR_OUTPUT_DIR):
|
|
44
|
+
shutil.rmtree(TOTAL_SEGMENTATOR_OUTPUT_DIR)
|
|
45
|
+
|
|
46
|
+
def get_z_delta_offset_for_mask(self, mask_name):
|
|
47
|
+
if mask_name not in Z_DELTA_OFFSETS.keys():
|
|
48
|
+
return None
|
|
49
|
+
return Z_DELTA_OFFSETS[mask_name]
|
|
50
|
+
|
|
51
|
+
def find_slice(self, scan_dir, vertebra):
|
|
52
|
+
if vertebra == 'L3':
|
|
53
|
+
vertebral_level = 'vertebrae_L3'
|
|
54
|
+
elif vertebra == 'T4':
|
|
55
|
+
vertebral_level = 'vertebrae_T4'
|
|
56
|
+
else:
|
|
57
|
+
raise RuntimeError(f'Unknown vertbra {vertebra}. Options are "L3" and "T4"')
|
|
58
|
+
# Find Z-positions DICOM images
|
|
59
|
+
z_positions = {}
|
|
60
|
+
for f in os.listdir(scan_dir):
|
|
61
|
+
f_path = os.path.join(scan_dir, f)
|
|
62
|
+
p = load_dicom(f_path, stop_before_pixels=True)
|
|
63
|
+
if p is not None:
|
|
64
|
+
z_positions[p.ImagePositionPatient[2]] = f_path
|
|
65
|
+
# Find Z-position L3 image
|
|
66
|
+
mask_file = os.path.join(TOTAL_SEGMENTATOR_OUTPUT_DIR, f'{vertebral_level}.nii.gz')
|
|
67
|
+
mask_obj = nib.load(mask_file)
|
|
68
|
+
mask = mask_obj.get_fdata()
|
|
69
|
+
affine_transform = mask_obj.affine
|
|
70
|
+
indexes = np.array(np.where(mask == 1))
|
|
71
|
+
index_min = indexes.min(axis=1)
|
|
72
|
+
index_max = indexes.max(axis=1)
|
|
73
|
+
world_min = nib.affines.apply_affine(affine_transform, index_min)
|
|
74
|
+
world_max = nib.affines.apply_affine(affine_transform, index_max)
|
|
75
|
+
z_direction = affine_transform[:3, 2][2]
|
|
76
|
+
z_sign = math.copysign(1, z_direction)
|
|
77
|
+
z_delta_offset = self.get_z_delta_offset_for_mask(vertebral_level)
|
|
78
|
+
if z_delta_offset is None:
|
|
79
|
+
return None
|
|
80
|
+
z_delta = 0.333 * abs(world_max[2] - world_min[2]) # This needs to be vertebra-specific perhaps
|
|
81
|
+
z_l3 = world_max[2] - z_sign * z_delta
|
|
82
|
+
# Find closest L3 image in DICOM set
|
|
83
|
+
positions = sorted(z_positions.keys())
|
|
84
|
+
closest_file = None
|
|
85
|
+
for z1, z2 in zip(positions[:-1], positions[1:]):
|
|
86
|
+
if min(z1, z2) <= z_l3 <= max(z1, z2):
|
|
87
|
+
closest_z = min(z_positions.keys(), key=lambda z: abs(z - z_l3))
|
|
88
|
+
closest_file = z_positions[closest_z]
|
|
89
|
+
LOG.info(f'Closest image: {closest_file}')
|
|
90
|
+
break
|
|
91
|
+
return closest_file
|
|
92
|
+
|
|
93
|
+
def run(self):
|
|
94
|
+
scan_dirs = self.load_scan_dirs()
|
|
95
|
+
vertebra = self.param('vertebra')
|
|
96
|
+
nr_steps = len(scan_dirs)
|
|
97
|
+
for step in range(nr_steps):
|
|
98
|
+
scan_dir = scan_dirs[step]
|
|
99
|
+
scan_name = os.path.split(scan_dir)[1]
|
|
100
|
+
self.extract_masks(scan_dir)
|
|
101
|
+
file_path = self.find_slice(scan_dir, vertebra)
|
|
102
|
+
if file_path is not None:
|
|
103
|
+
extension = '' if file_path.endswith('.dcm') else '.dcm'
|
|
104
|
+
target_file_path = os.path.join(self.output(), vertebra + '_' + scan_name + extension)
|
|
105
|
+
shutil.copyfile(file_path, target_file_path)
|
|
106
|
+
else:
|
|
107
|
+
LOG.error(f'Could not find slice for vertebral level: {vertebra}')
|
|
108
|
+
self.delete_total_segmentator_output()
|
|
109
|
+
self.set_progress(step, nr_steps)
|
|
@@ -119,10 +119,18 @@ def is_numpy_array(value):
|
|
|
119
119
|
return isinstance(value, np.array)
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
def
|
|
122
|
+
def is_numpy(f):
|
|
123
123
|
try:
|
|
124
|
+
np.load(f)
|
|
125
|
+
return True
|
|
126
|
+
except:
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def load_numpy_array(f):
|
|
131
|
+
if is_numpy(f):
|
|
124
132
|
return np.load(f)
|
|
125
|
-
|
|
133
|
+
else:
|
|
126
134
|
return None
|
|
127
135
|
|
|
128
136
|
|
|
@@ -6,6 +6,7 @@ from mosamatic2.core.tasks import SegmentMuscleFatL3TensorFlowTask
|
|
|
6
6
|
from mosamatic2.core.tasks import CalculateScoresTask
|
|
7
7
|
from mosamatic2.core.tasks import CreatePngsFromSegmentationsTask
|
|
8
8
|
from mosamatic2.core.tasks import Dicom2NiftiTask
|
|
9
|
+
from mosamatic2.core.tasks import SelectSliceFromScansTask
|
|
9
10
|
|
|
10
11
|
app = Flask(__name__)
|
|
11
12
|
|
|
@@ -105,6 +106,22 @@ def run_dicom2nifti():
|
|
|
105
106
|
return 'PASSED'
|
|
106
107
|
|
|
107
108
|
|
|
109
|
+
@app.route('/selectslicefromscans')
|
|
110
|
+
def run_selectslicefromscans():
|
|
111
|
+
scans = request.args.get('scans')
|
|
112
|
+
vertebra = request.args.get('vertebra')
|
|
113
|
+
output = request.args.get('output')
|
|
114
|
+
overwrite = request.args.get('overwrite', default=True, type=bool)
|
|
115
|
+
task = SelectSliceFromScansTask(
|
|
116
|
+
inputs={'scans': scans},
|
|
117
|
+
params={'vertebra': vertebra},
|
|
118
|
+
output=output,
|
|
119
|
+
overwrite=overwrite,
|
|
120
|
+
)
|
|
121
|
+
task.run()
|
|
122
|
+
return 'PASSED'
|
|
123
|
+
|
|
124
|
+
|
|
108
125
|
def main():
|
|
109
126
|
parser = argparse.ArgumentParser()
|
|
110
127
|
parser.add_argument('--port', type=int, default=constants.MOSAMATIC2_SERVER_PORT)
|
|
@@ -19,6 +19,7 @@ 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.selectslicefromscanstaskpanel import SelectSliceFromScansTaskPanel
|
|
22
23
|
from mosamatic2.ui.widgets.panels.pipelines.defaultpipelinepanel import DefaultPipelinePanel
|
|
23
24
|
|
|
24
25
|
LOG = LogManager()
|
|
@@ -36,6 +37,7 @@ class MainWindow(QMainWindow):
|
|
|
36
37
|
self._create_pngs_from_segmentations_task_panel = None
|
|
37
38
|
self._calculate_scores_task_panel = None
|
|
38
39
|
self._dicom2nifti_task_panel = None
|
|
40
|
+
self._select_slice_from_scans_task_panel = None
|
|
39
41
|
self._default_pipeline_panel = None
|
|
40
42
|
self.init_window()
|
|
41
43
|
|
|
@@ -75,12 +77,15 @@ class MainWindow(QMainWindow):
|
|
|
75
77
|
create_pngs_from_segmentations_task_action.triggered.connect(self.handle_create_pngs_from_segmentations_task_action)
|
|
76
78
|
dicom2nifti_task_action = QAction('Dicom2NiftiTask', self)
|
|
77
79
|
dicom2nifti_task_action.triggered.connect(self.handle_dicom2nifti_task_action)
|
|
80
|
+
select_slice_from_scans_task_action = QAction('SelectSliceFromScansTask', self)
|
|
81
|
+
select_slice_from_scans_task_action.triggered.connect(self.handle_select_slice_from_scans_task_action)
|
|
78
82
|
tasks_menu = self.menuBar().addMenu('Tasks')
|
|
79
83
|
tasks_menu.addAction(rescale_dicom_images_task_action)
|
|
80
84
|
tasks_menu.addAction(segment_muscle_fat_l3_tensorflow_task_action)
|
|
81
85
|
tasks_menu.addAction(calculate_scores_task_action)
|
|
82
86
|
tasks_menu.addAction(create_pngs_from_segmentations_task_action)
|
|
83
87
|
tasks_menu.addAction(dicom2nifti_task_action)
|
|
88
|
+
tasks_menu.addAction(select_slice_from_scans_task_action)
|
|
84
89
|
|
|
85
90
|
def init_pipelines_menu(self):
|
|
86
91
|
default_pipeline_action = QAction('DefaultPipeline', self)
|
|
@@ -106,6 +111,7 @@ class MainWindow(QMainWindow):
|
|
|
106
111
|
self._main_panel.add_panel(self.create_pngs_from_segmentations_task_panel(), 'createpngsfromsegmentationstaskpanel')
|
|
107
112
|
self._main_panel.add_panel(self.calculate_scores_task_panel(), 'calculatescorestaskpanel')
|
|
108
113
|
self._main_panel.add_panel(self.dicom2nifti_task_panel(), 'dicom2niftitaskpanel')
|
|
114
|
+
self._main_panel.add_panel(self.select_slice_from_scans_task_panel(), 'selectslicefromscanstaskpanel')
|
|
109
115
|
self._main_panel.add_panel(self.default_pipeline_panel(), 'defaultpipelinepanel')
|
|
110
116
|
self._main_panel.select_panel('defaultpipelinepanel')
|
|
111
117
|
return self._main_panel
|
|
@@ -142,6 +148,11 @@ class MainWindow(QMainWindow):
|
|
|
142
148
|
if not self._dicom2nifti_task_panel:
|
|
143
149
|
self._dicom2nifti_task_panel = Dicom2NiftiTaskPanel()
|
|
144
150
|
return self._dicom2nifti_task_panel
|
|
151
|
+
|
|
152
|
+
def select_slice_from_scans_task_panel(self):
|
|
153
|
+
if not self._select_slice_from_scans_task_panel:
|
|
154
|
+
self._select_slice_from_scans_task_panel = SelectSliceFromScansTaskPanel()
|
|
155
|
+
return self._select_slice_from_scans_task_panel
|
|
145
156
|
|
|
146
157
|
def default_pipeline_panel(self):
|
|
147
158
|
if not self._default_pipeline_panel:
|
|
@@ -170,6 +181,9 @@ class MainWindow(QMainWindow):
|
|
|
170
181
|
def handle_dicom2nifti_task_action(self):
|
|
171
182
|
self.main_panel().select_panel('dicom2niftitaskpanel')
|
|
172
183
|
|
|
184
|
+
def handle_select_slice_from_scans_task_action(self):
|
|
185
|
+
self.main_panel().select_panel('selectslicefromscanstaskpanel')
|
|
186
|
+
|
|
173
187
|
def handle_default_pipeline_action(self):
|
|
174
188
|
self.main_panel().select_panel('defaultpipelinepanel')
|
|
175
189
|
|
|
@@ -184,6 +198,7 @@ class MainWindow(QMainWindow):
|
|
|
184
198
|
self.create_pngs_from_segmentations_task_panel().save_inputs_and_parameters()
|
|
185
199
|
self.calculate_scores_task_panel().save_inputs_and_parameters()
|
|
186
200
|
self.dicom2nifti_task_panel().save_inputs_and_parameters()
|
|
201
|
+
self.select_slice_from_scans_task_panel().save_inputs_and_parameters()
|
|
187
202
|
self.default_pipeline_panel().save_inputs_and_parameters()
|
|
188
203
|
return super().closeEvent(event)
|
|
189
204
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.0.5
|
|
File without changes
|
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
from PySide6.QtWidgets import (
|
|
4
4
|
QLineEdit,
|
|
5
5
|
QCheckBox,
|
|
6
|
-
|
|
6
|
+
QComboBox,
|
|
7
7
|
QHBoxLayout,
|
|
8
8
|
QVBoxLayout,
|
|
9
9
|
QFormLayout,
|
|
@@ -16,26 +16,26 @@ from PySide6.QtCore import (
|
|
|
16
16
|
Slot,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
from
|
|
20
|
-
from
|
|
21
|
-
from
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
|
|
25
|
-
from mosamatic.tasks import SelectSliceFromScanTask
|
|
19
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
20
|
+
from mosamatic2.ui.widgets.panels.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 SelectSliceFromScansTask
|
|
26
25
|
|
|
27
26
|
LOG = LogManager()
|
|
28
27
|
|
|
29
|
-
PANEL_TITLE = 'Automatically select L3
|
|
30
|
-
PANEL_NAME = '
|
|
28
|
+
PANEL_TITLE = 'Automatically select L3 slice from full CT scan'
|
|
29
|
+
PANEL_NAME = 'selectslicefromscanstaskpanel'
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
class
|
|
32
|
+
class SelectSliceFromScansTaskPanel(TaskPanel):
|
|
34
33
|
def __init__(self):
|
|
35
|
-
super(
|
|
34
|
+
super(SelectSliceFromScansTaskPanel, self).__init__()
|
|
36
35
|
self.set_title(PANEL_TITLE)
|
|
37
36
|
self._scans_dir_line_edit = None
|
|
38
37
|
self._scans_dir_select_button = None
|
|
38
|
+
self._vertebra_combobox = None
|
|
39
39
|
self._output_dir_line_edit = None
|
|
40
40
|
self._output_dir_select_button = None
|
|
41
41
|
self._overwrite_checkbox = None
|
|
@@ -58,6 +58,13 @@ class SelectSliceFromScanTaskPanel(TaskPanel):
|
|
|
58
58
|
self._scans_dir_select_button.clicked.connect(self.handle_scans_dir_select_button)
|
|
59
59
|
return self._scans_dir_select_button
|
|
60
60
|
|
|
61
|
+
def vertebra_combobox(self):
|
|
62
|
+
if not self._vertebra_combobox:
|
|
63
|
+
self._vertebra_combobox = QComboBox()
|
|
64
|
+
self._vertebra_combobox.addItems(['L3'])
|
|
65
|
+
self._vertebra_combobox.setCurrentText(self.settings().get(f'{PANEL_NAME}/vertebra'))
|
|
66
|
+
return self._vertebra_combobox
|
|
67
|
+
|
|
61
68
|
def output_dir_line_edit(self):
|
|
62
69
|
if not self._output_dir_line_edit:
|
|
63
70
|
self._output_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/output_dir'))
|
|
@@ -101,6 +108,7 @@ class SelectSliceFromScanTaskPanel(TaskPanel):
|
|
|
101
108
|
output_dir_layout.addWidget(self.output_dir_line_edit())
|
|
102
109
|
output_dir_layout.addWidget(self.output_dir_select_button())
|
|
103
110
|
self.form_layout().addRow('Scans directory', scans_dir_layout)
|
|
111
|
+
# self.form_layout().addRow('Vertebra', self.vertebra_combobox())
|
|
104
112
|
self.form_layout().addRow('Output directory', output_dir_layout)
|
|
105
113
|
self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
|
|
106
114
|
layout = QVBoxLayout()
|
|
@@ -134,11 +142,11 @@ class SelectSliceFromScanTaskPanel(TaskPanel):
|
|
|
134
142
|
LOG.info('Running task...')
|
|
135
143
|
self.run_task_button().setEnabled(False)
|
|
136
144
|
self.save_inputs_and_parameters()
|
|
137
|
-
self._task =
|
|
138
|
-
self.scans_dir_line_edit().text(),
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
self.overwrite_checkbox().isChecked()
|
|
145
|
+
self._task = SelectSliceFromScansTask(
|
|
146
|
+
inputs={'scans': self.scans_dir_line_edit().text()},
|
|
147
|
+
params={'vertebra': 'L3'},
|
|
148
|
+
output=self.output_dir_line_edit().text(),
|
|
149
|
+
overwrite=self.overwrite_checkbox().isChecked(),
|
|
142
150
|
)
|
|
143
151
|
self._worker = Worker(self._task)
|
|
144
152
|
self._thread = QThread()
|
|
@@ -180,5 +188,6 @@ class SelectSliceFromScanTaskPanel(TaskPanel):
|
|
|
180
188
|
|
|
181
189
|
def save_inputs_and_parameters(self):
|
|
182
190
|
self.settings().set(f'{PANEL_NAME}/scans_dir', self.scans_dir_line_edit().text())
|
|
191
|
+
self.settings().set(f'{PANEL_NAME}/vertebra', self.vertebra_combobox().currentText())
|
|
183
192
|
self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
|
|
184
193
|
self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.0.4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/createpngsfromsegmentations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/commands/segmentmusclefatl3tensorflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/calculatescorestask/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py
RENAMED
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/core/tasks/rescaledicomimagestask/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4/src/mosamatic2/ui/widgets → mosamatic2-2.0.5/src/mosamatic2/ui}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/resources/images/body-composition.jpg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mosamatic2-2.0.4 → mosamatic2-2.0.5}/src/mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|