Initial version
Browse files- .gitattributes +1 -0
- README.md +45 -0
- configs/inference.json +124 -0
- configs/logging.conf +21 -0
- configs/metadata.json +62 -0
- configs/train.json +306 -0
- docs/README.md +38 -0
- docs/SC-N-2-3-0.npy +3 -0
- docs/license.txt +21 -0
- docs/visualise.ipynb +0 -0
- models/model.pt +3 -0
- models/model.ts +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
models/model.ts filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
tags:
|
3 |
+
- monai
|
4 |
+
- medical
|
5 |
+
library_name: monai
|
6 |
+
license: unknown
|
7 |
+
---
|
8 |
+
|
9 |
+
# 3 Label Ventricular Segmentation
|
10 |
+
|
11 |
+
This network segments cardiac ventricle in 2D short axis MR images. The left ventricular pool is class 1, left ventricular myocardium class 2, and right ventricular pool class 3. Full cycle segmentation with this network is possible although much of the training data is composed of segmented end-diastole images. The input to the network is single 2D images thus segmenting whole time-dependent volumes consists of multiple inference operations.
|
12 |
+
|
13 |
+
The network and training scheme are essentially identical to that described in:
|
14 |
+
|
15 |
+
`Kerfoot E., Clough J., Oksuz I., Lee J., King A.P., Schnabel J.A. (2019) Left-Ventricle Quantification Using Residual U-Net. In: Pop M. et al. (eds) Statistical Atlases and Computational Models of the Heart. Atrial Segmentation and LV Quantification Challenges. STACOM 2018. Lecture Notes in Computer Science, vol 11395. Springer, Cham. https://doi.org/10.1007/978-3-030-12029-0_40`
|
16 |
+
|
17 |
+
## Data
|
18 |
+
|
19 |
+
The dataset used to train this network unfortunately cannot be made public as it contains unreleased image data from King's College London. Existing public datasets such as the[Sunnybrook Cardiac Dataset](http://www.cardiacatlas.org/studies/sunnybrook-cardiac-data/) and [ACDC Challenge](https://www.creatis.insa-lyon.fr/Challenge/acdc/) set can be used to train a similar network.
|
20 |
+
|
21 |
+
The `train.json` configuration assumes all data is stored in a single npz file with keys "images" and "segs" containing respectively the raw image data and their accompanying segmentations. The given network was training with stored volumes with shapes `(9095, 256, 256)` thus other data of differing spatial dimensions must be cropped to `(256, 256)` or zero-padded to that size. For the training data this was done as a preprocessing step but the original pixel values are otherwise unchanged from their original forms.
|
22 |
+
|
23 |
+
## Training
|
24 |
+
|
25 |
+
The network is trained with this data in conjunction with a series of augmentations for regularisation and robustness. Many of the original images are smaller than the expected size of `(256, 256)` and so were zero-padded, the network can thus be expected to be robust against large amounts of empty space in the inputs. Rotation and zooming is also applied to force the network to learn different sizes and orientations of the heart in the field of view.
|
26 |
+
|
27 |
+
Free-form deformation is applied to vary the shape of the heart and its surrounding tissues which mimics to a degree deformation like what would be observed through the cardiac cycle. This of course does not replicate the heart moving through plane during the cycle or represent other observed changes but does provide enough variation that full-cycle segmentation is generally acceptable.
|
28 |
+
|
29 |
+
Smooth fields are used to vary contrast and intensity in localised regions to simulate some of the variation in image quality caused by acquisition artefacts. Guassian noise is also added to simulate poor quality acquisition. These together force the network to learn to deal with a wider variation of image quality and partially to account for the difference between scanner vendors.
|
30 |
+
|
31 |
+
Training is invoked with the following command line:
|
32 |
+
|
33 |
+
```sh
|
34 |
+
python -m monai.bundle run training --meta_file configs/metadata.json --config_file configs/train.json --logging_file configs/logging.conf --bundle_root .
|
35 |
+
```
|
36 |
+
|
37 |
+
The dataset file is assumed to be `allimages3label.npz` but can be changed by setting the `dataset_file` value to your own file.
|
38 |
+
|
39 |
+
## Inference
|
40 |
+
|
41 |
+
An example notebook [visualise.ipynb](./visualise.ipynb) demonstrates using the network directly with input images. Inference of 3D volumes only can be accomplished with the `inference.json` configuration:
|
42 |
+
|
43 |
+
```sh
|
44 |
+
python -m monai.bundle run evaluating --meta_file configs/metadata.json --config_file configs/inference.json --logging_file configs/logging.conf --dataset_dir dataset --output_dir ./output/ --bundle_root .
|
45 |
+
```
|
configs/inference.json
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"imports": [
|
3 |
+
"$import glob"
|
4 |
+
],
|
5 |
+
"device": "$torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')",
|
6 |
+
"ckpt_path": "$@bundle_root + '/models/model.pt'",
|
7 |
+
"dataset_dir": "/workspace/data",
|
8 |
+
"output_dir": "./output",
|
9 |
+
"datalist": "$list(sorted(glob.glob(@dataset_dir + '/*.nii*')))",
|
10 |
+
"network_def": {
|
11 |
+
"_target_": "UNet",
|
12 |
+
"spatial_dims": 2,
|
13 |
+
"in_channels": 1,
|
14 |
+
"out_channels": 4,
|
15 |
+
"channels": [
|
16 |
+
16,
|
17 |
+
32,
|
18 |
+
64,
|
19 |
+
128,
|
20 |
+
256
|
21 |
+
],
|
22 |
+
"strides": [
|
23 |
+
2,
|
24 |
+
2,
|
25 |
+
2,
|
26 |
+
2
|
27 |
+
],
|
28 |
+
"num_res_units": 2
|
29 |
+
},
|
30 |
+
"network": "$@network_def.to(@device)",
|
31 |
+
"preprocessing": {
|
32 |
+
"_target_": "Compose",
|
33 |
+
"transforms": [
|
34 |
+
{
|
35 |
+
"_target_": "LoadImaged",
|
36 |
+
"keys": "image"
|
37 |
+
},
|
38 |
+
{
|
39 |
+
"_target_": "EnsureChannelFirstd",
|
40 |
+
"keys": "image"
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"_target_": "ScaleIntensityd",
|
44 |
+
"keys": "image"
|
45 |
+
},
|
46 |
+
{
|
47 |
+
"_target_": "EnsureTyped",
|
48 |
+
"keys": "image",
|
49 |
+
"device": "@device"
|
50 |
+
}
|
51 |
+
]
|
52 |
+
},
|
53 |
+
"dataset": {
|
54 |
+
"_target_": "Dataset",
|
55 |
+
"data": "$[{'image': i} for i in @datalist]",
|
56 |
+
"transform": "@preprocessing"
|
57 |
+
},
|
58 |
+
"dataloader": {
|
59 |
+
"_target_": "DataLoader",
|
60 |
+
"dataset": "@dataset",
|
61 |
+
"batch_size": 1,
|
62 |
+
"shuffle": false,
|
63 |
+
"num_workers": 0
|
64 |
+
},
|
65 |
+
"inferer": {
|
66 |
+
"_target_": "SliceInferer",
|
67 |
+
"roi_size": [
|
68 |
+
256,
|
69 |
+
256
|
70 |
+
],
|
71 |
+
"spatial_dim": 2
|
72 |
+
},
|
73 |
+
"postprocessing": {
|
74 |
+
"_target_": "Compose",
|
75 |
+
"transforms": [
|
76 |
+
{
|
77 |
+
"_target_": "Activationsd",
|
78 |
+
"keys": "pred",
|
79 |
+
"softmax": true
|
80 |
+
},
|
81 |
+
{
|
82 |
+
"_target_": "Invertd",
|
83 |
+
"keys": "pred",
|
84 |
+
"transform": "@preprocessing",
|
85 |
+
"orig_keys": "image",
|
86 |
+
"meta_key_postfix": "meta_dict",
|
87 |
+
"nearest_interp": false,
|
88 |
+
"to_tensor": true
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"_target_": "AsDiscreted",
|
92 |
+
"keys": "pred",
|
93 |
+
"argmax": true
|
94 |
+
},
|
95 |
+
{
|
96 |
+
"_target_": "SaveImaged",
|
97 |
+
"keys": "pred",
|
98 |
+
"meta_keys": "pred_meta_dict",
|
99 |
+
"output_dir": "@output_dir"
|
100 |
+
}
|
101 |
+
]
|
102 |
+
},
|
103 |
+
"handlers": [
|
104 |
+
{
|
105 |
+
"_target_": "CheckpointLoader",
|
106 |
+
"load_path": "@ckpt_path",
|
107 |
+
"load_dict": {
|
108 |
+
"model": "@network"
|
109 |
+
}
|
110 |
+
}
|
111 |
+
],
|
112 |
+
"evaluator": {
|
113 |
+
"_target_": "SupervisedEvaluator",
|
114 |
+
"device": "@device",
|
115 |
+
"val_data_loader": "@dataloader",
|
116 |
+
"network": "@network",
|
117 |
+
"inferer": "@inferer",
|
118 |
+
"postprocessing": "@postprocessing",
|
119 |
+
"val_handlers": "@handlers"
|
120 |
+
},
|
121 |
+
"evaluating": [
|
122 |
+
"$@evaluator.run()"
|
123 |
+
]
|
124 |
+
}
|
configs/logging.conf
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[loggers]
|
2 |
+
keys=root
|
3 |
+
|
4 |
+
[handlers]
|
5 |
+
keys=consoleHandler
|
6 |
+
|
7 |
+
[formatters]
|
8 |
+
keys=fullFormatter
|
9 |
+
|
10 |
+
[logger_root]
|
11 |
+
level=INFO
|
12 |
+
handlers=consoleHandler
|
13 |
+
|
14 |
+
[handler_consoleHandler]
|
15 |
+
class=StreamHandler
|
16 |
+
level=INFO
|
17 |
+
formatter=fullFormatter
|
18 |
+
args=(sys.stdout,)
|
19 |
+
|
20 |
+
[formatter_fullFormatter]
|
21 |
+
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
configs/metadata.json
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"schema": "https://github.com/Project-MONAI/MONAI-extra-test-data/releases/download/0.8.1/meta_schema_20220324.json",
|
3 |
+
"version": "0.1.0",
|
4 |
+
"changelog": {
|
5 |
+
"0.1.0": "Initial version"
|
6 |
+
},
|
7 |
+
"monai_version": "0.9.0",
|
8 |
+
"pytorch_version": "1.10.2",
|
9 |
+
"numpy_version": "1.21.2",
|
10 |
+
"optional_packages_version": {
|
11 |
+
"nibabel": "3.2.1",
|
12 |
+
"pytorch-ignite": "0.4.8"
|
13 |
+
},
|
14 |
+
"task": "Segments the left and right ventricle in 2D short axis MR images",
|
15 |
+
"description": "This network segments full cycle short axis images of the ventricles, labelling LV pool separate from myocardium and RV pool",
|
16 |
+
"authors": "Eric Kerfoot",
|
17 |
+
"copyright": "Copyright (c) Eric Kerfoot, KCL",
|
18 |
+
"license": "See license.txt",
|
19 |
+
"network_data_format": {
|
20 |
+
"inputs": {
|
21 |
+
"image": {
|
22 |
+
"type": "image",
|
23 |
+
"format": "magnitude",
|
24 |
+
"modality": "MR",
|
25 |
+
"num_channels": 1,
|
26 |
+
"spatial_shape": [
|
27 |
+
256,
|
28 |
+
256
|
29 |
+
],
|
30 |
+
"dtype": "float32",
|
31 |
+
"value_range": [],
|
32 |
+
"is_patch_data": false,
|
33 |
+
"channel_def": {
|
34 |
+
"0": "image"
|
35 |
+
}
|
36 |
+
}
|
37 |
+
},
|
38 |
+
"outputs": {
|
39 |
+
"pred": {
|
40 |
+
"type": "image",
|
41 |
+
"format": "segmentation",
|
42 |
+
"num_channels": 4,
|
43 |
+
"spatial_shape": [
|
44 |
+
256,
|
45 |
+
256
|
46 |
+
],
|
47 |
+
"dtype": "float32",
|
48 |
+
"value_range": [
|
49 |
+
0,
|
50 |
+
3
|
51 |
+
],
|
52 |
+
"is_patch_data": false,
|
53 |
+
"channel_def": {
|
54 |
+
"0": "background",
|
55 |
+
"1": "lv_pool",
|
56 |
+
"2": "myocardium",
|
57 |
+
"3": "rv_pool"
|
58 |
+
}
|
59 |
+
}
|
60 |
+
}
|
61 |
+
}
|
62 |
+
}
|
configs/train.json
ADDED
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"imports": [
|
3 |
+
"$from functools import partial",
|
4 |
+
"$import numpy as np",
|
5 |
+
"$import torch",
|
6 |
+
"$from ignite.engine import Events"
|
7 |
+
],
|
8 |
+
"device": "$torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')",
|
9 |
+
"ckpt_path": "$@bundle_root + '/models/model.pt'",
|
10 |
+
"dataset_file": "./allimages3label.npz",
|
11 |
+
"network_def": {
|
12 |
+
"_target_": "UNet",
|
13 |
+
"spatial_dims": 2,
|
14 |
+
"in_channels": 1,
|
15 |
+
"out_channels": 4,
|
16 |
+
"channels": [
|
17 |
+
16,
|
18 |
+
32,
|
19 |
+
64,
|
20 |
+
128,
|
21 |
+
256
|
22 |
+
],
|
23 |
+
"strides": [
|
24 |
+
2,
|
25 |
+
2,
|
26 |
+
2,
|
27 |
+
2
|
28 |
+
],
|
29 |
+
"num_res_units": 2
|
30 |
+
},
|
31 |
+
"network": "$@network_def.to(@device)",
|
32 |
+
"npz": {
|
33 |
+
"_target_": "NPZDictItemDataset",
|
34 |
+
"npzfile": "$@dataset_file",
|
35 |
+
"keys": {
|
36 |
+
"images": "image",
|
37 |
+
"segs": "label"
|
38 |
+
}
|
39 |
+
},
|
40 |
+
"partitions": "$monai.data.partition_dataset(np.arange(len(@npz)),(8,2), shuffle=True)",
|
41 |
+
"train_sub": "$torch.utils.data.Subset(@npz,@partitions[0])",
|
42 |
+
"eval_sub": "$torch.utils.data.Subset(@npz,@partitions[1])",
|
43 |
+
"im_shape": "$@train_sub[0]['image'].shape",
|
44 |
+
"both_keys": [
|
45 |
+
"image",
|
46 |
+
"label"
|
47 |
+
],
|
48 |
+
"rand_prob": 0.5,
|
49 |
+
"train_transforms": {
|
50 |
+
"_target_": "Compose",
|
51 |
+
"transforms": [
|
52 |
+
{
|
53 |
+
"_target_": "CastToTyped",
|
54 |
+
"keys": "@both_keys",
|
55 |
+
"dtype": "$(np.float32, np.int32)"
|
56 |
+
},
|
57 |
+
{
|
58 |
+
"_target_": "ScaleIntensityd",
|
59 |
+
"keys": "image"
|
60 |
+
},
|
61 |
+
{
|
62 |
+
"_target_": "AddChanneld",
|
63 |
+
"keys": "@both_keys"
|
64 |
+
},
|
65 |
+
{
|
66 |
+
"_target_": "RandAxisFlipd",
|
67 |
+
"keys": "@both_keys",
|
68 |
+
"prob": "@rand_prob"
|
69 |
+
},
|
70 |
+
{
|
71 |
+
"_target_": "RandRotate90d",
|
72 |
+
"keys": "@both_keys",
|
73 |
+
"prob": "@rand_prob"
|
74 |
+
},
|
75 |
+
{
|
76 |
+
"_target_": "RandSmoothDeformd",
|
77 |
+
"keys": "@both_keys",
|
78 |
+
"prob": "@rand_prob",
|
79 |
+
"spatial_size": "@im_shape",
|
80 |
+
"rand_size": [
|
81 |
+
3,
|
82 |
+
3
|
83 |
+
],
|
84 |
+
"pad": 2,
|
85 |
+
"def_range": 0.1,
|
86 |
+
"field_mode": "$monai.utils.InterpolateMode.BICUBIC",
|
87 |
+
"grid_mode": "$(monai.utils.GridSampleMode.BICUBIC, monai.utils.GridSampleMode.NEAREST)",
|
88 |
+
"align_corners": true
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"_target_": "RandAffined",
|
92 |
+
"keys": "@both_keys",
|
93 |
+
"prob": "@rand_prob",
|
94 |
+
"rotate_range": 0.5,
|
95 |
+
"translate_range": 50,
|
96 |
+
"scale_range": 0.25,
|
97 |
+
"padding_mode": "$monai.utils.GridSamplePadMode.ZEROS",
|
98 |
+
"mode": "$(monai.utils.GridSampleMode.BILINEAR,monai.utils.GridSampleMode.NEAREST)",
|
99 |
+
"as_tensor_output": false
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"_target_": "RandSmoothFieldAdjustContrastd",
|
103 |
+
"keys": "image",
|
104 |
+
"prob": "@rand_prob",
|
105 |
+
"spatial_size": "@im_shape",
|
106 |
+
"rand_size": [
|
107 |
+
8,
|
108 |
+
8
|
109 |
+
],
|
110 |
+
"gamma": [
|
111 |
+
0.25,
|
112 |
+
3
|
113 |
+
],
|
114 |
+
"mode": "$monai.utils.InterpolateMode.BICUBIC",
|
115 |
+
"align_corners": true
|
116 |
+
},
|
117 |
+
{
|
118 |
+
"_target_": "RandSmoothFieldAdjustIntensityd",
|
119 |
+
"keys": "image",
|
120 |
+
"prob": "@rand_prob",
|
121 |
+
"spatial_size": "@im_shape",
|
122 |
+
"rand_size": [
|
123 |
+
5,
|
124 |
+
5
|
125 |
+
],
|
126 |
+
"gamma": [
|
127 |
+
0.1,
|
128 |
+
1
|
129 |
+
],
|
130 |
+
"mode": "$monai.utils.InterpolateMode.BICUBIC",
|
131 |
+
"align_corners": true
|
132 |
+
},
|
133 |
+
{
|
134 |
+
"_target_": "RandGaussianNoised",
|
135 |
+
"keys": "image",
|
136 |
+
"prob": "@rand_prob",
|
137 |
+
"std": 0.05
|
138 |
+
},
|
139 |
+
{
|
140 |
+
"_target_": "ScaleIntensityd",
|
141 |
+
"keys": "image"
|
142 |
+
},
|
143 |
+
{
|
144 |
+
"_target_": "CastToTyped",
|
145 |
+
"keys": "@both_keys",
|
146 |
+
"dtype": "$(np.float32, np.int32)"
|
147 |
+
},
|
148 |
+
{
|
149 |
+
"_target_": "EnsureTyped",
|
150 |
+
"keys": "@both_keys"
|
151 |
+
}
|
152 |
+
]
|
153 |
+
},
|
154 |
+
"eval_transforms": {
|
155 |
+
"_target_": "Compose",
|
156 |
+
"transforms": [
|
157 |
+
{
|
158 |
+
"_target_": "CastToTyped",
|
159 |
+
"keys": "@both_keys",
|
160 |
+
"dtype": "$(np.float32, np.int32)"
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"_target_": "ScaleIntensityd",
|
164 |
+
"keys": "image"
|
165 |
+
},
|
166 |
+
{
|
167 |
+
"_target_": "AddChanneld",
|
168 |
+
"keys": "@both_keys"
|
169 |
+
},
|
170 |
+
{
|
171 |
+
"_target_": "EnsureTyped",
|
172 |
+
"keys": "@both_keys"
|
173 |
+
}
|
174 |
+
]
|
175 |
+
},
|
176 |
+
"train_dataset": {
|
177 |
+
"_target_": "CacheDataset",
|
178 |
+
"data": "@train_sub",
|
179 |
+
"transform": "@train_transforms"
|
180 |
+
},
|
181 |
+
"eval_dataset": {
|
182 |
+
"_target_": "CacheDataset",
|
183 |
+
"data": "@eval_sub",
|
184 |
+
"transform": "@eval_transforms"
|
185 |
+
},
|
186 |
+
"train_no_aug_dataset": {
|
187 |
+
"_target_": "CacheDataset",
|
188 |
+
"data": "@train_sub",
|
189 |
+
"transform": "@eval_transforms"
|
190 |
+
},
|
191 |
+
"num_iters": 400,
|
192 |
+
"batch_size": 200,
|
193 |
+
"num_epochs": 50,
|
194 |
+
"num_substeps": 5,
|
195 |
+
"sampler": {
|
196 |
+
"_target_": "torch.utils.data.WeightedRandomSampler",
|
197 |
+
"weights": "$torch.ones(len(@train_dataset))",
|
198 |
+
"replacement": true,
|
199 |
+
"num_samples": "$@num_iters*@batch_size"
|
200 |
+
},
|
201 |
+
"train_dataloader": {
|
202 |
+
"_target_": "ThreadDataLoader",
|
203 |
+
"dataset": "@train_dataset",
|
204 |
+
"batch_size": "@batch_size",
|
205 |
+
"repeats": "@num_substeps",
|
206 |
+
"num_workers": 8,
|
207 |
+
"sampler": "@sampler"
|
208 |
+
},
|
209 |
+
"eval_dataloader": {
|
210 |
+
"_target_": "DataLoader",
|
211 |
+
"dataset": "@eval_dataset",
|
212 |
+
"batch_size": "@batch_size",
|
213 |
+
"num_workers": 4
|
214 |
+
},
|
215 |
+
"lossfn": {
|
216 |
+
"_target_": "DiceLoss",
|
217 |
+
"include_background": false,
|
218 |
+
"to_onehot_y": true,
|
219 |
+
"softmax": true
|
220 |
+
},
|
221 |
+
"optimizer": {
|
222 |
+
"_target_": "torch.optim.Adam",
|
223 |
+
"params": "$@network.parameters()",
|
224 |
+
"lr": 0.001
|
225 |
+
},
|
226 |
+
"post_transform": {
|
227 |
+
"_target_": "Compose",
|
228 |
+
"transforms": [
|
229 |
+
{
|
230 |
+
"_target_": "Activationsd",
|
231 |
+
"keys": "pred",
|
232 |
+
"softmax": true
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"_target_": "AsDiscreted",
|
236 |
+
"keys": [
|
237 |
+
"pred",
|
238 |
+
"label"
|
239 |
+
],
|
240 |
+
"argmax": [
|
241 |
+
true,
|
242 |
+
false
|
243 |
+
],
|
244 |
+
"to_onehot": 4
|
245 |
+
}
|
246 |
+
]
|
247 |
+
},
|
248 |
+
"evaluator": {
|
249 |
+
"_target_": "SupervisedEvaluator",
|
250 |
+
"device": "@device",
|
251 |
+
"val_data_loader": "@eval_dataloader",
|
252 |
+
"network": "@network",
|
253 |
+
"postprocessing": "@post_transform",
|
254 |
+
"key_val_metric": {
|
255 |
+
"val_mean_dice": {
|
256 |
+
"_target_": "MeanDice",
|
257 |
+
"include_background": false,
|
258 |
+
"output_transform": "$monai.handlers.from_engine(['pred', 'label'])"
|
259 |
+
}
|
260 |
+
},
|
261 |
+
"val_handlers": [
|
262 |
+
{
|
263 |
+
"_target_": "StatsHandler",
|
264 |
+
"output_transform": "$lambda x: None"
|
265 |
+
}
|
266 |
+
]
|
267 |
+
},
|
268 |
+
"handlers": [
|
269 |
+
{
|
270 |
+
"_target_": "ValidationHandler",
|
271 |
+
"validator": "@evaluator",
|
272 |
+
"epoch_level": true,
|
273 |
+
"interval": 1
|
274 |
+
},
|
275 |
+
{
|
276 |
+
"_target_": "CheckpointSaver",
|
277 |
+
"save_dir": "$@bundle_root + '/models'",
|
278 |
+
"save_dict": {
|
279 |
+
"model": "@network"
|
280 |
+
},
|
281 |
+
"save_interval": 1,
|
282 |
+
"save_final": true,
|
283 |
+
"epoch_level": true
|
284 |
+
},
|
285 |
+
{
|
286 |
+
"_target_": "StatsHandler",
|
287 |
+
"tag_name": "train_loss",
|
288 |
+
"output_transform": "$monai.handlers.from_engine(['loss'], first=True)"
|
289 |
+
}
|
290 |
+
],
|
291 |
+
"trainer": {
|
292 |
+
"_target_": "SupervisedTrainer",
|
293 |
+
"max_epochs": "@num_epochs",
|
294 |
+
"device": "@device",
|
295 |
+
"train_data_loader": "@train_dataloader",
|
296 |
+
"network": "@network",
|
297 |
+
"loss_function": "@lossfn",
|
298 |
+
"optimizer": "@optimizer",
|
299 |
+
"postprocessing": "@post_transform",
|
300 |
+
"key_train_metric": null,
|
301 |
+
"train_handlers": "@handlers"
|
302 |
+
},
|
303 |
+
"training": [
|
304 |
+
"$@trainer.run()"
|
305 |
+
]
|
306 |
+
}
|
docs/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# 3 Label Ventricular Segmentation
|
3 |
+
|
4 |
+
This network segments cardiac ventricle in 2D short axis MR images. The left ventricular pool is class 1, left ventricular myocardium class 2, and right ventricular pool class 3. Full cycle segmentation with this network is possible although much of the training data is composed of segmented end-diastole images. The input to the network is single 2D images thus segmenting whole time-dependent volumes consists of multiple inference operations.
|
5 |
+
|
6 |
+
The network and training scheme are essentially identical to that described in:
|
7 |
+
|
8 |
+
`Kerfoot E., Clough J., Oksuz I., Lee J., King A.P., Schnabel J.A. (2019) Left-Ventricle Quantification Using Residual U-Net. In: Pop M. et al. (eds) Statistical Atlases and Computational Models of the Heart. Atrial Segmentation and LV Quantification Challenges. STACOM 2018. Lecture Notes in Computer Science, vol 11395. Springer, Cham. https://doi.org/10.1007/978-3-030-12029-0_40`
|
9 |
+
|
10 |
+
## Data
|
11 |
+
|
12 |
+
The dataset used to train this network unfortunately cannot be made public as it contains unreleased image data from King's College London. Existing public datasets such as the[Sunnybrook Cardiac Dataset](http://www.cardiacatlas.org/studies/sunnybrook-cardiac-data/) and [ACDC Challenge](https://www.creatis.insa-lyon.fr/Challenge/acdc/) set can be used to train a similar network.
|
13 |
+
|
14 |
+
The `train.json` configuration assumes all data is stored in a single npz file with keys "images" and "segs" containing respectively the raw image data and their accompanying segmentations. The given network was training with stored volumes with shapes `(9095, 256, 256)` thus other data of differing spatial dimensions must be cropped to `(256, 256)` or zero-padded to that size. For the training data this was done as a preprocessing step but the original pixel values are otherwise unchanged from their original forms.
|
15 |
+
|
16 |
+
## Training
|
17 |
+
|
18 |
+
The network is trained with this data in conjunction with a series of augmentations for regularisation and robustness. Many of the original images are smaller than the expected size of `(256, 256)` and so were zero-padded, the network can thus be expected to be robust against large amounts of empty space in the inputs. Rotation and zooming is also applied to force the network to learn different sizes and orientations of the heart in the field of view.
|
19 |
+
|
20 |
+
Free-form deformation is applied to vary the shape of the heart and its surrounding tissues which mimics to a degree deformation like what would be observed through the cardiac cycle. This of course does not replicate the heart moving through plane during the cycle or represent other observed changes but does provide enough variation that full-cycle segmentation is generally acceptable.
|
21 |
+
|
22 |
+
Smooth fields are used to vary contrast and intensity in localised regions to simulate some of the variation in image quality caused by acquisition artefacts. Guassian noise is also added to simulate poor quality acquisition. These together force the network to learn to deal with a wider variation of image quality and partially to account for the difference between scanner vendors.
|
23 |
+
|
24 |
+
Training is invoked with the following command line:
|
25 |
+
|
26 |
+
```sh
|
27 |
+
python -m monai.bundle run training --meta_file configs/metadata.json --config_file configs/train.json --logging_file configs/logging.conf --bundle_root .
|
28 |
+
```
|
29 |
+
|
30 |
+
The dataset file is assumed to be `allimages3label.npz` but can be changed by setting the `dataset_file` value to your own file.
|
31 |
+
|
32 |
+
## Inference
|
33 |
+
|
34 |
+
An example notebook [visualise.ipynb](./visualise.ipynb) demonstrates using the network directly with input images. Inference of 3D volumes only can be accomplished with the `inference.json` configuration:
|
35 |
+
|
36 |
+
```sh
|
37 |
+
python -m monai.bundle run evaluating --meta_file configs/metadata.json --config_file configs/inference.json --logging_file configs/logging.conf --dataset_dir dataset --output_dir ./output/ --bundle_root .
|
38 |
+
```
|
docs/SC-N-2-3-0.npy
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:03c7fae03f14710b9b1e6709d1d4d101b8569a94f3781376ac92f659483d6f16
|
3 |
+
size 524416
|
docs/license.txt
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) [year] [fullname]
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
docs/visualise.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
models/model.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:464ca796028831f6c9e2b1cdaebe9af002fc1d7f494f7a89a63f2079e38837a1
|
3 |
+
size 6533160
|
models/model.ts
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:27d5532401fa6c1883872fa21635adbb7615981e7f385d0c58dd75b355e340b3
|
3 |
+
size 6631889
|