mosamatic2 2.0.2__tar.gz → 2.0.4__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.2 → mosamatic2-2.0.4}/PKG-INFO +6 -2
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/pyproject.toml +7 -2
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/app.py +4 -0
- mosamatic2-2.0.4/src/mosamatic2/cli.py +34 -0
- mosamatic2-2.0.4/src/mosamatic2/commands/calculatescores.py +73 -0
- mosamatic2-2.0.4/src/mosamatic2/commands/createpngsfromsegmentations.py +65 -0
- mosamatic2-2.0.4/src/mosamatic2/commands/dicom2nifti.py +46 -0
- mosamatic2-2.0.4/src/mosamatic2/commands/rescaledicomimages.py +54 -0
- mosamatic2-2.0.4/src/mosamatic2/commands/segmentmusclefatl3tensorflow.py +55 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/constants.py +6 -3
- mosamatic2-2.0.4/src/mosamatic2/core/data/__init__.py +5 -0
- mosamatic2-2.0.4/src/mosamatic2/core/data/dicomimage.py +18 -0
- mosamatic2-2.0.4/src/mosamatic2/core/data/dicomimageseries.py +26 -0
- mosamatic2-2.0.4/src/mosamatic2/core/data/dixonseries.py +22 -0
- mosamatic2-2.0.4/src/mosamatic2/core/data/filedata.py +26 -0
- mosamatic2-2.0.4/src/mosamatic2/core/data/multidicomimage.py +30 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/core/managers/logmanager.py +0 -2
- mosamatic2-2.0.4/src/mosamatic2/core/pipelines/__init__.py +1 -0
- mosamatic2-2.0.4/src/mosamatic2/core/pipelines/defaultpipeline.py +79 -0
- mosamatic2-2.0.4/src/mosamatic2/core/pipelines/pipeline.py +14 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/__init__.py +5 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +149 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +52 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/dicom2niftitask/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +24 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/rescaledicomimagestask/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +64 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +39 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +121 -0
- mosamatic2-2.0.4/src/mosamatic2/core/tasks/task.py +50 -0
- mosamatic2-2.0.4/src/mosamatic2/core/utils.py +316 -0
- mosamatic2-2.0.4/src/mosamatic2/server.py +113 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/mainwindow.py +211 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/resources/VERSION +1 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/dialogs/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/dialogs/dialog.py +16 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/dialogs/helpdialog.py +9 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/defaultpanel.py +31 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/logpanel.py +65 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/mainpanel.py +82 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/pipelines/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +299 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/stackedpanel.py +22 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/taskpanel.py +6 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/__init__.py +0 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +215 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +186 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +183 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +184 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +216 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/widgets/panels/tasks/selectslicefromscantaskpanel.py +184 -0
- mosamatic2-2.0.4/src/mosamatic2/ui/worker.py +29 -0
- mosamatic2-2.0.2/src/mosamatic2/server.py +0 -2
- mosamatic2-2.0.2/src/mosamatic2/ui/mainwindow.py +0 -62
- mosamatic2-2.0.2/src/mosamatic2/ui/resources/VERSION +0 -1
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/README.md +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/models.py +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/__init__.py +0 -0
- {mosamatic2-2.0.2/src/mosamatic2/core → mosamatic2-2.0.4/src/mosamatic2/commands}/__init__.py +0 -0
- {mosamatic2-2.0.2/src/mosamatic2/core/managers → mosamatic2-2.0.4/src/mosamatic2/core}/__init__.py +0 -0
- {mosamatic2-2.0.2/src/mosamatic2/ui → mosamatic2-2.0.4/src/mosamatic2/core/managers}/__init__.py +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/core/managers/logmanagerlistener.py +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/core/singleton.py +0 -0
- {mosamatic2-2.0.2/src/mosamatic2/ui/widgets → mosamatic2-2.0.4/src/mosamatic2/core/tasks/calculatescorestask}/__init__.py +0 -0
- /mosamatic2-2.0.2/src/mosamatic2/core/utils.py → /mosamatic2-2.0.4/src/mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/resources/icons/mosamatic2.icns +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/resources/icons/mosamatic2.ico +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/resources/icons/spinner.gif +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/resources/images/body-composition.jpg +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/settings.py +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/utils.py +0 -0
- {mosamatic2-2.0.2 → mosamatic2-2.0.4}/src/mosamatic2/ui/widgets/splashscreen.py +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mosamatic2
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Ralph Brecheisen
|
|
6
6
|
Author-email: r.brecheisen@maastrichtuniversity.nl
|
|
7
7
|
Requires-Python: >=3.11,<3.12
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Requires-Dist: antspyx (>=0.5.4)
|
|
11
|
+
Requires-Dist: dicom2nifti (>=2.6.2)
|
|
12
|
+
Requires-Dist: flask (>=3.1.2)
|
|
10
13
|
Requires-Dist: nibabel (>=5.3.2)
|
|
11
14
|
Requires-Dist: numpy (>=1.26.4)
|
|
12
15
|
Requires-Dist: openpyxl (>=3.1.5)
|
|
@@ -21,7 +24,8 @@ Requires-Dist: tensorboard (==2.15.2)
|
|
|
21
24
|
Requires-Dist: tensorboard-data-server (==0.7.2)
|
|
22
25
|
Requires-Dist: tensorflow (==2.15.*) ; platform_system == "Linux"
|
|
23
26
|
Requires-Dist: tensorflow-intel (==2.15.0) ; platform_system == "Windows"
|
|
24
|
-
Requires-Dist: tensorflow-io-gcs-filesystem (==0.31.0)
|
|
27
|
+
Requires-Dist: tensorflow-io-gcs-filesystem (==0.31.0) ; platform_system == "Windows"
|
|
28
|
+
Requires-Dist: tensorflow-io-gcs-filesystem (>=0.31.0) ; platform_system == "Darwin" and platform_machine == "arm64"
|
|
25
29
|
Requires-Dist: tensorflow-macos (==2.15.0) ; platform_system == "Darwin" and platform_machine == "arm64"
|
|
26
30
|
Requires-Dist: torch (>=2.8.0)
|
|
27
31
|
Requires-Dist: torchvision (>=0.23.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mosamatic2"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.4"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Ralph Brecheisen", email = "r.brecheisen@maastrichtuniversity.nl"}
|
|
@@ -10,6 +10,7 @@ requires-python = ">=3.11,<3.12"
|
|
|
10
10
|
dependencies = [
|
|
11
11
|
"pyside6-essentials>=6.9",
|
|
12
12
|
"pydicom>=3.0.1",
|
|
13
|
+
"dicom2nifti>=2.6.2",
|
|
13
14
|
"numpy>=1.26.4",
|
|
14
15
|
"pandas>=2.3.2",
|
|
15
16
|
"nibabel>=5.3.2",
|
|
@@ -25,8 +26,11 @@ dependencies = [
|
|
|
25
26
|
"tensorflow-macos==2.15.0; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
|
26
27
|
"tensorboard==2.15.2",
|
|
27
28
|
"tensorboard-data-server==0.7.2",
|
|
28
|
-
"tensorflow-io-gcs-filesystem==0.31.0",
|
|
29
|
+
"tensorflow-io-gcs-filesystem==0.31.0; platform_system == 'Windows'",
|
|
30
|
+
"tensorflow-io-gcs-filesystem>=0.31.0; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
|
29
31
|
"totalsegmentator>=2.11.0",
|
|
32
|
+
"flask>=3.1.2",
|
|
33
|
+
"antspyx>=0.5.4",
|
|
30
34
|
]
|
|
31
35
|
|
|
32
36
|
[tool.poetry]
|
|
@@ -37,6 +41,7 @@ packages = [
|
|
|
37
41
|
|
|
38
42
|
[tool.poetry.scripts]
|
|
39
43
|
mosamatic2 = "mosamatic2.app:main"
|
|
44
|
+
mosamatic2-cli = "mosamatic2.cli:main"
|
|
40
45
|
mosamatic2-server = "mosamatic2.server:main"
|
|
41
46
|
|
|
42
47
|
[tool.poetry.group.dev.dependencies]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from mosamatic2.commands import (
|
|
3
|
+
calculatescores,
|
|
4
|
+
rescaledicomimages,
|
|
5
|
+
segmentmusclefatl3tensorflow,
|
|
6
|
+
createpngsfromsegmentations,
|
|
7
|
+
dicom2nifti,
|
|
8
|
+
)
|
|
9
|
+
from mosamatic2.core.utils import show_doc_command
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CustomHelpGroup(click.Group):
|
|
13
|
+
def format_commands(self, ctx, formatter):
|
|
14
|
+
commands = self.list_commands(ctx)
|
|
15
|
+
with formatter.section('Commands'):
|
|
16
|
+
for command_name in commands:
|
|
17
|
+
command = self.get_command(ctx, command_name)
|
|
18
|
+
if command is None or command.hidden:
|
|
19
|
+
continue
|
|
20
|
+
help_text = command.get_short_help_str()
|
|
21
|
+
formatter.write_text(f'{command_name:15} {help_text}')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group(cls=CustomHelpGroup)
|
|
25
|
+
def main():
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
main.add_command(calculatescores.calculatescores)
|
|
30
|
+
main.add_command(rescaledicomimages.rescaledicomimages)
|
|
31
|
+
main.add_command(segmentmusclefatl3tensorflow.segmentmusclefatl3tensorflow)
|
|
32
|
+
main.add_command(createpngsfromsegmentations.createpngsfromsegmentations)
|
|
33
|
+
main.add_command(dicom2nifti.dicom2nifti)
|
|
34
|
+
main.add_command(show_doc_command(main)) # Special command to show long description for command
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import CalculateScoresTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Calculates body composition scores')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--images',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Directory with images',
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--segmentations',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(exists=True),
|
|
17
|
+
help='Directory with segmentations',
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--output',
|
|
21
|
+
required=True,
|
|
22
|
+
type=click.Path(),
|
|
23
|
+
help='Output directory'
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
'--file_type',
|
|
27
|
+
default='npy',
|
|
28
|
+
help='Options: [npy, tag]'
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
'--overwrite',
|
|
32
|
+
type=click.BOOL,
|
|
33
|
+
default=False,
|
|
34
|
+
help='Overwrite [true|false]'
|
|
35
|
+
)
|
|
36
|
+
def calculatescores(images, segmentations, output, file_type, overwrite):
|
|
37
|
+
"""
|
|
38
|
+
Calculates the following body composition metrics from the muscle and fat
|
|
39
|
+
images and segmentation files:
|
|
40
|
+
|
|
41
|
+
(1) Muscle area (cm^2)
|
|
42
|
+
(2) Mean muscle radiation attenuation (HU)
|
|
43
|
+
(3) Subcutaneous fat area (cm^2)
|
|
44
|
+
(4) Mean subcutaneous fat radiation attenuation (HU)
|
|
45
|
+
(5) Visceral fat area (cm^2)
|
|
46
|
+
(6) Mean visceral fat radiation attenuation (HU)
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
--images : str
|
|
51
|
+
Directory with input L3 images
|
|
52
|
+
|
|
53
|
+
--segmentations : str
|
|
54
|
+
Directory with L3 muscle and fat segmenation files. Must be output of
|
|
55
|
+
"mosamatic2-cli segmentmusclefatl3tensorflow" OR a list of TAG files
|
|
56
|
+
corresponding to the input images
|
|
57
|
+
|
|
58
|
+
--output : str
|
|
59
|
+
Path to output directory
|
|
60
|
+
|
|
61
|
+
--file_type : str
|
|
62
|
+
Type of segmentation file to use. Can be either "npy" or "tag"
|
|
63
|
+
|
|
64
|
+
--overwrite : bool
|
|
65
|
+
Overwrite contents output directory [true|false]
|
|
66
|
+
"""
|
|
67
|
+
task = CalculateScoresTask(
|
|
68
|
+
inputs={'images': images, 'segmentations': segmentations},
|
|
69
|
+
params={'file_type': file_type},
|
|
70
|
+
output=output,
|
|
71
|
+
overwrite=overwrite,
|
|
72
|
+
)
|
|
73
|
+
task.run()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import CreatePngsFromSegmentationsTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Create PNG images from segmentation files')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--segmentations',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Input directory to segmentation files'
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--output',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(),
|
|
17
|
+
help='Output directory for PNG images'
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--fig_width',
|
|
21
|
+
type=click.Path(),
|
|
22
|
+
default=10,
|
|
23
|
+
help='Figure width (default: 10)'
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
'--fig_height',
|
|
27
|
+
type=click.Path(),
|
|
28
|
+
default=10,
|
|
29
|
+
help='Figure height (default: 10)'
|
|
30
|
+
)
|
|
31
|
+
@click.option(
|
|
32
|
+
'--overwrite',
|
|
33
|
+
type=click.BOOL,
|
|
34
|
+
default=False,
|
|
35
|
+
help='Overwrite [true|false]'
|
|
36
|
+
)
|
|
37
|
+
def createpngsfromsegmentations(segmentations, output, fig_width, fig_height, overwrite):
|
|
38
|
+
"""
|
|
39
|
+
Creates PNG images from L3 muscle and fat segmentation files
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
--segmentations : str
|
|
44
|
+
Directory with with input segmentation files. Must be L3 muscle and fat segmentations
|
|
45
|
+
(output of "mosamatic2-cli segmentmusclefatl3tensorflow")
|
|
46
|
+
|
|
47
|
+
--output : str
|
|
48
|
+
Path to output directory
|
|
49
|
+
|
|
50
|
+
--fig_width : int
|
|
51
|
+
Width of PNG image
|
|
52
|
+
|
|
53
|
+
--fig_height : int
|
|
54
|
+
Height of PNG image
|
|
55
|
+
|
|
56
|
+
--overwrite : bool
|
|
57
|
+
Overwrite contents output directory [true|false]
|
|
58
|
+
"""
|
|
59
|
+
task = CreatePngsFromSegmentationsTask(
|
|
60
|
+
inputs={'segmentations': segmentations},
|
|
61
|
+
params={'fig_width': fig_width, 'fig_height': fig_height},
|
|
62
|
+
output=output,
|
|
63
|
+
overwrite=overwrite,
|
|
64
|
+
)
|
|
65
|
+
task.run()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import Dicom2NiftiTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Converts DICOM series to NIFTI')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--images',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Directory with images',
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--output',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(),
|
|
17
|
+
help='Output directory'
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--overwrite',
|
|
21
|
+
type=click.BOOL,
|
|
22
|
+
default=False,
|
|
23
|
+
help='Overwrite [true|false]'
|
|
24
|
+
)
|
|
25
|
+
def dicom2nifti(images, output, overwrite):
|
|
26
|
+
"""
|
|
27
|
+
Converts single DICOM series (scan) to NIFTI
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
--images : str
|
|
32
|
+
Directory with DICOM images of a single series
|
|
33
|
+
|
|
34
|
+
--output : str
|
|
35
|
+
Path to output directory
|
|
36
|
+
|
|
37
|
+
--overwrite : bool
|
|
38
|
+
Overwrite contents output directory [true|false]
|
|
39
|
+
"""
|
|
40
|
+
task = Dicom2NiftiTask(
|
|
41
|
+
inputs={'images': images},
|
|
42
|
+
params=None,
|
|
43
|
+
output=output,
|
|
44
|
+
overwrite=overwrite,
|
|
45
|
+
)
|
|
46
|
+
task.run()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import RescaleDicomImagesTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Rescale DICOM files to target size')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--images',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Input directory with images'
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--output',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(),
|
|
17
|
+
help='Output directory'
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--target_size',
|
|
21
|
+
default=512,
|
|
22
|
+
help='Target size of rescaled images (default: 512)'
|
|
23
|
+
)
|
|
24
|
+
@click.option(
|
|
25
|
+
'--overwrite',
|
|
26
|
+
type=click.BOOL,
|
|
27
|
+
default=False,
|
|
28
|
+
help='Overwrite [true|false]'
|
|
29
|
+
)
|
|
30
|
+
def rescaledicomimages(images, output, target_size, overwrite):
|
|
31
|
+
"""
|
|
32
|
+
Rescales DICOM images to given (square) target size
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
--images : str
|
|
37
|
+
Directory with input DICOM images
|
|
38
|
+
|
|
39
|
+
--output : str
|
|
40
|
+
Path to output directory
|
|
41
|
+
|
|
42
|
+
--target_size : int
|
|
43
|
+
Target size for rescaled images (default: 512)
|
|
44
|
+
|
|
45
|
+
--overwrite : bool
|
|
46
|
+
Overwrite contents output directory: [true|false]
|
|
47
|
+
"""
|
|
48
|
+
task = RescaleDicomImagesTask(
|
|
49
|
+
inputs={'images': images},
|
|
50
|
+
params={'target_size': target_size},
|
|
51
|
+
output=output,
|
|
52
|
+
overwrite=overwrite
|
|
53
|
+
)
|
|
54
|
+
task.run()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import SegmentMuscleFatL3TensorFlowTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Extracts muscle and fat regions from CT images at L3 (uses PyTorch)')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--images',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(),
|
|
11
|
+
help='Input directory with images'
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--model_files',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(),
|
|
17
|
+
help='Input directory with AI model files'
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--output',
|
|
21
|
+
required=True,
|
|
22
|
+
type=click.Path(),
|
|
23
|
+
help='Output directory'
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
'--overwrite',
|
|
27
|
+
default=False,
|
|
28
|
+
type=click.BOOL,
|
|
29
|
+
help='Overwrite [true|false]'
|
|
30
|
+
)
|
|
31
|
+
def segmentmusclefatl3tensorflow(images, model_files, output, overwrite):
|
|
32
|
+
"""
|
|
33
|
+
Automatically segments muscle and fat tissue in CT images at L3 level.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
--images : str
|
|
38
|
+
Directory with input L3 images
|
|
39
|
+
|
|
40
|
+
--model_files : str
|
|
41
|
+
Directory with AI model files (model-1.0.zip, contour_model-1.0.zip, params-1.0.json)
|
|
42
|
+
|
|
43
|
+
--output : str
|
|
44
|
+
Path to output directory
|
|
45
|
+
|
|
46
|
+
--overwrite : bool
|
|
47
|
+
Overwrite contents output directory [true|false]
|
|
48
|
+
"""
|
|
49
|
+
task = SegmentMuscleFatL3TensorFlowTask(
|
|
50
|
+
inputs={'images': images, 'model_files': model_files},
|
|
51
|
+
params={'model_version': 1.0},
|
|
52
|
+
output=output,
|
|
53
|
+
overwrite=overwrite,
|
|
54
|
+
)
|
|
55
|
+
task.run()
|
|
@@ -2,12 +2,15 @@ MOSAMATIC2_BUNDLE_IDENTIFIER = 'org.mumc'
|
|
|
2
2
|
MOSAMATIC2_APP_NAME = 'mosamatic2'
|
|
3
3
|
MOSAMATIC2_VERSION_FILE = 'mosamatic2/ui/resources/VERSION'
|
|
4
4
|
MOSAMATIC2_IMAGES_DIR_PATH = 'mosamatic2/ui/resources/images'
|
|
5
|
-
MOSAMATIC2_ICONS_DIR_PATH = '
|
|
6
|
-
MOSAMATIC2_APP_ICON_FILE_NAME_WIN = '
|
|
7
|
-
MOSAMATIC2_APP_ICON_FILE_NAME_MAC = '
|
|
5
|
+
MOSAMATIC2_ICONS_DIR_PATH = 'mosamatic2/ui/resources/icons'
|
|
6
|
+
MOSAMATIC2_APP_ICON_FILE_NAME_WIN = 'mosamatic2.ico'
|
|
7
|
+
MOSAMATIC2_APP_ICON_FILE_NAME_MAC = 'mosamatic2.icns'
|
|
8
8
|
MOSAMATIC2_BACKGROUND_IMAGE_FILE_NAME = 'body-composition.jpg'
|
|
9
9
|
MOSAMATIC2_BACKGROUND_IMAGE_OPACITY = 0.4
|
|
10
10
|
|
|
11
|
+
MOSAMATIC2_SERVER_PORT = 8000
|
|
12
|
+
MOSAMATIC2_SERVER_DEBUG = True
|
|
13
|
+
|
|
11
14
|
MOSAMATIC2_WINDOW_TITLE = 'Mosamatic'
|
|
12
15
|
MOSAMATIC2_WINDOW_W = 1000
|
|
13
16
|
MOSAMATIC2_WINDOW_H = 600
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from mosamatic2.core.data.filedata import FileData
|
|
2
|
+
from mosamatic2.core.data.dicomimage import DicomImage
|
|
3
|
+
from mosamatic2.core.data.dicomimageseries import DicomImageSeries
|
|
4
|
+
from mosamatic2.core.data.multidicomimage import MultiDicomImage
|
|
5
|
+
from mosamatic2.core.data.dixonseries import DixonSeries
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from mosamatic2.core.data.filedata import FileData
|
|
2
|
+
from mosamatic2.core.utils import (
|
|
3
|
+
is_dicom,
|
|
4
|
+
load_dicom,
|
|
5
|
+
is_jpeg2000_compressed,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DicomImage(FileData):
|
|
10
|
+
def load(self):
|
|
11
|
+
if self.path():
|
|
12
|
+
if is_dicom(self.path()):
|
|
13
|
+
p = load_dicom(self.path())
|
|
14
|
+
if is_jpeg2000_compressed(p):
|
|
15
|
+
p.decompress()
|
|
16
|
+
self.set_object(p)
|
|
17
|
+
return True
|
|
18
|
+
return False
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mosamatic2.core.data.filedata import FileData
|
|
3
|
+
from mosamatic2.core.data.dicomimage import DicomImage
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DicomImageSeries(FileData):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super(DicomImageSeries, self).__init__()
|
|
9
|
+
self._images = []
|
|
10
|
+
|
|
11
|
+
def images(self):
|
|
12
|
+
return self._images
|
|
13
|
+
|
|
14
|
+
def load(self):
|
|
15
|
+
if self.path():
|
|
16
|
+
images = []
|
|
17
|
+
for f in os.listdir(self.path()):
|
|
18
|
+
f_path = os.path.join(self.path(), f)
|
|
19
|
+
image = DicomImage()
|
|
20
|
+
image.set_path(f_path)
|
|
21
|
+
if image.load():
|
|
22
|
+
images.append(image)
|
|
23
|
+
# Sort DICOM objects by instance number
|
|
24
|
+
self._images = sorted(images, key=lambda image: int(image.object().get('InstanceNumber')))
|
|
25
|
+
return True
|
|
26
|
+
return False
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from mosamatic2.core.data.filedata import FileData
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DixonSeries(FileData):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super(DixonSeries, self).__init__()
|
|
7
|
+
self._series = {'ip': None, 'op': None, 'water': None, 'fat': None}
|
|
8
|
+
|
|
9
|
+
def ip(self):
|
|
10
|
+
return self._series['ip']
|
|
11
|
+
|
|
12
|
+
def op(self):
|
|
13
|
+
return self._series['op']
|
|
14
|
+
|
|
15
|
+
def water(self):
|
|
16
|
+
return self._series['water']
|
|
17
|
+
|
|
18
|
+
def fat(self):
|
|
19
|
+
return self._series['fat']
|
|
20
|
+
|
|
21
|
+
def load(self):
|
|
22
|
+
pass
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FileData:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self._path = None
|
|
7
|
+
self._name = None
|
|
8
|
+
self._object = None
|
|
9
|
+
|
|
10
|
+
def path(self):
|
|
11
|
+
return self._path
|
|
12
|
+
|
|
13
|
+
def set_path(self, path):
|
|
14
|
+
self._path = path
|
|
15
|
+
|
|
16
|
+
def name(self):
|
|
17
|
+
return os.path.split(self.path())[1]
|
|
18
|
+
|
|
19
|
+
def object(self):
|
|
20
|
+
return self._object
|
|
21
|
+
|
|
22
|
+
def set_object(self, object):
|
|
23
|
+
self._object = object
|
|
24
|
+
|
|
25
|
+
def load(self):
|
|
26
|
+
raise NotImplementedError()
|
|
@@ -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
|
|
@@ -11,8 +11,6 @@ class LogManager:
|
|
|
11
11
|
self._suppress_print = suppress_print
|
|
12
12
|
self._listeners = []
|
|
13
13
|
file_path = os.path.join(Path.home(), 'mosamatic2.log')
|
|
14
|
-
if os.path.isfile(file_path):
|
|
15
|
-
os.remove(file_path)
|
|
16
14
|
self._file_handle = open(file_path, 'w', buffering=1)
|
|
17
15
|
atexit.register(self.close_file)
|
|
18
16
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from mosamatic2.core.pipelines.defaultpipeline import DefaultPipeline
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
|
|
12
|
+
|
|
13
|
+
class DefaultPipeline(Pipeline):
|
|
14
|
+
INPUTS = [
|
|
15
|
+
'images',
|
|
16
|
+
'model_files',
|
|
17
|
+
]
|
|
18
|
+
PARAMS = [
|
|
19
|
+
'target_size',
|
|
20
|
+
'file_type',
|
|
21
|
+
'fig_width',
|
|
22
|
+
'fig_height',
|
|
23
|
+
'model_type',
|
|
24
|
+
'model_version',
|
|
25
|
+
]
|
|
26
|
+
def __init__(self, inputs, params, output, overwrite):
|
|
27
|
+
super(DefaultPipeline, self).__init__(inputs, params, output, overwrite)
|
|
28
|
+
model_type = self.param('model_type')
|
|
29
|
+
# segmentation_task_class = SegmentMuscleFatL3Task if model_type == 'pytorch' else SegmentMuscleFatL3TensorFlowTask
|
|
30
|
+
segmentation_task_class = SegmentMuscleFatL3TensorFlowTask
|
|
31
|
+
self.add_task(
|
|
32
|
+
RescaleDicomImagesTask(
|
|
33
|
+
inputs={'images': self.input('images')},
|
|
34
|
+
params={'target_size': self.param('target_size')},
|
|
35
|
+
output=self.output(),
|
|
36
|
+
overwrite=self.overwrite(),
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
self.add_task(
|
|
40
|
+
segmentation_task_class(
|
|
41
|
+
inputs={
|
|
42
|
+
'images': os.path.join(self.output(), 'rescaledicomimagestask'),
|
|
43
|
+
'model_files': self.input('model_files'),
|
|
44
|
+
},
|
|
45
|
+
params={'model_version': self.param('model_version')},
|
|
46
|
+
output=self.output(),
|
|
47
|
+
overwrite=self.overwrite(),
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
self.add_task(
|
|
51
|
+
CalculateScoresTask(
|
|
52
|
+
inputs={
|
|
53
|
+
'images': os.path.join(self.output(), 'rescaledicomimagestask'),
|
|
54
|
+
'segmentations': os.path.join(
|
|
55
|
+
self.output(),
|
|
56
|
+
'segmentmusclefatl3pytorchtask' if model_type == 'pytorch' else 'segmentmusclefatl3tensorflowtask',
|
|
57
|
+
)
|
|
58
|
+
},
|
|
59
|
+
params={'file_type': self.param('file_type')},
|
|
60
|
+
output=self.output(),
|
|
61
|
+
overwrite=self.overwrite(),
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
self.add_task(
|
|
65
|
+
CreatePngsFromSegmentationsTask(
|
|
66
|
+
inputs={
|
|
67
|
+
'segmentations': os.path.join(
|
|
68
|
+
self.output(),
|
|
69
|
+
'segmentmusclefatl3pytorchtask' if model_type == 'pytorch' else 'segmentmusclefatl3tensorflowtask',
|
|
70
|
+
)
|
|
71
|
+
},
|
|
72
|
+
params={
|
|
73
|
+
'fig_width': self.param('fig_width'),
|
|
74
|
+
'fig_height': self.param('fig_height'),
|
|
75
|
+
},
|
|
76
|
+
output=self.output(),
|
|
77
|
+
overwrite=self.overwrite(),
|
|
78
|
+
)
|
|
79
|
+
)
|
|
@@ -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,5 @@
|
|
|
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
|