mosamatic2 2.0.12__tar.gz → 2.0.14__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.

Files changed (106) hide show
  1. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/PKG-INFO +3 -1
  2. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/pyproject.toml +3 -1
  3. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/cli.py +4 -0
  4. mosamatic2-2.0.14/src/mosamatic2/commands/boadockerpipeline.py +48 -0
  5. mosamatic2-2.0.14/src/mosamatic2/commands/defaultdockerpipeline.py +73 -0
  6. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/dicom2nifti.py +11 -2
  7. mosamatic2-2.0.14/src/mosamatic2/commands/totalsegmentator.py +68 -0
  8. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/pipelines/__init__.py +2 -1
  9. mosamatic2-2.0.14/src/mosamatic2/core/pipelines/boadockerpipeline/boadockerpipeline.py +70 -0
  10. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py +5 -12
  11. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/__init__.py +2 -1
  12. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +5 -2
  13. mosamatic2-2.0.14/src/mosamatic2/core/tasks/totalsegmentatortask/totalsegmentatortask.py +50 -0
  14. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/utils.py +15 -1
  15. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/mainwindow.py +30 -0
  16. mosamatic2-2.0.14/src/mosamatic2/ui/resources/VERSION +1 -0
  17. mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels/pipelines/boadockerpipelinepanel.py +185 -0
  18. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/pipelines/defaultdockerpipelinepanel.py +0 -14
  19. mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels/tasks/totalsegmentatortaskpanel.py +195 -0
  20. mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels/visualizations/__init__.py +0 -0
  21. mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py +0 -0
  22. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +2 -2
  23. mosamatic2-2.0.12/src/mosamatic2/ui/resources/VERSION +0 -1
  24. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/README.md +0 -0
  25. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/models.py +0 -0
  26. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/__init__.py +0 -0
  27. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/app.py +0 -0
  28. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/__init__.py +0 -0
  29. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/calculatescores.py +0 -0
  30. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/createdicomsummary.py +0 -0
  31. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/createpngsfromsegmentations.py +0 -0
  32. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/defaultpipeline.py +0 -0
  33. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/rescaledicomimages.py +0 -0
  34. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/segmentmusclefatl3tensorflow.py +0 -0
  35. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/commands/selectslicefromscans.py +0 -0
  36. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/constants.py +0 -0
  37. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/__init__.py +0 -0
  38. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/__init__.py +0 -0
  39. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/dicomimage.py +0 -0
  40. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/dicomimageseries.py +0 -0
  41. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/dixonseries.py +0 -0
  42. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/filedata.py +0 -0
  43. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/multidicomimage.py +0 -0
  44. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/multiniftiimage.py +0 -0
  45. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/multinumpyimage.py +0 -0
  46. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/niftiimage.py +0 -0
  47. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/data/numpyimage.py +0 -0
  48. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/managers/__init__.py +0 -0
  49. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/managers/logmanager.py +0 -0
  50. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/managers/logmanagerlistener.py +0 -0
  51. {mosamatic2-2.0.12/src/mosamatic2/core/pipelines/defaultdockerpipeline → mosamatic2-2.0.14/src/mosamatic2/core/pipelines/boadockerpipeline}/__init__.py +0 -0
  52. {mosamatic2-2.0.12/src/mosamatic2/core/pipelines/defaultpipeline → mosamatic2-2.0.14/src/mosamatic2/core/pipelines/defaultdockerpipeline}/__init__.py +0 -0
  53. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/calculatescorestask → mosamatic2-2.0.14/src/mosamatic2/core/pipelines/defaultpipeline}/__init__.py +0 -0
  54. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/pipelines/defaultpipeline/defaultpipeline.py +0 -0
  55. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/pipelines/pipeline.py +0 -0
  56. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/singleton.py +0 -0
  57. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/createdicomsummarytask → mosamatic2-2.0.14/src/mosamatic2/core/tasks/calculatescorestask}/__init__.py +0 -0
  58. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +0 -0
  59. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/createpngsfromsegmentationstask → mosamatic2-2.0.14/src/mosamatic2/core/tasks/createdicomsummarytask}/__init__.py +0 -0
  60. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py +0 -0
  61. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/dicom2niftitask → mosamatic2-2.0.14/src/mosamatic2/core/tasks/createpngsfromsegmentationstask}/__init__.py +0 -0
  62. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +0 -0
  63. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/rescaledicomimagestask → mosamatic2-2.0.14/src/mosamatic2/core/tasks/dicom2niftitask}/__init__.py +0 -0
  64. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask → mosamatic2-2.0.14/src/mosamatic2/core/tasks/rescaledicomimagestask}/__init__.py +0 -0
  65. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +0 -0
  66. {mosamatic2-2.0.12/src/mosamatic2/core/tasks/selectslicefromscanstask → mosamatic2-2.0.14/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask}/__init__.py +0 -0
  67. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +0 -0
  68. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +0 -0
  69. {mosamatic2-2.0.12/src/mosamatic2/ui → mosamatic2-2.0.14/src/mosamatic2/core/tasks/selectslicefromscanstask}/__init__.py +0 -0
  70. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py +0 -0
  71. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/core/tasks/task.py +0 -0
  72. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets → mosamatic2-2.0.14/src/mosamatic2/core/tasks/totalsegmentatortask}/__init__.py +0 -0
  73. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/server.py +0 -0
  74. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets/dialogs → mosamatic2-2.0.14/src/mosamatic2/ui}/__init__.py +0 -0
  75. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/resources/icons/mosamatic2.icns +0 -0
  76. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/resources/icons/mosamatic2.ico +0 -0
  77. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/resources/icons/spinner.gif +0 -0
  78. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/resources/images/body-composition.jpg +0 -0
  79. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/settings.py +0 -0
  80. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/utils.py +0 -0
  81. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets/panels → mosamatic2-2.0.14/src/mosamatic2/ui/widgets}/__init__.py +0 -0
  82. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets/panels/pipelines → mosamatic2-2.0.14/src/mosamatic2/ui/widgets/dialogs}/__init__.py +0 -0
  83. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/dialogs/dialog.py +0 -0
  84. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/dialogs/helpdialog.py +0 -0
  85. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets/panels/tasks → mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels}/__init__.py +0 -0
  86. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/defaultpanel.py +0 -0
  87. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/logpanel.py +0 -0
  88. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/mainpanel.py +0 -0
  89. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets/panels/visualizations → mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels/pipelines}/__init__.py +0 -0
  90. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +0 -0
  91. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py +0 -0
  92. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/stackedpanel.py +0 -0
  93. {mosamatic2-2.0.12/src/mosamatic2/ui/widgets/panels/visualizations/slicevisualization → mosamatic2-2.0.14/src/mosamatic2/ui/widgets/panels/tasks}/__init__.py +0 -0
  94. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +0 -0
  95. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py +0 -0
  96. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +0 -0
  97. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +0 -0
  98. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +0 -0
  99. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +0 -0
  100. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py +0 -0
  101. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/tasks/taskpanel.py +0 -0
  102. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +0 -0
  103. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +0 -0
  104. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/panels/visualizations/visualization.py +0 -0
  105. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/widgets/splashscreen.py +0 -0
  106. {mosamatic2-2.0.12 → mosamatic2-2.0.14}/src/mosamatic2/ui/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mosamatic2
3
- Version: 2.0.12
3
+ Version: 2.0.14
4
4
  Summary:
5
5
  Author: Ralph Brecheisen
6
6
  Author-email: r.brecheisen@maastrichtuniversity.nl
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Requires-Dist: antspyx (>=0.5.4)
11
11
  Requires-Dist: dicom2nifti (>=2.6.2)
12
+ Requires-Dist: docker
12
13
  Requires-Dist: flask (>=3.1.2)
13
14
  Requires-Dist: moosez
14
15
  Requires-Dist: nibabel (>=5.3.2)
@@ -22,6 +23,7 @@ Requires-Dist: pyqtgraph (>=0.13.7)
22
23
  Requires-Dist: pyside6-essentials (>=6.9)
23
24
  Requires-Dist: python-gdcm (>=3.0.26)
24
25
  Requires-Dist: scipy (>=1.15.3)
26
+ Requires-Dist: slicer
25
27
  Requires-Dist: tensorboard (==2.15.2)
26
28
  Requires-Dist: tensorboard-data-server (==0.7.2)
27
29
  Requires-Dist: tensorflow (==2.15.*) ; platform_system == "Linux"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mosamatic2"
3
- version = "2.0.12"
3
+ version = "2.0.14"
4
4
  description = ""
5
5
  authors = [
6
6
  {name = "Ralph Brecheisen", email = "r.brecheisen@maastrichtuniversity.nl"}
@@ -34,6 +34,8 @@ dependencies = [
34
34
  "vtk>=9.5.1",
35
35
  "pyqtgraph>=0.13.7",
36
36
  "moosez",
37
+ "docker",
38
+ "slicer",
37
39
  ]
38
40
 
39
41
  [tool.poetry]
@@ -8,6 +8,8 @@ from mosamatic2.commands import (
8
8
  selectslicefromscans,
9
9
  createdicomsummary,
10
10
  defaultpipeline,
11
+ defaultdockerpipeline,
12
+ boadockerpipeline,
11
13
  )
12
14
  from mosamatic2.core.utils import show_doc_command
13
15
 
@@ -37,4 +39,6 @@ main.add_command(dicom2nifti.dicom2nifti)
37
39
  main.add_command(selectslicefromscans.selectslicefromscans)
38
40
  main.add_command(createdicomsummary.createdicomsummary)
39
41
  main.add_command(defaultpipeline.defaultpipeline)
42
+ main.add_command(defaultdockerpipeline.defaultdockerpipeline)
43
+ main.add_command(boadockerpipeline.boadockerpipeline)
40
44
  main.add_command(show_doc_command(main)) # Special command to show long description for command
@@ -0,0 +1,48 @@
1
+ import click
2
+
3
+ from mosamatic2.core.pipelines import BoaDockerPipeline
4
+
5
+
6
+ @click.command(help='Runs 3D body composition analysis pipeline using BOA algorithm')
7
+ @click.option(
8
+ '--scans',
9
+ required=True,
10
+ type=click.Path(exists=True),
11
+ help='Root directory with scan directories for each patient (no spaces allowed)',
12
+ )
13
+ @click.option(
14
+ '--output',
15
+ required=True,
16
+ type=click.Path(),
17
+ help='Output directory (no spaces allowed)'
18
+ )
19
+ @click.option(
20
+ '--overwrite',
21
+ type=click.BOOL,
22
+ default=False,
23
+ help='Overwrite [true|false]'
24
+ )
25
+ def boadockerpipeline(scans, output, overwrite):
26
+ """
27
+ Runs 3D body composition analysis pipeline using BOA algorithm.
28
+
29
+ Parameters
30
+ ----------
31
+ --scans : str
32
+ Root directory with scan directories for each patient. Each scan directory should
33
+ contain DICOM images for a single scan only and nothing else. Also, the directory
34
+ paths cannot contain any spaces.
35
+
36
+ --output : str
37
+ Path to output directory. No spaces allowed.
38
+
39
+ --overwrite : bool
40
+ Overwrite contents output directory [true|false]
41
+ """
42
+ pipeline = BoaDockerPipeline(
43
+ inputs={'scans': scans},
44
+ params=None,
45
+ output=output,
46
+ overwrite=overwrite,
47
+ )
48
+ pipeline.run()
@@ -0,0 +1,73 @@
1
+ import click
2
+
3
+ from mosamatic2.core.pipelines import DefaultDockerPipeline
4
+
5
+
6
+ @click.command(help='Runs default L3 analysis pipeline through Docker')
7
+ @click.option(
8
+ '--images',
9
+ required=True,
10
+ type=click.Path(exists=True),
11
+ help='Directory with L3 images (no spaces allowed)',
12
+ )
13
+ @click.option(
14
+ '--model_files',
15
+ required=True,
16
+ type=click.Path(),
17
+ help='Input directory with AI model files (no spaces allowed)'
18
+ )
19
+ @click.option(
20
+ '--output',
21
+ required=True,
22
+ type=click.Path(),
23
+ help='Output directory (no spaces allowed)'
24
+ )
25
+ @click.option(
26
+ '--overwrite',
27
+ type=click.BOOL,
28
+ default=False,
29
+ help='Overwrite [true|false]'
30
+ )
31
+ def defaultdockerpipeline(images, model_files, output, overwrite):
32
+ """
33
+ Runs default L3 analysis pipeline through Docker
34
+
35
+ Parameters
36
+ ----------
37
+ --images : str
38
+ Directory with L3 images. Warning: This directory path cannot contain any spaces!
39
+ Docker has issues working with that.
40
+
41
+ --model_files : str
42
+ Directory with AI model files. This directory can ONLY contain the following
43
+ files:
44
+
45
+ (1) contour_model-1.0.zip
46
+ (2) model-1.0.zip
47
+ (3) params-1.0.json
48
+
49
+ These files can be downloaded from:
50
+ https://mosamatic.rbeesoft.nl/wordpress/mosamatic/installation/
51
+
52
+ Warning: This directory path cannot contain any spaces!
53
+
54
+ --output : str
55
+ Path to output directory (no spaces!)
56
+
57
+ --overwrite : bool
58
+ Overwrite contents output directory [true|false]
59
+ """
60
+ pipeline = DefaultDockerPipeline(
61
+ inputs={'images': images, 'model_files': model_files},
62
+ params={
63
+ 'target_size': 512,
64
+ 'model_type': 'tensorflow',
65
+ 'model_version': 1.0,
66
+ 'file_type': 'npy',
67
+ 'fig_width': 10,
68
+ 'fig_height': 10,
69
+ },
70
+ output=output,
71
+ overwrite=overwrite,
72
+ )
73
+ pipeline.run()
@@ -10,6 +10,12 @@ from mosamatic2.core.tasks import Dicom2NiftiTask
10
10
  type=click.Path(exists=True),
11
11
  help='Root directory with DICOM scans (one for each patient)',
12
12
  )
13
+ @click.option(
14
+ '--compressed',
15
+ type=click.BOOL,
16
+ default=True,
17
+ help='Compress with gzip [true|false] (default: True)'
18
+ )
13
19
  @click.option(
14
20
  '--output',
15
21
  required=True,
@@ -22,7 +28,7 @@ from mosamatic2.core.tasks import Dicom2NiftiTask
22
28
  default=False,
23
29
  help='Overwrite [true|false]'
24
30
  )
25
- def dicom2nifti(scans, output, overwrite):
31
+ def dicom2nifti(scans, compressed, output, overwrite):
26
32
  """
27
33
  Converts DICOM scans to NIFTI format.
28
34
 
@@ -31,6 +37,9 @@ def dicom2nifti(scans, output, overwrite):
31
37
  --scans : str
32
38
  Root directory with DICOM scans (one subdirectory for each patient)
33
39
 
40
+ --compressed : bool
41
+ Whether to compress the NIFTI file with gzip or not (default: True)
42
+
34
43
  --output : str
35
44
  Path to output directory
36
45
 
@@ -39,7 +48,7 @@ def dicom2nifti(scans, output, overwrite):
39
48
  """
40
49
  task = Dicom2NiftiTask(
41
50
  inputs={'scans': scans},
42
- params=None,
51
+ params={'compressed': compressed},
43
52
  output=output,
44
53
  overwrite=overwrite,
45
54
  )
@@ -0,0 +1,68 @@
1
+ import click
2
+
3
+ from mosamatic2.core.tasks import TotalSegmentatorTask
4
+
5
+
6
+ @click.command(help='Run Total Segmentator on 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
+ '--tasks',
21
+ default='total',
22
+ help='Comma-separated list of Total Segmentator tasks to run (no spaces!)'
23
+ )
24
+ @click.option(
25
+ '--overwrite',
26
+ type=click.BOOL,
27
+ default=False,
28
+ help='Overwrite [true|false]'
29
+ )
30
+ def totalsegmentator(scans, tasks, output, overwrite):
31
+ """
32
+ Run Total Segmentator on CT scans. If you want to run specialized tasks
33
+ like "liver_segments" or "liver_vessels" you need an educational license.
34
+ Check out https://github.com/wasserth/TotalSegmentator?tab=readme-ov-file
35
+ to find out how to get such a license.
36
+
37
+ Parameters
38
+ ----------
39
+ --scans : str
40
+ Directory to scans. Each patient's scan should be in a separate
41
+ subdirectory. For example:
42
+
43
+ /scans
44
+ /scans/patient1
45
+ /scans/patient1/file1.dcm
46
+ /scans/patient1/file2.dcm
47
+ ...
48
+ /scans/patient2
49
+ ...
50
+
51
+ --output : str
52
+ Path to output directory where selected slices will be placed. Each
53
+ slice's file name will be the same as the scan directory name, so in
54
+ the example above that would be "patient1", "patient2", etc.
55
+
56
+ --tasks : str
57
+ Comma-separated list of Total Segmentator tasks to run (no spaces!)
58
+
59
+ --overwrite : bool
60
+ Overwrite contents output directory [true|false]
61
+ """
62
+ task = TotalSegmentatorTask(
63
+ inputs={'scans': scans},
64
+ params={'tasks': tasks},
65
+ output=output,
66
+ overwrite=overwrite,
67
+ )
68
+ task.run()
@@ -1,2 +1,3 @@
1
1
  from mosamatic2.core.pipelines.defaultpipeline.defaultpipeline import DefaultPipeline
2
- from mosamatic2.core.pipelines.defaultdockerpipeline.defaultdockerpipeline import DefaultDockerPipeline
2
+ from mosamatic2.core.pipelines.defaultdockerpipeline.defaultdockerpipeline import DefaultDockerPipeline
3
+ from mosamatic2.core.pipelines.boadockerpipeline.boadockerpipeline import BoaDockerPipeline
@@ -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)}')
@@ -1,17 +1,9 @@
1
1
  import os
2
-
3
2
  from mosamatic2.core.pipelines.pipeline import Pipeline
4
3
  from mosamatic2.core.managers.logmanager import LogManager
4
+ from mosamatic2.core.utils import is_docker_running, to_unix_path
5
5
 
6
6
  LOG = LogManager()
7
- DOCKER_SCRIPT = """
8
- docker run --rm ^
9
- -v "%IMAGES%":/data/images ^
10
- -v "%MODEL_FILES%":/data/model_files ^
11
- -v "%OUTPUT%":/data/output ^
12
- brecheisen/mosamatic2-cli:%VERSION% defaultpipeline ^
13
- --images /data/images --model_files /data/model_files --output /data/output --overwrite true
14
- """
15
7
 
16
8
 
17
9
  class DefaultDockerPipeline(Pipeline):
@@ -32,10 +24,11 @@ class DefaultDockerPipeline(Pipeline):
32
24
  super(DefaultDockerPipeline, self).__init__(inputs, params, output, overwrite)
33
25
 
34
26
  def run(self):
27
+ assert is_docker_running()
35
28
  docker_script = 'docker run --rm ' + \
36
- '-v "{}":/data/images '.format(self.input('images')) + \
37
- '-v "{}":/data/model_files '.format(self.input('model_files')) + \
38
- '-v "{}":/data/output '.format(self.output()) + \
29
+ '-v "{}":/data/images '.format(to_unix_path(self.input('images'))) + \
30
+ '-v "{}":/data/model_files '.format(to_unix_path(self.input('model_files'))) + \
31
+ '-v "{}":/data/output '.format(to_unix_path(self.output())) + \
39
32
  'brecheisen/mosamatic2-cli:{} defaultpipeline '.format(self.param('version')) + \
40
33
  '--images /data/images --model_files /data/model_files --output /data/output --overwrite true'
41
34
  LOG.info(f'Running Docker script: {docker_script}')
@@ -4,4 +4,5 @@ from mosamatic2.core.tasks.calculatescorestask.calculatescorestask import Calcul
4
4
  from mosamatic2.core.tasks.createpngsfromsegmentationstask.createpngsfromsegmentationstask import CreatePngsFromSegmentationsTask
5
5
  from mosamatic2.core.tasks.dicom2niftitask.dicom2niftitask import Dicom2NiftiTask
6
6
  from mosamatic2.core.tasks.selectslicefromscanstask.selectslicefromscanstask import SelectSliceFromScansTask
7
- from mosamatic2.core.tasks.createdicomsummarytask.createdicomsummarytask import CreateDicomSummaryTask
7
+ from mosamatic2.core.tasks.createdicomsummarytask.createdicomsummarytask import CreateDicomSummaryTask
8
+ from mosamatic2.core.tasks.totalsegmentatortask.totalsegmentatortask import TotalSegmentatorTask
@@ -9,7 +9,7 @@ LOG = LogManager()
9
9
 
10
10
  class Dicom2NiftiTask(Task):
11
11
  INPUTS = ['scans']
12
- PARAMS = []
12
+ PARAMS = ['compressed']
13
13
 
14
14
  def __init__(self, inputs, params, output, overwrite):
15
15
  super(Dicom2NiftiTask, self).__init__(inputs, params, output, overwrite)
@@ -28,7 +28,10 @@ class Dicom2NiftiTask(Task):
28
28
  for step in range(nr_steps):
29
29
  scan_dir = scan_dirs[step]
30
30
  scan_name = os.path.split(scan_dir)[1]
31
- nifti_file_name = scan_name + '.nii.gz'
31
+ if self.param('compressed'):
32
+ nifti_file_name = scan_name + '.nii.gz'
33
+ else:
34
+ nifti_file_name = scan_name + '.nii'
32
35
  LOG.info(f'Converting DICOM series in {scan_dir} to {nifti_file_name}')
33
36
  dicom2nifti.dicom_series_to_nifti(
34
37
  scan_dir,
@@ -0,0 +1,50 @@
1
+ import os
2
+ import shutil
3
+ import tempfile
4
+ from totalsegmentator.python_api import totalsegmentator
5
+ from mosamatic2.core.tasks.task import Task
6
+ from mosamatic2.core.managers.logmanager import LogManager
7
+
8
+ LOG = LogManager()
9
+ TOTAL_SEGMENTATOR_OUTPUT_DIR = os.path.join(tempfile.gettempdir(), 'total_segmentator_output')
10
+
11
+
12
+ class TotalSegmentatorTask(Task):
13
+ INPUTS = ['scans']
14
+ PARAMS = ['tasks']
15
+
16
+ def __init__(self, inputs, params, output, overwrite):
17
+ super(TotalSegmentatorTask, self).__init__(inputs, params, output, overwrite)
18
+ LOG.info(f'Using temporary output directory: {TOTAL_SEGMENTATOR_OUTPUT_DIR}')
19
+
20
+ def load_scan_dirs(self):
21
+ scan_dirs = []
22
+ for d in os.listdir(self.input('scans')):
23
+ scan_dir = os.path.join(self.input('scans'), d)
24
+ if os.path.isdir(scan_dir):
25
+ scan_dirs.append(scan_dir)
26
+ return scan_dirs
27
+
28
+ def extract_masks(self, scan_dir):
29
+ os.makedirs(TOTAL_SEGMENTATOR_OUTPUT_DIR, exist_ok=True)
30
+ tasks = self.param('tasks').split(",") if self.param('tasks') else []
31
+ for task in tasks:
32
+ LOG.info(f'Running task {task}...')
33
+ totalsegmentator(input=scan_dir, output=TOTAL_SEGMENTATOR_OUTPUT_DIR, task=task)
34
+
35
+ def run(self):
36
+ scan_dirs = self.load_scan_dirs()
37
+ nr_steps = len(scan_dirs)
38
+ for step in range(nr_steps):
39
+ scan_dir = scan_dirs[step]
40
+ try:
41
+ self.extract_masks(scan_dir)
42
+ except Exception as e:
43
+ LOG.error(f'{scan_dir}: Could not extract masks [{str(e)}]. Skipping scan...')
44
+ self.set_progress(step, nr_steps)
45
+ LOG.info(f'Copying temporary output to final output directory...')
46
+ for f in os.listdir(TOTAL_SEGMENTATOR_OUTPUT_DIR):
47
+ if f.endswith('.nii') or f.endswith('.nii.gz'):
48
+ f_path = os.path.join(TOTAL_SEGMENTATOR_OUTPUT_DIR, f)
49
+ shutil.move(f_path, self.output())
50
+ LOG.info(f'Copied {f}')
@@ -350,4 +350,18 @@ def convert_numpy_array_to_png_image(
350
350
  png_file_name += '.png'
351
351
  png_file_path = os.path.join(output_dir_path, png_file_name)
352
352
  image.save(png_file_path)
353
- return png_file_path
353
+ return png_file_path
354
+
355
+
356
+ def is_docker_running():
357
+ import docker
358
+ try:
359
+ client = docker.from_env()
360
+ client.ping()
361
+ return True
362
+ except Exception:
363
+ return False
364
+
365
+
366
+ def to_unix_path(path):
367
+ return path.replace("\\", "/").replace(" ", "\\ ")
@@ -21,8 +21,10 @@ from mosamatic2.ui.widgets.panels.tasks.calculatescorestaskpanel import Calculat
21
21
  from mosamatic2.ui.widgets.panels.tasks.dicom2niftitaskpanel import Dicom2NiftiTaskPanel
22
22
  from mosamatic2.ui.widgets.panels.tasks.createdicomsummarytaskpanel import CreateDicomSummaryTaskPanel
23
23
  from mosamatic2.ui.widgets.panels.tasks.selectslicefromscanstaskpanel import SelectSliceFromScansTaskPanel
24
+ from mosamatic2.ui.widgets.panels.tasks.totalsegmentatortaskpanel import TotalSegmentatorTaskPanel
24
25
  from mosamatic2.ui.widgets.panels.pipelines.defaultpipelinepanel import DefaultPipelinePanel
25
26
  from mosamatic2.ui.widgets.panels.pipelines.defaultdockerpipelinepanel import DefaultDockerPipelinePanel
27
+ from mosamatic2.ui.widgets.panels.pipelines.boadockerpipelinepanel import BoaDockerPipelinePanel
26
28
  from mosamatic2.ui.widgets.panels.visualizations.slicevisualization.slicevisualization import SliceVisualization
27
29
 
28
30
  LOG = LogManager()
@@ -42,8 +44,10 @@ class MainWindow(QMainWindow):
42
44
  self._dicom2nifti_task_panel = None
43
45
  self._create_dicom_summary_task_panel = None
44
46
  self._select_slice_from_scans_task_panel = None
47
+ self._total_segmentator_task_panel = None
45
48
  self._default_pipeline_panel = None
46
49
  self._default_docker_pipeline_panel = None
50
+ self._boa_docker_pipeline_panel = None
47
51
  self._slice_visualization = None
48
52
  self.init_window()
49
53
 
@@ -88,6 +92,8 @@ class MainWindow(QMainWindow):
88
92
  create_dicom_summary_task_action.triggered.connect(self.handle_create_dicom_summary_task_action)
89
93
  select_slice_from_scans_task_action = QAction('SelectSliceFromScansTask', self)
90
94
  select_slice_from_scans_task_action.triggered.connect(self.handle_select_slice_from_scans_task_action)
95
+ total_segmentator_task_action = QAction('TotalSegmentatorTask', self)
96
+ total_segmentator_task_action.triggered.connect(self.handle_total_segmentator_task_action)
91
97
  tasks_menu = self.menuBar().addMenu('Tasks')
92
98
  tasks_menu.addAction(rescale_dicom_images_task_action)
93
99
  tasks_menu.addAction(segment_muscle_fat_l3_tensorflow_task_action)
@@ -96,15 +102,19 @@ class MainWindow(QMainWindow):
96
102
  tasks_menu.addAction(dicom2nifti_task_action)
97
103
  tasks_menu.addAction(create_dicom_summary_task_action)
98
104
  tasks_menu.addAction(select_slice_from_scans_task_action)
105
+ tasks_menu.addAction(total_segmentator_task_action)
99
106
 
100
107
  def init_pipelines_menu(self):
101
108
  default_pipeline_action = QAction('DefaultPipeline', self)
102
109
  default_pipeline_action.triggered.connect(self.handle_default_pipeline_action)
103
110
  default_docker_pipeline_action = QAction('DefaultDockerPipeline', self)
104
111
  default_docker_pipeline_action.triggered.connect(self.handle_default_docker_pipeline_action)
112
+ boa_docker_pipeline_action = QAction('BoaDockerPipeline', self)
113
+ boa_docker_pipeline_action.triggered.connect(self.handle_boa_docker_pipeline_action)
105
114
  pipelines_menu = self.menuBar().addMenu('Pipelines')
106
115
  pipelines_menu.addAction(default_pipeline_action)
107
116
  pipelines_menu.addAction(default_docker_pipeline_action)
117
+ pipelines_menu.addAction(boa_docker_pipeline_action)
108
118
 
109
119
  def init_visualizations_menu(self):
110
120
  slice_visualization_action = QAction('SliceVisualization', self)
@@ -132,8 +142,10 @@ class MainWindow(QMainWindow):
132
142
  self._main_panel.add_panel(self.dicom2nifti_task_panel(), 'dicom2niftitaskpanel')
133
143
  self._main_panel.add_panel(self.create_dicom_summary_task_panel(), 'createdicomsummarytaskpanel')
134
144
  self._main_panel.add_panel(self.select_slice_from_scans_task_panel(), 'selectslicefromscanstaskpanel')
145
+ self._main_panel.add_panel(self.total_segmentator_task_panel(), 'totalsegmentatortaskpanel')
135
146
  self._main_panel.add_panel(self.default_pipeline_panel(), 'defaultpipelinepanel')
136
147
  self._main_panel.add_panel(self.default_docker_pipeline_panel(), 'defaultdockerpipelinepanel')
148
+ self._main_panel.add_panel(self.boa_docker_pipeline_panel(), 'boadockerpipelinepanel')
137
149
  self._main_panel.add_panel(self.slice_visualization(), 'slicevisualization')
138
150
  self._main_panel.select_panel('defaultpipelinepanel')
139
151
  return self._main_panel
@@ -180,6 +192,11 @@ class MainWindow(QMainWindow):
180
192
  if not self._select_slice_from_scans_task_panel:
181
193
  self._select_slice_from_scans_task_panel = SelectSliceFromScansTaskPanel()
182
194
  return self._select_slice_from_scans_task_panel
195
+
196
+ def total_segmentator_task_panel(self):
197
+ if not self._total_segmentator_task_panel:
198
+ self._total_segmentator_task_panel = TotalSegmentatorTaskPanel()
199
+ return self._total_segmentator_task_panel
183
200
 
184
201
  def default_pipeline_panel(self):
185
202
  if not self._default_pipeline_panel:
@@ -190,6 +207,11 @@ class MainWindow(QMainWindow):
190
207
  if not self._default_docker_pipeline_panel:
191
208
  self._default_docker_pipeline_panel = DefaultDockerPipelinePanel()
192
209
  return self._default_docker_pipeline_panel
210
+
211
+ def boa_docker_pipeline_panel(self):
212
+ if not self._boa_docker_pipeline_panel:
213
+ self._boa_docker_pipeline_panel = BoaDockerPipelinePanel()
214
+ return self._boa_docker_pipeline_panel
193
215
 
194
216
  def slice_visualization(self):
195
217
  if not self._slice_visualization:
@@ -224,12 +246,18 @@ class MainWindow(QMainWindow):
224
246
  def handle_select_slice_from_scans_task_action(self):
225
247
  self.main_panel().select_panel('selectslicefromscanstaskpanel')
226
248
 
249
+ def handle_total_segmentator_task_action(self):
250
+ self.main_panel().select_panel('totalsegmentatortaskpanel')
251
+
227
252
  def handle_default_pipeline_action(self):
228
253
  self.main_panel().select_panel('defaultpipelinepanel')
229
254
 
230
255
  def handle_default_docker_pipeline_action(self):
231
256
  self.main_panel().select_panel('defaultdockerpipelinepanel')
232
257
 
258
+ def handle_boa_docker_pipeline_action(self):
259
+ self.main_panel().select_panel('boadockerpipelinepanel')
260
+
233
261
  def handle_slice_visualization_action(self):
234
262
  self.main_panel().select_panel('slicevisualization')
235
263
 
@@ -246,8 +274,10 @@ class MainWindow(QMainWindow):
246
274
  self.dicom2nifti_task_panel().save_inputs_and_parameters()
247
275
  self.create_dicom_summary_task_panel().save_inputs_and_parameters()
248
276
  self.select_slice_from_scans_task_panel().save_inputs_and_parameters()
277
+ self.total_segmentator_task_panel().save_inputs_and_parameters()
249
278
  self.default_pipeline_panel().save_inputs_and_parameters()
250
279
  self.default_docker_pipeline_panel().save_inputs_and_parameters()
280
+ self.boa_docker_pipeline_panel().save_inputs_and_parameters()
251
281
  self.slice_visualization().save_inputs_and_parameters()
252
282
  return super().closeEvent(event)
253
283