mosamatic2 2.0.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- models.py +259 -0
- mosamatic2/__init__.py +0 -0
- mosamatic2/app.py +32 -0
- mosamatic2/cli.py +50 -0
- mosamatic2/commands/__init__.py +0 -0
- mosamatic2/commands/boadockerpipeline.py +48 -0
- mosamatic2/commands/calculatemaskstatistics.py +59 -0
- mosamatic2/commands/calculatescores.py +73 -0
- mosamatic2/commands/createdicomsummary.py +61 -0
- mosamatic2/commands/createpngsfromsegmentations.py +65 -0
- mosamatic2/commands/defaultdockerpipeline.py +84 -0
- mosamatic2/commands/defaultpipeline.py +70 -0
- mosamatic2/commands/dicom2nifti.py +55 -0
- mosamatic2/commands/liveranalysispipeline.py +61 -0
- mosamatic2/commands/rescaledicomimages.py +54 -0
- mosamatic2/commands/segmentmusclefatl3tensorflow.py +55 -0
- mosamatic2/commands/selectslicefromscans.py +66 -0
- mosamatic2/commands/totalsegmentator.py +77 -0
- mosamatic2/constants.py +27 -0
- mosamatic2/core/__init__.py +0 -0
- mosamatic2/core/data/__init__.py +5 -0
- mosamatic2/core/data/dicomimage.py +27 -0
- mosamatic2/core/data/dicomimageseries.py +26 -0
- mosamatic2/core/data/dixonseries.py +22 -0
- mosamatic2/core/data/filedata.py +26 -0
- mosamatic2/core/data/multidicomimage.py +30 -0
- mosamatic2/core/data/multiniftiimage.py +26 -0
- mosamatic2/core/data/multinumpyimage.py +26 -0
- mosamatic2/core/data/niftiimage.py +13 -0
- mosamatic2/core/data/numpyimage.py +13 -0
- mosamatic2/core/managers/__init__.py +0 -0
- mosamatic2/core/managers/logmanager.py +45 -0
- mosamatic2/core/managers/logmanagerlistener.py +3 -0
- mosamatic2/core/pipelines/__init__.py +4 -0
- mosamatic2/core/pipelines/boadockerpipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/boadockerpipeline/boadockerpipeline.py +70 -0
- mosamatic2/core/pipelines/defaultdockerpipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py +28 -0
- mosamatic2/core/pipelines/defaultpipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/defaultpipeline/defaultpipeline.py +90 -0
- mosamatic2/core/pipelines/liveranalysispipeline/__init__.py +0 -0
- mosamatic2/core/pipelines/liveranalysispipeline/liveranalysispipeline.py +48 -0
- mosamatic2/core/pipelines/pipeline.py +14 -0
- mosamatic2/core/singleton.py +9 -0
- mosamatic2/core/tasks/__init__.py +13 -0
- mosamatic2/core/tasks/applythresholdtosegmentationstask/__init__.py +0 -0
- mosamatic2/core/tasks/applythresholdtosegmentationstask/applythresholdtosegmentationstask.py +117 -0
- mosamatic2/core/tasks/calculatemaskstatisticstask/__init__.py +0 -0
- mosamatic2/core/tasks/calculatemaskstatisticstask/calculatemaskstatisticstask.py +104 -0
- mosamatic2/core/tasks/calculatescorestask/__init__.py +0 -0
- mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +152 -0
- mosamatic2/core/tasks/createdicomsummarytask/__init__.py +0 -0
- mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py +88 -0
- mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py +0 -0
- mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +101 -0
- mosamatic2/core/tasks/dicom2niftitask/__init__.py +0 -0
- mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +45 -0
- mosamatic2/core/tasks/rescaledicomimagestask/__init__.py +0 -0
- mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +64 -0
- mosamatic2/core/tasks/segmentationnifti2numpytask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentationnifti2numpytask/segmentationnifti2numpytask.py +57 -0
- mosamatic2/core/tasks/segmentationnumpy2niftitask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentationnumpy2niftitask/segmentationnumpy2niftitask.py +86 -0
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +39 -0
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +122 -0
- mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/__init__.py +0 -0
- mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/paramloader.py +39 -0
- mosamatic2/core/tasks/segmentmusclefatt4pytorchtask/segmentmusclefatt4pytorchtask.py +128 -0
- mosamatic2/core/tasks/selectslicefromscanstask/__init__.py +0 -0
- mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py +249 -0
- mosamatic2/core/tasks/task.py +50 -0
- mosamatic2/core/tasks/totalsegmentatortask/__init__.py +0 -0
- mosamatic2/core/tasks/totalsegmentatortask/totalsegmentatortask.py +75 -0
- mosamatic2/core/utils.py +405 -0
- mosamatic2/server.py +146 -0
- mosamatic2/ui/__init__.py +0 -0
- mosamatic2/ui/mainwindow.py +426 -0
- mosamatic2/ui/resources/VERSION +1 -0
- mosamatic2/ui/resources/icons/mosamatic2.icns +0 -0
- mosamatic2/ui/resources/icons/mosamatic2.ico +0 -0
- mosamatic2/ui/resources/icons/spinner.gif +0 -0
- mosamatic2/ui/resources/images/body-composition.jpg +0 -0
- mosamatic2/ui/settings.py +62 -0
- mosamatic2/ui/utils.py +36 -0
- mosamatic2/ui/widgets/__init__.py +0 -0
- mosamatic2/ui/widgets/dialogs/__init__.py +0 -0
- mosamatic2/ui/widgets/dialogs/dialog.py +16 -0
- mosamatic2/ui/widgets/dialogs/helpdialog.py +9 -0
- mosamatic2/ui/widgets/panels/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/defaultpanel.py +31 -0
- mosamatic2/ui/widgets/panels/logpanel.py +65 -0
- mosamatic2/ui/widgets/panels/mainpanel.py +82 -0
- mosamatic2/ui/widgets/panels/pipelines/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/pipelines/boadockerpipelinepanel.py +195 -0
- mosamatic2/ui/widgets/panels/pipelines/defaultdockerpipelinepanel.py +314 -0
- mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +302 -0
- mosamatic2/ui/widgets/panels/pipelines/liveranalysispipelinepanel.py +187 -0
- mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py +6 -0
- mosamatic2/ui/widgets/panels/settingspanel.py +16 -0
- mosamatic2/ui/widgets/panels/stackedpanel.py +22 -0
- mosamatic2/ui/widgets/panels/tasks/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/tasks/applythresholdtosegmentationstaskpanel.py +271 -0
- mosamatic2/ui/widgets/panels/tasks/calculatemaskstatisticstaskpanel.py +215 -0
- mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +238 -0
- mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py +206 -0
- mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +247 -0
- mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +183 -0
- mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +184 -0
- mosamatic2/ui/widgets/panels/tasks/segmentationnifti2numpytaskpanel.py +192 -0
- mosamatic2/ui/widgets/panels/tasks/segmentationnumpy2niftitaskpanel.py +213 -0
- mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +216 -0
- mosamatic2/ui/widgets/panels/tasks/segmentmusclefatt4pytorchtaskpanel.py +217 -0
- mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py +193 -0
- mosamatic2/ui/widgets/panels/tasks/taskpanel.py +6 -0
- mosamatic2/ui/widgets/panels/tasks/totalsegmentatortaskpanel.py +195 -0
- mosamatic2/ui/widgets/panels/visualizations/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentpicker.py +96 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentviewer.py +130 -0
- mosamatic2/ui/widgets/panels/visualizations/liversegmentvisualization/liversegmentvisualization.py +120 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/sliceselectionviewer.py +61 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/sliceselectionvisualization.py +133 -0
- mosamatic2/ui/widgets/panels/visualizations/sliceselectionvisualization/slicetile.py +63 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py +0 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +80 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +116 -0
- mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +141 -0
- mosamatic2/ui/widgets/panels/visualizations/visualization.py +6 -0
- mosamatic2/ui/widgets/splashscreen.py +101 -0
- mosamatic2/ui/worker.py +29 -0
- mosamatic2-2.0.24.dist-info/METADATA +43 -0
- mosamatic2-2.0.24.dist-info/RECORD +136 -0
- mosamatic2-2.0.24.dist-info/WHEEL +4 -0
- mosamatic2-2.0.24.dist-info/entry_points.txt +5 -0
models.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
from torch.nn import functional as F
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def double_conv(in_c, out_c, dropout_rate):
|
|
7
|
+
conv = nn.Sequential(
|
|
8
|
+
nn.Conv2d(in_c, out_c, kernel_size=3, padding="same"),
|
|
9
|
+
nn.PReLU(),
|
|
10
|
+
nn.BatchNorm2d(out_c),
|
|
11
|
+
nn.Dropout(dropout_rate),
|
|
12
|
+
nn.Conv2d(out_c, out_c, kernel_size=3, padding="same"),
|
|
13
|
+
nn.PReLU(),
|
|
14
|
+
nn.BatchNorm2d(out_c),
|
|
15
|
+
)
|
|
16
|
+
return conv
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UNet(nn.Module):
|
|
20
|
+
def __init__(self, params, num_classes):
|
|
21
|
+
super(UNet, self).__init__()
|
|
22
|
+
# num_classes = params.dict['num_classes_bc']
|
|
23
|
+
dropout_rate = params.dict["dropout_rate"]
|
|
24
|
+
|
|
25
|
+
self.max_pool_2x2 = nn.MaxPool2d(kernel_size=2, stride=2)
|
|
26
|
+
self.down_conv_1 = double_conv(1, 32, dropout_rate)
|
|
27
|
+
self.down_conv_2 = double_conv(32, 64, dropout_rate)
|
|
28
|
+
self.down_conv_3 = double_conv(64, 128, dropout_rate)
|
|
29
|
+
self.down_conv_4 = double_conv(128, 256, dropout_rate)
|
|
30
|
+
self.down_conv_5 = double_conv(256, 512, dropout_rate)
|
|
31
|
+
|
|
32
|
+
self.up_trans_1 = nn.ConvTranspose2d(
|
|
33
|
+
in_channels=512, out_channels=256, kernel_size=2, stride=2
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self.up_conv_1 = double_conv(512, 256, dropout_rate)
|
|
37
|
+
|
|
38
|
+
self.up_trans_2 = nn.ConvTranspose2d(
|
|
39
|
+
in_channels=256, out_channels=128, kernel_size=2, stride=2
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.up_conv_2 = double_conv(256, 128, dropout_rate)
|
|
43
|
+
|
|
44
|
+
self.up_trans_3 = nn.ConvTranspose2d(
|
|
45
|
+
in_channels=128, out_channels=64, kernel_size=2, stride=2
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self.up_conv_3 = double_conv(128, 64, dropout_rate)
|
|
49
|
+
|
|
50
|
+
self.up_trans_4 = nn.ConvTranspose2d(
|
|
51
|
+
in_channels=64, out_channels=32, kernel_size=2, stride=2
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
self.up_conv_4 = double_conv(64, 32, dropout_rate)
|
|
55
|
+
|
|
56
|
+
self.out = nn.Conv2d(in_channels=32, out_channels=num_classes, kernel_size=1)
|
|
57
|
+
|
|
58
|
+
def forward(self, image):
|
|
59
|
+
|
|
60
|
+
x1 = self.down_conv_1(image)
|
|
61
|
+
# after each convolution apply max_pooling
|
|
62
|
+
p1 = self.max_pool_2x2(x1)
|
|
63
|
+
|
|
64
|
+
x2 = self.down_conv_2(p1)
|
|
65
|
+
p2 = self.max_pool_2x2(x2)
|
|
66
|
+
|
|
67
|
+
x3 = self.down_conv_3(p2)
|
|
68
|
+
p3 = self.max_pool_2x2(x3)
|
|
69
|
+
|
|
70
|
+
x4 = self.down_conv_4(p3)
|
|
71
|
+
p4 = self.max_pool_2x2(x4)
|
|
72
|
+
|
|
73
|
+
x5 = self.down_conv_5(p4)
|
|
74
|
+
|
|
75
|
+
# decoder
|
|
76
|
+
u6 = self.up_trans_1(x5)
|
|
77
|
+
x6 = self.up_conv_1(torch.cat([u6, x4], 1))
|
|
78
|
+
|
|
79
|
+
u7 = self.up_trans_2(x6)
|
|
80
|
+
x7 = self.up_conv_2(torch.cat([u7, x3], 1))
|
|
81
|
+
|
|
82
|
+
u8 = self.up_trans_3(x7)
|
|
83
|
+
x8 = self.up_conv_3(torch.cat([u8, x2], 1))
|
|
84
|
+
|
|
85
|
+
u9 = self.up_trans_4(x8)
|
|
86
|
+
x9 = self.up_conv_4(torch.cat([u9, x1], 1))
|
|
87
|
+
|
|
88
|
+
x = self.out(x9)
|
|
89
|
+
x = F.softmax(x, dim=1)
|
|
90
|
+
return x
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class BasicConv2d(nn.Module):
|
|
94
|
+
def __init__(self, in_channels, out_channels, dropout, **kwargs):
|
|
95
|
+
super(BasicConv2d, self).__init__()
|
|
96
|
+
self.conv = nn.Conv2d(
|
|
97
|
+
in_channels,
|
|
98
|
+
out_channels,
|
|
99
|
+
kernel_size=3,
|
|
100
|
+
stride=1,
|
|
101
|
+
padding="same",
|
|
102
|
+
bias=True,
|
|
103
|
+
**kwargs
|
|
104
|
+
)
|
|
105
|
+
# self.norm = nn.GroupNorm(32, out_channels, affine=True)
|
|
106
|
+
# self.norm = nn.InstanceNorm2d(out_channels, affine=True)
|
|
107
|
+
self.norm = nn.BatchNorm2d(out_channels)
|
|
108
|
+
self.dropout = nn.Dropout(dropout)
|
|
109
|
+
|
|
110
|
+
def forward(self, x):
|
|
111
|
+
x = self.conv(x)
|
|
112
|
+
x = self.norm(x)
|
|
113
|
+
x = F.relu(x, inplace=True)
|
|
114
|
+
x = self.dropout(x)
|
|
115
|
+
return x
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class AttentionGate2D(nn.Module):
|
|
119
|
+
def __init__(self, in_channels_g, in_channels_l, out_channels, **kwargs):
|
|
120
|
+
super(AttentionGate2D, self).__init__()
|
|
121
|
+
self.wg = nn.Conv2d(
|
|
122
|
+
in_channels_g, out_channels, bias=True, padding=0, kernel_size=1, stride=1
|
|
123
|
+
)
|
|
124
|
+
# self.norm_wg = nn.GroupNorm(num_groups=32, num_channels=out_channels, affine=True)
|
|
125
|
+
# self.norm_wg = nn.InstanceNorm2d(out_channels, affine=True)
|
|
126
|
+
self.norm_wg = nn.BatchNorm2d(out_channels)
|
|
127
|
+
self.wx = nn.Conv2d(
|
|
128
|
+
in_channels_l, out_channels, bias=True, padding=0, kernel_size=1, stride=1
|
|
129
|
+
)
|
|
130
|
+
# self.norm_wx = nn.GroupNorm(num_groups=32, num_channels=out_channels, affine=True)
|
|
131
|
+
# self.norm_wx = nn.InstanceNorm2d(out_channels, affine=True)
|
|
132
|
+
self.norm_wx = nn.BatchNorm2d(out_channels)
|
|
133
|
+
self.psi = nn.Conv2d(
|
|
134
|
+
in_channels=out_channels,
|
|
135
|
+
out_channels=1,
|
|
136
|
+
bias=True,
|
|
137
|
+
padding=0,
|
|
138
|
+
kernel_size=1,
|
|
139
|
+
stride=1,
|
|
140
|
+
)
|
|
141
|
+
# self.norm_psi = nn.GroupNorm(num_groups=32, num_channels=out_channels, affine=True)
|
|
142
|
+
# self.norm_psi = nn.InstanceNorm2d(out_channels, affine=True)
|
|
143
|
+
self.norm_psi = nn.BatchNorm2d(1)
|
|
144
|
+
|
|
145
|
+
def forward(self, g, x):
|
|
146
|
+
g1 = self.wg(g)
|
|
147
|
+
g1 = self.norm_wg(g1)
|
|
148
|
+
x1 = self.wx(x)
|
|
149
|
+
x1 = self.norm_wx(x1)
|
|
150
|
+
psi = F.relu(g1 + x1, inplace=True)
|
|
151
|
+
psi = self.psi(psi)
|
|
152
|
+
psi = self.norm_psi(psi)
|
|
153
|
+
psi = torch.sigmoid(psi)
|
|
154
|
+
return x * psi
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ConvBlock2D(nn.Module):
|
|
158
|
+
def __init__(self, in_channels, out_channels, dropout, **kwargs):
|
|
159
|
+
super(ConvBlock2D, self).__init__()
|
|
160
|
+
self.conv1 = BasicConv2d(
|
|
161
|
+
in_channels=in_channels, out_channels=out_channels, dropout=dropout
|
|
162
|
+
)
|
|
163
|
+
self.conv2 = BasicConv2d(
|
|
164
|
+
in_channels=out_channels, out_channels=out_channels, dropout=dropout
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def forward(self, input):
|
|
168
|
+
x1 = self.conv1(input)
|
|
169
|
+
x2 = self.conv2(x1)
|
|
170
|
+
return x2
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class AttentionUNet(nn.Module):
|
|
174
|
+
def __init__(self, params, num_classes):
|
|
175
|
+
super(AttentionUNet, self).__init__()
|
|
176
|
+
# num_classes = params.dict['num_classes_bc']
|
|
177
|
+
dropout_rate = params.dict["dropout_rate"]
|
|
178
|
+
|
|
179
|
+
self.max_pool_2x2 = nn.MaxPool2d(kernel_size=2, stride=2)
|
|
180
|
+
# self.down_conv_1 = double_conv(1, 32, dropout_rate)
|
|
181
|
+
self.down1 = ConvBlock2D(1, 32, dropout_rate)
|
|
182
|
+
# self.down_conv_2 = double_conv(32, 64, dropout_rate)
|
|
183
|
+
self.down2 = ConvBlock2D(32, 64, dropout_rate)
|
|
184
|
+
# self.down_conv_3 = double_conv(64, 128, dropout_rate)
|
|
185
|
+
self.down3 = ConvBlock2D(64, 128, dropout_rate)
|
|
186
|
+
# self.down_conv_4 = double_conv(128, 256, dropout_rate)
|
|
187
|
+
self.down4 = ConvBlock2D(128, 256, dropout_rate)
|
|
188
|
+
# self.down_conv_5 = double_conv(256, 512, dropout_rate)
|
|
189
|
+
self.down5 = ConvBlock2D(256, 512, dropout_rate)
|
|
190
|
+
|
|
191
|
+
self.up_trans_5 = nn.ConvTranspose2d(
|
|
192
|
+
in_channels=512, out_channels=256, kernel_size=2, stride=2
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# self.up_conv_1 = double_conv(512, 256, dropout_rate)
|
|
196
|
+
self.up5 = ConvBlock2D(512, 256, dropout_rate)
|
|
197
|
+
self.ag5 = AttentionGate2D(256, 256, 128)
|
|
198
|
+
|
|
199
|
+
self.up_trans_4 = nn.ConvTranspose2d(
|
|
200
|
+
in_channels=256, out_channels=128, kernel_size=2, stride=2
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# self.up_conv_2 = double_conv(256, 128, dropout_rate)
|
|
204
|
+
self.up4 = ConvBlock2D(256, 128, dropout_rate)
|
|
205
|
+
self.ag4 = AttentionGate2D(128, 128, 64)
|
|
206
|
+
self.up_trans_3 = nn.ConvTranspose2d(
|
|
207
|
+
in_channels=128, out_channels=64, kernel_size=2, stride=2
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# self.up_conv_3 = double_conv(128, 64, dropout_rate)
|
|
211
|
+
self.up3 = ConvBlock2D(128, 64, dropout_rate)
|
|
212
|
+
self.ag3 = AttentionGate2D(64, 64, 32)
|
|
213
|
+
|
|
214
|
+
self.up_trans_2 = nn.ConvTranspose2d(
|
|
215
|
+
in_channels=64, out_channels=32, kernel_size=2, stride=2
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# self.up_conv_4 = double_conv(64, 32, dropout_rate)
|
|
219
|
+
self.up2 = ConvBlock2D(64, 32, dropout_rate)
|
|
220
|
+
self.ag2 = AttentionGate2D(32, 32, 32)
|
|
221
|
+
self.out = nn.Conv2d(in_channels=32, out_channels=num_classes, kernel_size=1)
|
|
222
|
+
|
|
223
|
+
def forward(self, image):
|
|
224
|
+
|
|
225
|
+
x1 = self.down1(image)
|
|
226
|
+
# after each convolution apply max_pooling
|
|
227
|
+
p1 = self.max_pool_2x2(x1)
|
|
228
|
+
|
|
229
|
+
x2 = self.down2(p1)
|
|
230
|
+
p2 = self.max_pool_2x2(x2)
|
|
231
|
+
|
|
232
|
+
x3 = self.down3(p2)
|
|
233
|
+
p3 = self.max_pool_2x2(x3)
|
|
234
|
+
|
|
235
|
+
x4 = self.down4(p3)
|
|
236
|
+
p4 = self.max_pool_2x2(x4)
|
|
237
|
+
|
|
238
|
+
x5 = self.down5(p4)
|
|
239
|
+
|
|
240
|
+
# decoder
|
|
241
|
+
u6 = self.up_trans_5(x5)
|
|
242
|
+
s4 = self.ag5(g=u6, x=x4)
|
|
243
|
+
x6 = self.up5(torch.cat([u6, s4], 1))
|
|
244
|
+
|
|
245
|
+
u7 = self.up_trans_4(x6)
|
|
246
|
+
s3 = self.ag4(u7, x3)
|
|
247
|
+
x7 = self.up4(torch.cat([u7, s3], 1))
|
|
248
|
+
|
|
249
|
+
u8 = self.up_trans_3(x7)
|
|
250
|
+
s2 = self.ag3(u8, x2)
|
|
251
|
+
x8 = self.up3(torch.cat([u8, s2], 1))
|
|
252
|
+
|
|
253
|
+
u9 = self.up_trans_2(x8)
|
|
254
|
+
s1 = self.ag2(u9, x1)
|
|
255
|
+
x9 = self.up2(torch.cat([u9, s1], 1))
|
|
256
|
+
|
|
257
|
+
x = self.out(x9)
|
|
258
|
+
x = F.softmax(x, dim=1)
|
|
259
|
+
return x
|
mosamatic2/__init__.py
ADDED
|
File without changes
|
mosamatic2/app.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
import mosamatic2.constants as constants
|
|
4
|
+
from PySide6 import QtWidgets
|
|
5
|
+
from mosamatic2.ui.settings import Settings
|
|
6
|
+
from mosamatic2.core.managers.logmanager import LogManager
|
|
7
|
+
from mosamatic2.ui.widgets.splashscreen import SplashScreen
|
|
8
|
+
|
|
9
|
+
LOG = LogManager()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run_tests():
|
|
13
|
+
return 'PASSED'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
settings = Settings()
|
|
18
|
+
application_name = settings.get(constants.MOSAMATIC2_WINDOW_TITLE)
|
|
19
|
+
QtWidgets.QApplication.setApplicationName(application_name)
|
|
20
|
+
app = QtWidgets.QApplication(sys.argv)
|
|
21
|
+
app.setApplicationName(application_name)
|
|
22
|
+
try:
|
|
23
|
+
splash = SplashScreen()
|
|
24
|
+
splash.show()
|
|
25
|
+
sys.exit(app.exec())
|
|
26
|
+
except Exception as e:
|
|
27
|
+
LOG.error(str(e))
|
|
28
|
+
LOG.error(traceback.format_exc())
|
|
29
|
+
sys.exit(1)
|
|
30
|
+
|
|
31
|
+
if __name__ == '__main__':
|
|
32
|
+
main()
|
mosamatic2/cli.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from mosamatic2.commands import (
|
|
3
|
+
calculatescores,
|
|
4
|
+
calculatemaskstatistics,
|
|
5
|
+
rescaledicomimages,
|
|
6
|
+
segmentmusclefatl3tensorflow,
|
|
7
|
+
createpngsfromsegmentations,
|
|
8
|
+
dicom2nifti,
|
|
9
|
+
selectslicefromscans,
|
|
10
|
+
createdicomsummary,
|
|
11
|
+
defaultpipeline,
|
|
12
|
+
defaultdockerpipeline,
|
|
13
|
+
boadockerpipeline,
|
|
14
|
+
liveranalysispipeline,
|
|
15
|
+
totalsegmentator,
|
|
16
|
+
)
|
|
17
|
+
from mosamatic2.core.utils import show_doc_command
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CustomHelpGroup(click.Group):
|
|
21
|
+
def format_commands(self, ctx, formatter):
|
|
22
|
+
commands = self.list_commands(ctx)
|
|
23
|
+
with formatter.section('Commands'):
|
|
24
|
+
for command_name in commands:
|
|
25
|
+
command = self.get_command(ctx, command_name)
|
|
26
|
+
if command is None or command.hidden:
|
|
27
|
+
continue
|
|
28
|
+
help_text = command.get_short_help_str()
|
|
29
|
+
formatter.write_text(f'{command_name:15} {help_text}')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@click.group(cls=CustomHelpGroup)
|
|
33
|
+
def main():
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
main.add_command(calculatescores.calculatescores)
|
|
38
|
+
main.add_command(calculatemaskstatistics.calculatemaskstatistics)
|
|
39
|
+
main.add_command(rescaledicomimages.rescaledicomimages)
|
|
40
|
+
main.add_command(segmentmusclefatl3tensorflow.segmentmusclefatl3tensorflow)
|
|
41
|
+
main.add_command(createpngsfromsegmentations.createpngsfromsegmentations)
|
|
42
|
+
main.add_command(dicom2nifti.dicom2nifti)
|
|
43
|
+
main.add_command(selectslicefromscans.selectslicefromscans)
|
|
44
|
+
main.add_command(createdicomsummary.createdicomsummary)
|
|
45
|
+
main.add_command(defaultpipeline.defaultpipeline)
|
|
46
|
+
main.add_command(defaultdockerpipeline.defaultdockerpipeline)
|
|
47
|
+
main.add_command(boadockerpipeline.boadockerpipeline)
|
|
48
|
+
main.add_command(liveranalysispipeline.liveranalysispipeline)
|
|
49
|
+
main.add_command(totalsegmentator.totalsegmentator)
|
|
50
|
+
main.add_command(show_doc_command(main)) # Special command to show long description for command
|
|
File without changes
|
|
@@ -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,59 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import CalculateMaskStatisticsTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Calculates segmentation mask statistics')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--scans',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Directory with scans in NIFTI format',
|
|
12
|
+
)
|
|
13
|
+
@click.option(
|
|
14
|
+
'--masks',
|
|
15
|
+
required=True,
|
|
16
|
+
type=click.Path(exists=True),
|
|
17
|
+
help='Directory with segmentation mask files in NIFTI format',
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
'--output',
|
|
21
|
+
required=True,
|
|
22
|
+
type=click.Path(),
|
|
23
|
+
help='Output directory'
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
'--overwrite',
|
|
27
|
+
type=click.BOOL,
|
|
28
|
+
default=False,
|
|
29
|
+
help='Overwrite [true|false]'
|
|
30
|
+
)
|
|
31
|
+
def calculatemaskstatistics(scans, masks, output, overwrite):
|
|
32
|
+
"""
|
|
33
|
+
Calculates segmentation mask statistics. The following metrics are calculated:
|
|
34
|
+
|
|
35
|
+
(1) Mean radiation attenuation (HU)
|
|
36
|
+
(2) Standard deviation radiation attenuation (HU)
|
|
37
|
+
(3) Segmentation mask volume (mL)
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
--scans : str
|
|
42
|
+
Directory with scans in NIFTI format
|
|
43
|
+
|
|
44
|
+
--masks : str
|
|
45
|
+
Directory with segmentation mask files in NIFTI format
|
|
46
|
+
|
|
47
|
+
--output : str
|
|
48
|
+
Path to output directory
|
|
49
|
+
|
|
50
|
+
--overwrite : bool
|
|
51
|
+
Overwrite contents output directory [true|false]
|
|
52
|
+
"""
|
|
53
|
+
task = CalculateMaskStatisticsTask(
|
|
54
|
+
inputs={'scans': scans, 'masks': masks},
|
|
55
|
+
params=None,
|
|
56
|
+
output=output,
|
|
57
|
+
overwrite=overwrite,
|
|
58
|
+
)
|
|
59
|
+
task.run()
|
|
@@ -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,61 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mosamatic2.core.tasks import CreateDicomSummaryTask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command(help='Creates a DICOM summary inside a root directory')
|
|
7
|
+
@click.option(
|
|
8
|
+
'--directory',
|
|
9
|
+
required=True,
|
|
10
|
+
type=click.Path(exists=True),
|
|
11
|
+
help='Root directory with DICOM images (can be multiple scans)',
|
|
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 createdicomsummary(directory, output, overwrite):
|
|
26
|
+
"""
|
|
27
|
+
Creates a DICOM summary inside a root directory. Each patient should have
|
|
28
|
+
its own directory. Inside each patient's directory, there can be multiple
|
|
29
|
+
scans (called series) and multiple DICOM files per scan. The output of this
|
|
30
|
+
command is a file summary.txt (stored in the output directory) that contains
|
|
31
|
+
the following information:
|
|
32
|
+
|
|
33
|
+
- A list of patient directory names with the number of scans inside each
|
|
34
|
+
patient directory
|
|
35
|
+
- For each patient directory and scan directory the following information:
|
|
36
|
+
- Nr. of DICOM images in the scan
|
|
37
|
+
- Modality (e.g., CT or MRI)
|
|
38
|
+
- Image type (e.g., for Dixon scans can be in-phase, opposite-phase,
|
|
39
|
+
water or fat images)
|
|
40
|
+
- Rows/columns (size of the images)
|
|
41
|
+
- Pixel spacing (size of each pixel in mm^2)
|
|
42
|
+
- Slice thickness: (thickness of each image slice)
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
--directory : str
|
|
47
|
+
Root directory with patient directories, scans and DICOM images
|
|
48
|
+
|
|
49
|
+
--output : str
|
|
50
|
+
Path to output directory
|
|
51
|
+
|
|
52
|
+
--overwrite : bool
|
|
53
|
+
Overwrite contents output directory [true|false]
|
|
54
|
+
"""
|
|
55
|
+
task = CreateDicomSummaryTask(
|
|
56
|
+
inputs={'directory': directory},
|
|
57
|
+
params=None,
|
|
58
|
+
output=output,
|
|
59
|
+
overwrite=overwrite,
|
|
60
|
+
)
|
|
61
|
+
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()
|