ChrisXzZ commited on
Commit
9dbe2e0
Β·
verified Β·
1 Parent(s): 2f5b020

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .gitattributes +41 -0
  2. README.md +134 -0
  3. nnUNet/.github/workflows/codespell.yml +22 -0
  4. nnUNet/.github/workflows/run_tests_nnunet.yml +49 -0
  5. nnUNet/.gitignore +119 -0
  6. nnUNet/CONTRIBUTING.md +50 -0
  7. nnUNet/LICENSE +201 -0
  8. nnUNet/documentation/__init__.py +0 -0
  9. nnUNet/documentation/assets/HI_Logo.png +0 -0
  10. nnUNet/documentation/assets/amos2022_sparseseg10.png +3 -0
  11. nnUNet/documentation/assets/amos2022_sparseseg10_2d.png +3 -0
  12. nnUNet/documentation/assets/dkfz_logo.png +0 -0
  13. nnUNet/documentation/assets/nnU-Net_overview.png +3 -0
  14. nnUNet/documentation/assets/nnUNetMagician.png +3 -0
  15. nnUNet/documentation/assets/regions_vs_labels.png +3 -0
  16. nnUNet/documentation/assets/scribble_example.png +3 -0
  17. nnUNet/documentation/assets/sparse_annotation_amos.png +0 -0
  18. nnUNet/documentation/benchmarking.md +115 -0
  19. nnUNet/documentation/changelog.md +51 -0
  20. nnUNet/documentation/competitions/AortaSeg24.md +46 -0
  21. nnUNet/documentation/competitions/AutoPETII.md +129 -0
  22. nnUNet/documentation/competitions/FLARE24/Task_1/__init__.py +0 -0
  23. nnUNet/documentation/competitions/FLARE24/Task_1/inference_flare_task1.py +191 -0
  24. nnUNet/documentation/competitions/FLARE24/Task_1/readme.md +78 -0
  25. nnUNet/documentation/competitions/FLARE24/Task_2/__init__.py +0 -0
  26. nnUNet/documentation/competitions/FLARE24/Task_2/inference_flare_task2.py +449 -0
  27. nnUNet/documentation/competitions/FLARE24/Task_2/readme.md +102 -0
  28. nnUNet/documentation/competitions/FLARE24/__init__.py +0 -0
  29. nnUNet/documentation/competitions/Toothfairy2/__init__.py +0 -0
  30. nnUNet/documentation/competitions/Toothfairy2/inference_script_semseg_only_customInf2.py +343 -0
  31. nnUNet/documentation/competitions/Toothfairy2/readme.md +208 -0
  32. nnUNet/documentation/competitions/__init__.py +0 -0
  33. nnUNet/documentation/convert_msd_dataset.md +3 -0
  34. nnUNet/documentation/dataset_format.md +254 -0
  35. nnUNet/documentation/dataset_format_inference.md +39 -0
  36. nnUNet/documentation/explanation_logging.md +88 -0
  37. nnUNet/documentation/explanation_normalization.md +45 -0
  38. nnUNet/documentation/explanation_plans_files.md +185 -0
  39. nnUNet/documentation/extending_nnunet.md +37 -0
  40. nnUNet/documentation/how_to_use_nnunet.md +313 -0
  41. nnUNet/documentation/ignore_label.md +104 -0
  42. nnUNet/documentation/installation_instructions.md +87 -0
  43. nnUNet/documentation/manual_data_splits.md +46 -0
  44. nnUNet/documentation/pretraining_and_finetuning.md +82 -0
  45. nnUNet/documentation/region_based_training.md +79 -0
  46. nnUNet/documentation/resenc_presets.md +126 -0
  47. nnUNet/documentation/run_inference_with_pretrained_models.md +7 -0
  48. nnUNet/documentation/set_environment_variables.md +78 -0
  49. nnUNet/documentation/setting_up_paths.md +39 -0
  50. nnUNet/documentation/tldr_migration_guide_from_v1.md +20 -0
.gitattributes ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz 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
+ nnUNet/documentation/assets/amos2022_sparseseg10.png filter=lfs diff=lfs merge=lfs -text
37
+ nnUNet/documentation/assets/amos2022_sparseseg10_2d.png filter=lfs diff=lfs merge=lfs -text
38
+ nnUNet/documentation/assets/nnU-Net_overview.png filter=lfs diff=lfs merge=lfs -text
39
+ nnUNet/documentation/assets/nnUNetMagician.png filter=lfs diff=lfs merge=lfs -text
40
+ nnUNet/documentation/assets/regions_vs_labels.png filter=lfs diff=lfs merge=lfs -text
41
+ nnUNet/documentation/assets/scribble_example.png filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-4.0
3
+ ---
4
+ This repository provides the nnUNet v2 checkpoint for multi-organ lesion segmentation from abdominal CT images.
5
+
6
+ The model was trained on a large-scale multi-center CT dataset with expert-annotated lesion masks across multiple organs (liver, pancreas, kidney, colon).
7
+
8
+ First of all, you need a Linux OS server and basic terminal usage.
9
+
10
+ ## 0. Before you start
11
+ You need:
12
+ CT files in .nii.gz format
13
+
14
+ Each CT is a 3D volume (not single images)
15
+
16
+ ## 1. Installation (Environment and nnUNet packages)
17
+
18
+ ```bash
19
+ git clone https://huggingface.co/ChrisXzZ/LesionSegmenter
20
+ conda create -n lesionseg python=3.10 -y
21
+ conda activate lesionseg
22
+ git clone https://github.com/MIC-DKFZ/nnUNet.git
23
+ cd nnUNet
24
+ pip install -e .
25
+ pip uninstall -y torch torchvision torchaudio
26
+ pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
27
+ ```
28
+
29
+ ## 2. Prepare your CT data
30
+ Create the following folder structure:
31
+ ```bash
32
+ mkdir -p nnUNet_eval/Dataset1356/imagesTs
33
+ ```
34
+
35
+ Put your CT files into:
36
+ ```bash
37
+ nnUNet_eval/Dataset1356/imagesTs/
38
+ ```
39
+
40
+ Each file must be renamed like this:
41
+ ```bash
42
+ nnUNet_eval/
43
+ └── Dataset1356/
44
+ └── imagesTs/
45
+ β”œβ”€β”€ ct001_0000.nii.gz
46
+ β”œβ”€β”€ ct002_0000.nii.gz
47
+ ```
48
+
49
+ Key rules:
50
+ Must end with _0000.nii.gz
51
+ Each case = one file
52
+
53
+ #### 3. Put the Model (Checkpoint)
54
+ Download the model checkpoint (provided by us), then place it like this:
55
+ ```bash
56
+ nnUNet_results/
57
+ └── Dataset1356_RADV3S/nnUNetTrainer__nnUNetResEncUNetLPlans__3d_fullres/fold_all/checkpoint_final.pth
58
+ ```
59
+ Or
60
+
61
+ ```bash
62
+ nnUNet_results/
63
+ └── Dataset1357_RADN1/nnUNetTrainer__nnUNetResEncUNetLPlans__3d_fullres/fold_all/checkpoint_final.pth
64
+ ```
65
+
66
+
67
+ #### 4. Run inference
68
+ First set paths:
69
+ ```bash
70
+ export nnUNet_N_proc_DA=36
71
+ export nnUNet_results="./nnUNet_results"
72
+ export nnUNet_predictions="./nnUNet_predictions"
73
+ export nnUNet_eval="./nnUNet_eval"
74
+ ```
75
+
76
+ Then run:
77
+
78
+ ```bash
79
+ GPU_ID=0
80
+ DATASET=1356
81
+
82
+ TRAINER=nnUNetTrainer
83
+
84
+ CUDA_VISIBLE_DEVICES=$GPU_ID nnUNetv2_predict -d $DATASET -i $nnUNet_eval/Dataset1356/imagesTs/ -o $nnUNet_predictions/ -tr $TRAINER -d $DATASET -c 3d_fullres -f all -p nnUNetResEncUNetLPlans --continue_prediction
85
+ ```
86
+ Or
87
+
88
+ ```bash
89
+ export nnUNet_N_proc_DA=36
90
+ export nnUNet_results="./nnUNet_results"
91
+ export nnUNet_predictions="./nnUNet_predictions"
92
+ export nnUNet_eval="./nnUNet_eval"
93
+
94
+ GPU_ID=0
95
+ DATASET=1357
96
+
97
+ TRAINER=nnUNetTrainer
98
+
99
+ CUDA_VISIBLE_DEVICES=$GPU_ID nnUNetv2_predict -d $DATASET -i $nnUNet_eval/Dataset1357/imagesTs// -o $nnUNet_predictions/ -tr $TRAINER -d $DATASET -c 3d_fullres -f all -p nnUNetResEncUNetLPlans --continue_prediction
100
+ ```
101
+
102
+ ### 5. Run post-processing
103
+ This step removes false positives and improves quality.
104
+
105
+ For DATASET=1356 checkpoint, run:
106
+ ```bash
107
+ python p-processing.py \
108
+ --input_dir ./nnUNet_predictions \
109
+ --output_dir ./nnUNet_predictions \
110
+ --rules "39:5,40:6|7|8,41:3|4,42:16" \
111
+ --keep_mode any \
112
+ --target_labels "39,40,41,42" \
113
+ --min_size 25 \
114
+ --dilation_iters 1 \
115
+ --connectivity 26 \
116
+ --num_workers 16
117
+ ```
118
+
119
+ For DATASET=1357 checkpoint, run:
120
+ ```bash
121
+ python p-processing.py \
122
+ --input_dir ./nnUNet_predictions \
123
+ --output_dir ./nnUNet_predictions \
124
+ --rules "116:5|6|7|8|9|10|11|12,117:13|14|15,118:3|4,119:23" \
125
+ --keep_mode any \
126
+ --target_labels "116,117,118,119" \
127
+ --min_size 25 \
128
+ --dilation_iters 1 \
129
+ --connectivity 26 \
130
+ --num_workers 16
131
+ ```
132
+
133
+
134
+
nnUNet/.github/workflows/codespell.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Codespell
3
+
4
+ on:
5
+ push:
6
+ branches: [master]
7
+ pull_request:
8
+ branches: [master]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ codespell:
15
+ name: Check for spelling errors
16
+ runs-on: ubuntu-latest
17
+
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v3
21
+ - name: Codespell
22
+ uses: codespell-project/actions-codespell@v2
nnUNet/.github/workflows/run_tests_nnunet.yml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Run nnunet tests on all OS
2
+ on:
3
+ push:
4
+ paths-ignore:
5
+ - '**.md'
6
+ jobs:
7
+
8
+ run-tests:
9
+ strategy:
10
+ matrix:
11
+ # os: [ubuntu-latest, windows-latest, macos-latest] # fails on windows until https://github.com/MIC-DKFZ/nnUNet/issues/2396 is resolved
12
+ os: [ubuntu-latest, macos-latest]
13
+ python-version: ["3.10"]
14
+ runs-on: ${{ matrix.os }}
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Download and extract weights and download example file
25
+ # use python instead of curl to avoid the need to install curl (more difficult for macos)
26
+ run: |
27
+ mkdir -p $HOME/github_actions_nnunet/results
28
+ python -c "import urllib.request; urllib.request.urlretrieve('https://github.com/wasserth/TotalSegmentator/releases/download/v2.0.0-weights/Dataset300_body_6mm_1559subj.zip', '$HOME/github_actions_nnunet/results/tmp_download_file.zip')"
29
+ unzip -o $HOME/github_actions_nnunet/results/tmp_download_file.zip -d $HOME/github_actions_nnunet/results
30
+ rm $HOME/github_actions_nnunet/results/tmp_download_file.zip
31
+
32
+ - name: Install dependencies on Ubuntu
33
+ if: runner.os == 'Linux'
34
+ run: |
35
+ python -m pip install --upgrade pip
36
+ pip install pytest Cython
37
+ pip install torch==2.4.0 -f https://download.pytorch.org/whl/cpu
38
+ pip install .
39
+
40
+ - name: Install dependencies on Windows / MacOS
41
+ if: runner.os == 'Windows' || runner.os == 'macOS'
42
+ run: |
43
+ python -m pip install --upgrade pip
44
+ pip install pytest Cython
45
+ pip install torch==2.4.0
46
+ pip install .
47
+
48
+ - name: Run test script
49
+ run: python nnunetv2/tests/integration_tests/run_nnunet_inference.py
nnUNet/.gitignore ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ env/
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ # Usually these files are written by a python script from a template
29
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .coverage
41
+ .coverage.*
42
+ .cache
43
+ nosetests.xml
44
+ coverage.xml
45
+ *,cover
46
+ .hypothesis/
47
+
48
+ # Translations
49
+ *.mo
50
+ *.pot
51
+
52
+ # Django stuff:
53
+ *.log
54
+ local_settings.py
55
+
56
+ # Flask stuff:
57
+ instance/
58
+ .webassets-cache
59
+
60
+ # Scrapy stuff:
61
+ .scrapy
62
+
63
+ # Sphinx documentation
64
+ docs/_build/
65
+
66
+ # PyBuilder
67
+ target/
68
+
69
+ # IPython Notebook
70
+ .ipynb_checkpoints
71
+
72
+ # pyenv
73
+ .python-version
74
+
75
+ # celery beat schedule file
76
+ celerybeat-schedule
77
+
78
+ # dotenv
79
+ .env
80
+
81
+ # virtualenv
82
+ venv/
83
+ ENV/
84
+
85
+ # Spyder project settings
86
+ .spyderproject
87
+
88
+ # Rope project settings
89
+ .ropeproject
90
+
91
+ *.memmap
92
+ *.png
93
+ *.zip
94
+ *.npz
95
+ *.npy
96
+ *.jpg
97
+ *.jpeg
98
+ .idea
99
+ *.txt
100
+ .idea/*
101
+ *.png
102
+ # *.nii.gz # nifti files needed for example_data for github actions tests
103
+ *.nii
104
+ *.tif
105
+ *.bmp
106
+ *.pkl
107
+ *.xml
108
+ *.pkl
109
+ *.pdf
110
+ *.png
111
+ *.jpg
112
+ *.jpeg
113
+
114
+ *.model
115
+
116
+ !documentation/assets/scribble_example.png
117
+
118
+ CLAUDE.md
119
+ AGENTS.md
nnUNet/CONTRIBUTING.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to nnU-Net
2
+
3
+ Thank you for your interest in contributing to nnU-Net.
4
+
5
+ nnU-Net is developed and maintained by researchers at DKFZ. There is no dedicated funding or staff for maintaining the
6
+ repository, and development happens alongside research and teaching responsibilities. Our bandwidth for reviewing
7
+ external contributions is therefore limited, and review times may be long.
8
+
9
+ ## General principles
10
+
11
+ nnU-Net is intentionally designed to be focused, stable, and generally applicable across datasets and use cases.
12
+ Contributions should respect this philosophy and should not introduce unnecessary complexity or specialization.
13
+
14
+ New functionality must either be generally valid across datasets and setups or convincingly benefit a large enough
15
+ portion of the user base. We aim to avoid bloating the framework or increasing its complexity further.
16
+
17
+ ## How to contribute
18
+
19
+ For larger features and refactors, please open a GitHub issue to discuss the idea before starting work. Tag
20
+ @FabianIsensee so that the discussion doesn't get missed.
21
+
22
+ To submit a contribution, fork the repository, make your changes on a branch, and open a pull request.
23
+
24
+ ## Bug reports and bug fixes
25
+
26
+ Bug reports must include a minimal reproducible example. Without a repro, it is usually impossible for us to
27
+ investigate issues.
28
+
29
+ Pull requests fixing bugs should also include a clear reproduction of the issue and an explanation of how the fix
30
+ resolves it.
31
+
32
+ ## Performance improvements
33
+
34
+ If a pull request claims performance improvements, it must include benchmarks demonstrating the effect. The benchmark
35
+ setup must be described clearly enough for us to reproduce the results independently. We may run additional
36
+ tests ourselves before merging.
37
+
38
+ ## Contributions that are unlikely to be merged
39
+
40
+ To keep the framework maintainable and the workload manageable on our end, we deprioritize:
41
+
42
+ - dataset-specific code
43
+ - features that only apply to niche setups
44
+ - narrow custom architectures or training pipelines
45
+ - large refactorings without prior discussion
46
+ - small PRs fixing minor typos or formatting issues
47
+
48
+ ## Final note
49
+
50
+ We appreciate the effort people invest in improving nnU-Net!
nnUNet/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [2019] [Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
nnUNet/documentation/__init__.py ADDED
File without changes
nnUNet/documentation/assets/HI_Logo.png ADDED
nnUNet/documentation/assets/amos2022_sparseseg10.png ADDED

Git LFS Details

  • SHA256: 56abdc099d44a0751f7baeb4840c879d16387f8c29fa6b57f07247e30aa2c727
  • Pointer size: 131 Bytes
  • Size of remote file: 166 kB
nnUNet/documentation/assets/amos2022_sparseseg10_2d.png ADDED

Git LFS Details

  • SHA256: 38c072e9a846fd95e370265fb76545edf88a88ae0829cb2b51b1b6e543f49652
  • Pointer size: 131 Bytes
  • Size of remote file: 168 kB
nnUNet/documentation/assets/dkfz_logo.png ADDED
nnUNet/documentation/assets/nnU-Net_overview.png ADDED

Git LFS Details

  • SHA256: 507ab7c0ad019dc36c2679ee8138085f9a2cec30b548693bf060eac27284f2e9
  • Pointer size: 131 Bytes
  • Size of remote file: 923 kB
nnUNet/documentation/assets/nnUNetMagician.png ADDED

Git LFS Details

  • SHA256: 59c034a41966f5f3738abb6ca076c9824cfd0c5c339778c8b5b1547045c7f039
  • Pointer size: 131 Bytes
  • Size of remote file: 164 kB
nnUNet/documentation/assets/regions_vs_labels.png ADDED

Git LFS Details

  • SHA256: 2994ba2df4df20fc25065b5011f83fa6701e69d5675d0196585c1e66034ec5ab
  • Pointer size: 131 Bytes
  • Size of remote file: 464 kB
nnUNet/documentation/assets/scribble_example.png ADDED

Git LFS Details

  • SHA256: 9b75d9db30953164f58395d4962d8be003e431627c5293ebdb65202072c230d5
  • Pointer size: 132 Bytes
  • Size of remote file: 2.28 MB
nnUNet/documentation/assets/sparse_annotation_amos.png ADDED
nnUNet/documentation/benchmarking.md ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # nnU-Netv2 benchmarks
2
+
3
+ Does your system run like it should? Is your epoch time longer than expected? What epoch times should you expect?
4
+
5
+ Look no further for we have the solution here!
6
+
7
+ ## What does the nnU-netv2 benchmark do?
8
+
9
+ nnU-Net's benchmark trains models for 5 epochs. At the end, the fastest epoch will
10
+ be noted down, along with the GPU name, torch version and cudnn version. You can find the benchmark output in the
11
+ corresponding nnUNet_results subfolder (see example below). Don't worry, we also provide scripts to collect your
12
+ results. Or you just start a benchmark and look at the console output. Everything is possible. Nothing is forbidden.
13
+
14
+ The benchmark implementation revolves around two trainers:
15
+ - `nnUNetTrainerBenchmark_5epochs` runs a regular training for 5 epochs. When completed, writes a .json file with the fastest
16
+ epoch time as well as the GPU used and the torch and cudnn versions. Useful for speed testing the entire pipeline
17
+ (data loading, augmentation, GPU training)
18
+ - `nnUNetTrainerBenchmark_5epochs_noDataLoading` is the same, but it doesn't do any data loading or augmentation. It
19
+ just presents dummy arrays to the GPU. Useful for checking pure GPU speed.
20
+
21
+ ## How to run the nnU-Netv2 benchmark?
22
+ It's quite simple, actually. It looks just like a regular nnU-Net training.
23
+
24
+ We provide reference numbers for some of the Medical Segmentation Decathlon datasets because they are easily
25
+ accessible: [download here](https://drive.google.com/drive/folders/1HqEgzS8BV2c7xYNrZdEAnrHk7osJJ--2). If it needs to be
26
+ quick and dirty, focus on Tasks 2 and 4. Download and extract the data and convert them to the nnU-Net format with
27
+ `nnUNetv2_convert_MSD_dataset`.
28
+ Run `nnUNetv2_plan_and_preprocess` for them.
29
+
30
+ Then, for each dataset, run the following commands (only one per GPU! Or one after the other):
31
+
32
+ ```bash
33
+ nnUNetv2_train DATSET_ID 2d 0 -tr nnUNetTrainerBenchmark_5epochs
34
+ nnUNetv2_train DATSET_ID 3d_fullres 0 -tr nnUNetTrainerBenchmark_5epochs
35
+ nnUNetv2_train DATSET_ID 2d 0 -tr nnUNetTrainerBenchmark_5epochs_noDataLoading
36
+ nnUNetv2_train DATSET_ID 3d_fullres 0 -tr nnUNetTrainerBenchmark_5epochs_noDataLoading
37
+ ```
38
+
39
+ If you want to inspect the outcome manually, check (for example!) your
40
+ `nnUNet_results/DATASET_NAME/nnUNetTrainerBenchmark_5epochs__nnUNetPlans__3d_fullres/fold_0/` folder for the `benchmark_result.json` file.
41
+
42
+ Note that there can be multiple entries in this file if the benchmark was run on different GPU types, torch versions or cudnn versions!
43
+
44
+ If you want to summarize your results like we did in our [results](#results), check the
45
+ [summary script](../nnunetv2/batch_running/benchmarking/summarize_benchmark_results.py). Here you need to change the
46
+ torch version, cudnn version and dataset you want to summarize, then execute the script. You can find the exact
47
+ values you need to put there in one of your `benchmark_result.json` files.
48
+
49
+ ## Results
50
+ We have tested a variety of GPUs and summarized the results in a
51
+ [spreadsheet](https://docs.google.com/spreadsheets/d/12Cvt_gr8XU2qWaE0XJk5jJlxMEESPxyqW0CWbQhTNNY/edit?usp=sharing).
52
+ Note that you can select the torch and cudnn versions at the bottom! There may be comments in this spreadsheet. Read them!
53
+
54
+ ## Result interpretation
55
+
56
+ Results are shown as epoch time in seconds. Lower is better (duh). Epoch times can fluctuate between runs, so as
57
+ long as you are within like 5-10% of the numbers we report, everything should be dandy.
58
+
59
+ If not, here is how you can try to find the culprit!
60
+
61
+ The first thing to do is to compare the performance between the `nnUNetTrainerBenchmark_5epochs_noDataLoading` and
62
+ `nnUNetTrainerBenchmark_5epochs` trainers. If the difference is about the same as we report in our spreadsheet, but
63
+ both your numbers are worse, the problem is with your GPU:
64
+
65
+ - Are you certain you compare the correct GPU? (duh)
66
+ - If yes, then you might want to install PyTorch in a different way. Never `pip install torch`! Go to the
67
+ [PyTorch installation](https://pytorch.org/get-started/locally/) page, select the most recent cuda version your
68
+ system supports and only then copy and execute the correct command! Either pip or conda should work
69
+ - If the problem is still not fixed, we recommend you try
70
+ [compiling pytorch from source](https://github.com/pytorch/pytorch#from-source). It's more difficult but that's
71
+ how we roll here at the DKFZ (at least the cool kids here).
72
+ - Another thing to consider is to try exactly the same torch + cudnn version as we did in our spreadsheet.
73
+ Sometimes newer versions can actually degrade performance and there might be bugs from time to time. Older versions
74
+ are also often a lot slower!
75
+ - Finally, some very basic things that could impact your GPU performance:
76
+ - Is the GPU cooled adequately? Check the temperature with `nvidia-smi`. Hot GPUs throttle performance in order to not self-destruct
77
+ - Is your OS using the GPU for displaying your desktop at the same time? If so then you can expect a performance
78
+ penalty (I dunno like 10% !?). That's expected and OK.
79
+ - Are other users using the GPU as well?
80
+
81
+
82
+ If you see a large performance difference between `nnUNetTrainerBenchmark_5epochs_noDataLoading` (fast) and
83
+ `nnUNetTrainerBenchmark_5epochs` (slow) then the problem might be related to data loading and augmentation. As a
84
+ reminder, nnU-net does not use pre-augmented images (offline augmentation) but instead generates augmented training
85
+ samples on the fly during training (no, you cannot switch it to offline). This requires that your system can do partial
86
+ reads of the image files fast enough (SSD storage required!) and that your CPU is powerful enough to run the augmentations.
87
+
88
+ Check the following:
89
+
90
+ - [CPU bottleneck] How many CPU threads are running during the training? nnU-Net uses 12 processes for data augmentation by default.
91
+ If you see those 12 running constantly during training, consider increasing the number of processes used for data
92
+ augmentation (provided there is headroom on your CPU!). Increase the number until you see less active workers than
93
+ you configured (or just set the number to 32 and forget about it). You can do so by setting the `nnUNet_n_proc_DA`
94
+ environment variable (Linux: `export nnUNet_n_proc_DA=24`). Read [here](set_environment_variables.md) on how to do this.
95
+ If your CPU does not support more processes (setting more processes than your CPU has threads makes
96
+ no sense!) you are out of luck and in desperate need of a system upgrade!
97
+ - [I/O bottleneck] If you don't see 12 (or nnUNet_n_proc_DA if you set it) processes running but your training times
98
+ are still slow then open up `top` (sorry, Windows users. I don't know how to do this on Windows) and look at the value
99
+ left of 'wa' in the row that begins
100
+ with '%Cpu (s)'. If this is >1.0 (arbitrarily set threshold here, essentially look for unusually high 'wa'. In a
101
+ healthy training 'wa' will be almost 0) then your storage cannot keep up with data loading. Make sure to set
102
+ nnUNet_preprocessed to a folder that is located on an SSD. nvme is preferred over SATA. PCIe3 is enough. 3000MB/s
103
+ sequential read recommended.
104
+ - [funky stuff] Sometimes there is funky stuff going on, especially when batch sizes are large, files are small and
105
+ patch sizes are small as well. As part of the data loading process, nnU-Net needs to open and close a file for each
106
+ training sample. Now imagine a dataset like Dataset004_Hippocampus where for the 2d config we have a batch size of
107
+ 366 and we run 250 iterations in <10s on an A100. That's a lotta files per second (366 * 250 / 10 = 9150 files per second).
108
+ Oof. If the files are on some network drive (even if it's nvme) then (probably) good night. The good news: nnU-Net
109
+ has got you covered: add `export nnUNet_keep_files_open=True` to your .bashrc and the problem goes away. The neat
110
+ part: it causes new problems if you are not allowed to have enough open files. You may have to increase the number
111
+ of allowed open files. `ulimit -n` gives your current limit (Linux only). It should not be something like 1024.
112
+ Increasing that to 65535 works well for me. See here for how to change these limits:
113
+ [Link](https://kupczynski.info/posts/ubuntu-18-10-ulimits/)
114
+ (works for Ubuntu 18, google for your OS!).
115
+
nnUNet/documentation/changelog.md ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # What is different in v2?
2
+
3
+ - We now support **hierarchical labels** (named regions in nnU-Net). For example, instead of training BraTS with the
4
+ 'edema', 'necrosis' and 'enhancing tumor' labels you can directly train it on the target areas 'whole tumor',
5
+ 'tumor core' and 'enhancing tumor'. See [here](region_based_training.md) for a detailed description + also have a look at the
6
+ [BraTS 2021 conversion script](../nnunetv2/dataset_conversion/Dataset137_BraTS21.py).
7
+ - Cross-platform support. Cuda, mps (Apple M1/M2) and of course CPU support! Simply select the device with
8
+ `-device` in `nnUNetv2_train` and `nnUNetv2_predict`.
9
+ - Unified trainer class: nnUNetTrainer. No messing around with cascaded trainer, DDP trainer, region-based trainer,
10
+ ignore trainer etc. All default functionality is in there!
11
+ - Supports more input/output data formats through ImageIO classes.
12
+ - I/O formats can be extended by implementing new Adapters based on `BaseReaderWriter`.
13
+ - The nnUNet_raw_cropped folder no longer exists -> saves disk space at no performance penalty. magic! (no jk the
14
+ saving of cropped npz files was really slow, so it's actually faster to crop on the fly).
15
+ - Preprocessed data and segmentation are stored in different files when unpacked. Seg is stored as int8 and thus
16
+ takes 1/4 of the disk space per pixel (and I/O throughput) as in v1.
17
+ - Native support for multi-GPU (DDP) TRAINING.
18
+ Multi-GPU INFERENCE should still be run with `CUDA_VISIBLE_DEVICES=X nnUNetv2_predict [...] -num_parts Y -part_id X`.
19
+ There is no cross-GPU communication in inference, so it doesn't make sense to add additional complexity with DDP.
20
+ - All nnU-Net functionality is now also accessible via API. Check the corresponding entry point in `setup.py` to see
21
+ what functions you need to call.
22
+ - Dataset fingerprint is now explicitly created and saved in a json file (see nnUNet_preprocessed).
23
+
24
+ - Complete overhaul of plans files (read also [this](explanation_plans_files.md):
25
+ - Plans are now .json and can be opened and read more easily
26
+ - Configurations are explicitly named ("3d_fullres" , ...)
27
+ - Configurations can inherit from each other to make manual experimentation easier
28
+ - A ton of additional functionality is now included in and can be changed through the plans, for example normalization strategy, resampling etc.
29
+ - Stages of the cascade are now explicitly listed in the plans. 3d_lowres has 'next_stage' (which can also be a
30
+ list of configurations!). 3d_cascade_fullres has a 'previous_stage' entry. By manually editing plans files you can
31
+ now connect anything you want, for example 2d with 3d_fullres or whatever. Be wild! (But don't create cycles!)
32
+ - Multiple configurations can point to the same preprocessed data folder to save disk space. Careful! Only
33
+ configurations that use the same spacing, resampling, normalization etc. should share a data source! By default,
34
+ 3d_fullres and 3d_cascade_fullres share the same data
35
+ - Any number of configurations can be added to the plans (remember to give them a unique "data_identifier"!)
36
+
37
+ Folder structures are different and more user-friendly:
38
+ - nnUNet_preprocessed
39
+ - By default, preprocessed data is now saved as: `nnUNet_preprocessed/DATASET_NAME/PLANS_IDENTIFIER_CONFIGURATION` to clearly link them to their corresponding plans and configuration
40
+ - Name of the folder containing the preprocessed images can be adapted with the `data_identifier` key.
41
+ - nnUNet_results
42
+ - Results are now sorted as follows: DATASET_NAME/TRAINERCLASS__PLANSIDENTIFIER__CONFIGURATION/FOLD
43
+
44
+ ## What other changes are planned and not yet implemented?
45
+ - Integration into MONAI (together with our friends at Nvidia)
46
+ - New pretrained weights for a large number of datasets (coming very soon))
47
+
48
+
49
+ [//]: # (- nnU-Net now also natively supports an **ignore label**. Pixels with this label will not contribute to the loss. )
50
+
51
+ [//]: # (Use this to learn from sparsely annotated data, or excluding irrelevant areas from training. Read more [here]&#40;ignore_label.md&#41;.)
nnUNet/documentation/competitions/AortaSeg24.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Authors: \
2
+ Maximilian Rokuss, Michael Baumgartner, Yannick Kirchhoff, Klaus H. Maier-Hein*, Fabian Isensee*
3
+
4
+ *: equal contribution
5
+
6
+ Author Affiliations:\
7
+ Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg \
8
+ Helmholtz Imaging
9
+
10
+ # Introduction
11
+
12
+ This document describes our submission to the [AortaSeg24 Challenge](https://aortaseg24.grand-challenge.org/).
13
+ Our model is essentially a nnU-Net ResEnc L with modified data augmentation. We disable left/right mirroring and use the heavy data augmentation [DA5 Trainer](../../nnunetv2/training/nnUNetTrainer/variants/data_augmentation/nnUNetTrainerDA5.py). Training was performed on an A100 40GB GPU.
14
+
15
+ # Experiment Planning and Preprocessing
16
+ After converting the data into the [nnUNet format](../../../nnUNet/documentation/dataset_format.md) (either keep and just rename the .mha files or convert them to .nii.gz), you can run the preprocessing:
17
+
18
+ ```bash
19
+ nnUNetv2_plan_and_preprocess -d 610 -c 3d_fullres -pl nnUNetPlannerResEncL -np 16
20
+ ```
21
+
22
+ # Training
23
+ We train our model using:
24
+
25
+ ```bash
26
+ nnUNetv2_train 610 3d_fullres all -p nnUNetResEncUNetLPlans -tr nnUNetTrainer_onlyMirror01_DA5
27
+ ```
28
+ Models are trained from scratch. We train one model using all the images and a five fold cross validation ensemble for the submission.
29
+
30
+ We recommend to increase the number of processes used for data augmentation. Otherwise you can run into CPU bottlenecks.
31
+ Use `export nnUNet_n_proc_DA=32` or higher (if your system permits!).
32
+
33
+ # Inference
34
+ For inference you can use the default [nnUNet inference functionalities](../../../nnUNet/documentation/how_to_use_nnunet.md). Specifically, once the training is finished, run:
35
+
36
+ ```bash
37
+ nnUNetv2_predict_from_modelfolder -i INPUT_FOLDER -o OUTPUT_FOLDER -m MODEL_FOLDER -f all
38
+ ```
39
+
40
+ for the single model trained on all the data and
41
+
42
+ ```bash
43
+ nnUNetv2_predict_from_modelfolder -i INPUT_FOLDER -o OUTPUT_FOLDER -m MODEL_FOLDER
44
+ ```
45
+
46
+ for the five fold ensemble.
nnUNet/documentation/competitions/AutoPETII.md ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Look Ma, no code: fine tuning nnU-Net for the AutoPET II challenge by only adjusting its JSON plans
2
+
3
+ Please cite our paper :-*
4
+
5
+ ```text
6
+ COMING SOON
7
+ ```
8
+
9
+ ## Intro
10
+
11
+ See the [Challenge Website](https://autopet-ii.grand-challenge.org/) for details on the challenge.
12
+
13
+ Our solution to this challenge rewuires no code changes at all. All we do is optimize nnU-Net's hyperparameters
14
+ (architecture, batch size, patch size) through modifying the nnUNetplans.json file.
15
+
16
+ ## Prerequisites
17
+ Use the latest pytorch version!
18
+
19
+ We recommend you use the latest nnU-Net version as well! We ran our trainings with commit 913705f which you can try in case something doesn't work as expected:
20
+ `pip install git+https://github.com/MIC-DKFZ/nnUNet.git@913705f`
21
+
22
+ ## How to reproduce our trainings
23
+
24
+ ### Download and convert the data
25
+ 1. Download and extract the AutoPET II dataset
26
+ 2. Convert it to nnU-Net format by running `python nnunetv2/dataset_conversion/Dataset221_AutoPETII_2023.py FOLDER` where folder is the extracted AutoPET II dataset.
27
+
28
+ ### Experiment planning and preprocessing
29
+ We deviate a little from the standard nnU-Net procedure because all our experiments are based on just the 3d_fullres configuration
30
+
31
+ Run the following commands:
32
+ - `nnUNetv2_extract_fingerprint -d 221` extracts the dataset fingerprint
33
+ - `nnUNetv2_plan_experiment -d 221` does the planning for the plain unet
34
+ - `nnUNetv2_plan_experiment -d 221 -pl ResEncUNetPlanner` does the planning for the residual encoder unet
35
+ - `nnUNetv2_preprocess -d 221 -c 3d_fullres` runs all the preprocessing we need
36
+
37
+ ### Modification of plans files
38
+ Please read the [information on how to modify plans files](../explanation_plans_files.md) first!!!
39
+
40
+
41
+ It is easier to have everything in one plans file, so the first thing we do is transfer the ResEnc UNet to the
42
+ default plans file. We use the configuration inheritance feature of nnU-Net to make it use the same data as the
43
+ 3d_fullres configuration.
44
+ Add the following to the 'configurations' dict in 'nnUNetPlans.json':
45
+
46
+ ```json
47
+ "3d_fullres_resenc": {
48
+ "inherits_from": "3d_fullres",
49
+ "network_arch_class_name": "ResidualEncoderUNet",
50
+ "n_conv_per_stage_encoder": [
51
+ 1,
52
+ 3,
53
+ 4,
54
+ 6,
55
+ 6,
56
+ 6
57
+ ],
58
+ "n_conv_per_stage_decoder": [
59
+ 1,
60
+ 1,
61
+ 1,
62
+ 1,
63
+ 1
64
+ ]
65
+ },
66
+ ```
67
+
68
+ (these values are basically just copied from the 'nnUNetResEncUNetPlans.json' file! With everything redundant being omitted thanks to inheritance from 3d_fullres)
69
+
70
+ Now we crank up the patch and batch sizes. Add the following configurations:
71
+ ```json
72
+ "3d_fullres_resenc_bs80": {
73
+ "inherits_from": "3d_fullres_resenc",
74
+ "batch_size": 80
75
+ },
76
+ "3d_fullres_resenc_192x192x192_b24": {
77
+ "inherits_from": "3d_fullres_resenc",
78
+ "patch_size": [
79
+ 192,
80
+ 192,
81
+ 192
82
+ ],
83
+ "batch_size": 24
84
+ }
85
+ ```
86
+
87
+ Save the file (and check for potential Syntax Errors!)
88
+
89
+ ### Run trainings
90
+ Training each model requires 8 Nvidia A100 40GB GPUs. Expect training to run for 5-7 days. You'll need a really good
91
+ CPU to handle the data augmentation! 128C/256T are a must! If you have less threads available, scale down nnUNet_n_proc_DA accordingly.
92
+
93
+ ```bash
94
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_bs80 0 -num_gpus 8
95
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_bs80 1 -num_gpus 8
96
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_bs80 2 -num_gpus 8
97
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_bs80 3 -num_gpus 8
98
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_bs80 4 -num_gpus 8
99
+
100
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_192x192x192_b24 0 -num_gpus 8
101
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_192x192x192_b24 1 -num_gpus 8
102
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_192x192x192_b24 2 -num_gpus 8
103
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_192x192x192_b24 3 -num_gpus 8
104
+ nnUNet_compile=T nnUNet_n_proc_DA=28 nnUNetv2_train 221 3d_fullres_resenc_192x192x192_b24 4 -num_gpus 8
105
+ ```
106
+
107
+ Done!
108
+
109
+ (We also provide pretrained weights in case you don't want to invest the GPU resources, see below)
110
+
111
+ ## How to make predictions with pretrained weights
112
+ Our final model is an ensemble of two configurations:
113
+ - ResEnc UNet with batch size 80
114
+ - ResEnc UNet with patch size 192x192x192 and batch size 24
115
+
116
+ To run inference with these models, do the following:
117
+
118
+ 1. Download the pretrained model weights from [Zenodo](https://zenodo.org/record/8362371)
119
+ 2. Install both .zip files using `nnUNetv2_install_pretrained_model_from_zip`
120
+ 3. Make sure
121
+ 4. Now you can run inference on new cases with `nnUNetv2_predict`:
122
+ - `nnUNetv2_predict -i INPUT -o OUTPUT1 -d 221 -c 3d_fullres_resenc_bs80 -f 0 1 2 3 4 -step_size 0.6 --save_probabilities`
123
+ - `nnUNetv2_predict -i INPUT -o OUTPUT2 -d 221 -c 3d_fullres_resenc_192x192x192_b24 -f 0 1 2 3 4 --save_probabilities`
124
+ - `nnUNetv2_ensemble -i OUTPUT1 OUTPUT2 -o OUTPUT_ENSEMBLE`
125
+
126
+ Note that our inference Docker omitted TTA via mirroring along the axial direction during prediction (only sagittal +
127
+ coronal mirroring). This was
128
+ done to keep the inference time below 10 minutes per image on a T4 GPU (we actually never tested whether we could
129
+ have left this enabled). Just leave it on! You can also leave the step_size at default for the 3d_fullres_resenc_bs80.
nnUNet/documentation/competitions/FLARE24/Task_1/__init__.py ADDED
File without changes
nnUNet/documentation/competitions/FLARE24/Task_1/inference_flare_task1.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Union, Tuple
2
+ import argparse
3
+ import numpy as np
4
+ import os
5
+ from os.path import join
6
+ from pathlib import Path
7
+ from time import time
8
+ import torch
9
+ from torch._dynamo import OptimizedModule
10
+
11
+ from nnunetv2.utilities.label_handling.label_handling import LabelManager
12
+
13
+ from acvl_utils.cropping_and_padding.bounding_boxes import bounding_box_to_slice
14
+ from batchgenerators.utilities.file_and_folder_operations import load_json
15
+
16
+ import nnunetv2
17
+ from nnunetv2.configuration import default_num_processes
18
+ from nnunetv2.utilities.find_class_by_name import recursive_find_python_class
19
+ from nnunetv2.utilities.label_handling.label_handling import determine_num_input_channels
20
+ from nnunetv2.utilities.plans_handling.plans_handler import PlansManager, ConfigurationManager
21
+ from nnunetv2.inference.predict_from_raw_data import nnUNetPredictor
22
+ from nnunetv2.imageio.nibabel_reader_writer import NibabelIOWithReorient
23
+
24
+
25
+ class FlarePredictor(nnUNetPredictor):
26
+ def initialize_from_trained_model_folder(self, model_training_output_dir: str,
27
+ use_folds: Union[Tuple[Union[int, str]], None],
28
+ checkpoint_name: str = 'checkpoint_final.pth'):
29
+ """
30
+ This is used when making predictions with a trained model
31
+ """
32
+ if use_folds is None:
33
+ use_folds = nnUNetPredictor.auto_detect_available_folds(model_training_output_dir, checkpoint_name)
34
+
35
+ dataset_json = load_json(join(model_training_output_dir, 'dataset.json'))
36
+ plans = load_json(join(model_training_output_dir, 'plans.json'))
37
+ plans_manager = PlansManager(plans)
38
+
39
+ if isinstance(use_folds, str):
40
+ use_folds = [use_folds]
41
+
42
+ parameters = []
43
+ for i, f in enumerate(use_folds):
44
+ f = int(f) if f != 'all' else f
45
+ checkpoint = torch.load(join(model_training_output_dir, f'fold_{f}', checkpoint_name),
46
+ map_location=torch.device('cpu'))
47
+ if i == 0:
48
+ trainer_name = checkpoint['trainer_name']
49
+ configuration_name = checkpoint['init_args']['configuration']
50
+ inference_allowed_mirroring_axes = checkpoint['inference_allowed_mirroring_axes'] if \
51
+ 'inference_allowed_mirroring_axes' in checkpoint.keys() else None
52
+
53
+ parameters.append(checkpoint['network_weights'])
54
+
55
+ configuration_manager = plans_manager.get_configuration(configuration_name)
56
+ # restore network
57
+ num_input_channels = determine_num_input_channels(plans_manager, configuration_manager, dataset_json)
58
+ trainer_class = recursive_find_python_class(join(nnunetv2.__path__[0], "training", "nnUNetTrainer"),
59
+ trainer_name, 'nnunetv2.training.nnUNetTrainer')
60
+ if trainer_class is None:
61
+ raise RuntimeError(f'Unable to locate trainer class {trainer_name} in nnunetv2.training.nnUNetTrainer. '
62
+ f'Please place it there (in any .py file)!')
63
+ network = trainer_class.build_network_architecture(
64
+ configuration_manager.network_arch_class_name,
65
+ configuration_manager.network_arch_init_kwargs,
66
+ configuration_manager.network_arch_init_kwargs_req_import,
67
+ num_input_channels,
68
+ plans_manager.get_label_manager(dataset_json).num_segmentation_heads,
69
+ enable_deep_supervision=False
70
+ )
71
+
72
+ self.plans_manager = plans_manager
73
+ self.configuration_manager = configuration_manager
74
+ self.list_of_parameters = parameters
75
+ self.network = network
76
+ self.dataset_json = dataset_json
77
+ self.trainer_name = trainer_name
78
+ self.allowed_mirroring_axes = inference_allowed_mirroring_axes
79
+ self.label_manager = plans_manager.get_label_manager(dataset_json)
80
+
81
+ if ('nnUNet_compile' in os.environ.keys()) and (os.environ['nnUNet_compile'].lower() in ('true', '1', 't')) \
82
+ and not isinstance(self.network, OptimizedModule):
83
+ self.network = torch.compile(self.network)
84
+
85
+
86
+ def convert_predicted_logits_to_segmentation_with_correct_shape(predicted_logits: Union[torch.Tensor, np.ndarray],
87
+ plans_manager: PlansManager,
88
+ configuration_manager: ConfigurationManager,
89
+ label_manager: LabelManager,
90
+ properties_dict: dict,
91
+ return_probabilities: bool = False,
92
+ num_threads_torch: int = default_num_processes):
93
+ old_threads = torch.get_num_threads()
94
+ torch.set_num_threads(num_threads_torch)
95
+
96
+ # resample to original shape
97
+ current_spacing = configuration_manager.spacing if \
98
+ len(configuration_manager.spacing) == \
99
+ len(properties_dict['shape_after_cropping_and_before_resampling']) else \
100
+ [properties_dict['spacing'][0], *configuration_manager.spacing]
101
+ predicted_logits = configuration_manager.resampling_fn_probabilities(predicted_logits,
102
+ properties_dict['shape_after_cropping_and_before_resampling'],
103
+ current_spacing,
104
+ properties_dict['spacing'])
105
+ # return value of resampling_fn_probabilities can be ndarray or Tensor but that does not matter because
106
+ # apply_inference_nonlin will convert to torch
107
+ # predicted_probabilities = label_manager.apply_inference_nonlin(predicted_logits)
108
+ segmentation = predicted_logits.argmax(0)
109
+ del predicted_logits
110
+ # segmentation = label_manager.convert_probabilities_to_segmentation(predicted_probabilities)
111
+
112
+ # segmentation may be torch.Tensor but we continue with numpy
113
+ if isinstance(segmentation, torch.Tensor):
114
+ segmentation = segmentation.cpu().numpy()
115
+
116
+ # put segmentation in bbox (revert cropping)
117
+ segmentation_reverted_cropping = np.zeros(properties_dict['shape_before_cropping'],
118
+ dtype=np.uint8 if len(label_manager.foreground_labels) < 255 else np.uint16)
119
+ slicer = bounding_box_to_slice(properties_dict['bbox_used_for_cropping'])
120
+ segmentation_reverted_cropping[slicer] = segmentation
121
+ del segmentation
122
+
123
+ # revert transpose
124
+ segmentation_reverted_cropping = segmentation_reverted_cropping.transpose(plans_manager.transpose_backward)
125
+ torch.set_num_threads(old_threads)
126
+ return segmentation_reverted_cropping
127
+
128
+
129
+ def export_prediction_from_logits(predicted_array_or_file: Union[np.ndarray, torch.Tensor], properties_dict: dict,
130
+ configuration_manager: ConfigurationManager,
131
+ plans_manager: PlansManager,
132
+ dataset_json_dict_or_file: Union[dict, str], output_file_truncated: str,
133
+ save_probabilities: bool = False):
134
+
135
+ if isinstance(dataset_json_dict_or_file, str):
136
+ dataset_json_dict_or_file = load_json(dataset_json_dict_or_file)
137
+
138
+ label_manager = plans_manager.get_label_manager(dataset_json_dict_or_file)
139
+ ret = convert_predicted_logits_to_segmentation_with_correct_shape(
140
+ predicted_array_or_file, plans_manager, configuration_manager, label_manager, properties_dict,
141
+ return_probabilities=save_probabilities
142
+ )
143
+ del predicted_array_or_file
144
+
145
+ segmentation_final = ret
146
+
147
+ rw = NibabelIOWithReorient()
148
+ rw.write_seg(segmentation_final, output_file_truncated + dataset_json_dict_or_file['file_ending'],
149
+ properties_dict)
150
+
151
+
152
+ def predict_flare(input_dir, output_dir, model_folder, folds=("all",)):
153
+ input_dir = Path(input_dir)
154
+ output_dir = Path(output_dir)
155
+ input_files = sorted(input_dir.glob("*.nii.gz"))
156
+ output_files = [str(output_dir / f.name[:-12]) for f in input_files]
157
+ for input_file, output_file in zip(input_files, output_files):
158
+ print(f"Predicting {input_file.name}")
159
+ start = time()
160
+ plans_manager = PlansManager(load_json(join(model_folder, 'plans.json')))
161
+ configuration_manager = plans_manager.get_configuration("3d_fullres")
162
+ dataset_json = load_json(join(model_folder, 'dataset.json'))
163
+ rw = NibabelIOWithReorient()
164
+ image, props = rw.read_images([input_file,])
165
+ with torch.no_grad():
166
+ predictor = FlarePredictor(tile_step_size=0.5, use_mirroring=False)
167
+ predictor.initialize_from_trained_model_folder(model_folder, use_folds=folds)
168
+ preprocessor = configuration_manager.preprocessor_class(verbose=False)
169
+ data, _ = preprocessor.run_case_npy(image,
170
+ None,
171
+ props,
172
+ plans_manager,
173
+ configuration_manager,
174
+ dataset_json)
175
+ data = torch.from_numpy(data).to(dtype=torch.float32, memory_format=torch.contiguous_format)
176
+ predicted_logits = predictor.predict_logits_from_preprocessed_data(data).cpu()
177
+ export_prediction_from_logits(predicted_logits, props, configuration_manager,
178
+ plans_manager, dataset_json, output_file,
179
+ False)
180
+ print(f"Prediction time: {time() - start:.2f}s")
181
+
182
+
183
+ if __name__ == '__main__':
184
+ os.environ['nnUNet_compile'] = 'f'
185
+ parser = argparse.ArgumentParser()
186
+ parser.add_argument("-i", "--input", default="/workspace/inputs")
187
+ parser.add_argument("-o", "--output", default="/workspace/outputs")
188
+ parser.add_argument("-m", "--model", default="/opt/app/_trained_model")
189
+ parser.add_argument("-f", "--folds", nargs="+", default=["all"])
190
+ args = parser.parse_args()
191
+ predict_flare(args.input, args.output, args.model, args.folds)
nnUNet/documentation/competitions/FLARE24/Task_1/readme.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Authors: \
2
+ Yannick Kirchhoff, Maximilian Rouven Rokuss, Benjamin Hamm, Ashis Ravindran, Constantin Ulrich, Klaus Maier-Hein<sup>&#8224;</sup>, Fabian Isensee<sup>&#8224;</sup>
3
+
4
+ &#8224;: equal contribution
5
+
6
+ # Introduction
7
+
8
+ This document describes our contribution to [Task 1 of the FLARE24 Challenge](https://www.codabench.org/competitions/2319/).
9
+ Our model is basically is a default nnU-Net trained with larger batch size of 4 and 8, respectively. We submitted the batch size 8 model and an ensemble of the batch size 4 and batch size 8 models to the final test set.
10
+
11
+ # Experiment Planning and Preprocessing
12
+
13
+ Bring the downloaded data into the [nnU-Net format](../../../nnUNet/documentation/dataset_format.md) and add the dataset.json file as given here:
14
+
15
+ ```json
16
+ {
17
+ "name": "Dataset301_FLARE24Task1_labeled",
18
+ "description": "Pan Cancer Segmentation",
19
+ "labels": {
20
+ "background": 0,
21
+ "lesion": 1
22
+ },
23
+ "file_ending": ".nii.gz",
24
+ "channel_names": {
25
+ "0": "CT"
26
+ },
27
+ "numTraining": 5000
28
+ }
29
+ ```
30
+
31
+ Afterwards you can run the default nnU-Net planning and preprocessing
32
+
33
+ ```bash
34
+ nnUNetv2_plan_and_preprocess -d 301 -c 3d_fullres
35
+ ```
36
+
37
+ ## Edit the plans files
38
+
39
+ In the generated `nnUNetPlans.json` file add the following configurations
40
+
41
+ ```json
42
+ "3d_fullres_bs4": {
43
+ "inherits_from": "3d_fullres",
44
+ "batch_size": 4
45
+ },
46
+ "3d_fullres_bs8": {
47
+ "inherits_from": "3d_fullres",
48
+ "batch_size": 8
49
+ },
50
+ "3d_fullres_bs4u8": {
51
+ "inherits_from": "3d_fullres",
52
+ "batch_size": 48
53
+ }
54
+ ```
55
+
56
+ Note, the last one is only used for the ensemble model during inference!
57
+
58
+ # Model training
59
+
60
+ Run the following commands to train the models with batch size 4 and 8. The large batch size helps stabilize the training despite the partial labels present in the dataset as well as handling the large number of scans in the dataset. We therefore keep the number of epochs at 1000.
61
+
62
+ ```bash
63
+ nnUNetv2_train 301 3d_fullres_bs4 all
64
+
65
+ nnUNetv2_train 301 3d_fullres_bs8 all
66
+ ```
67
+
68
+ # Inference
69
+
70
+ Our inference is optimized for efficient single scan prediction. For best performance, we strongly recommend running inference using the default `nnUNetv2_predict` command!
71
+
72
+ In order to run inference with the ensemble model you need to create a folder called `nnUNetTrainer__nnUNetPlans__3d_fullres_bs4u8` in the results folder and copy the `dataset.json`, `dataset_fingerprint.json` and `plans.json` from one of the other results folder as well as the `fold_all` from both trainings as `fold_0` and `fold_1`, respectively, into this new folder. This allows for easy ensembling of both models.
73
+
74
+ To run inference simply run the following commands with `folds` set to `all` for single model inference or `0 1` for the ensemble. `model_folder` is the folder containing the training results, i.e. for example `nnUNetTrainer__nnUNetPlans__3d_fullres_bs8`.
75
+
76
+ ```bash
77
+ python inference_flare_task1.py -i input_folder -o output_folder -m model_folder -f folds
78
+ ```
nnUNet/documentation/competitions/FLARE24/Task_2/__init__.py ADDED
File without changes
nnUNet/documentation/competitions/FLARE24/Task_2/inference_flare_task2.py ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Union, List, Tuple
2
+ import argparse
3
+ import itertools
4
+ import multiprocessing
5
+ import numpy as np
6
+ import os
7
+ from os.path import join
8
+ from pathlib import Path
9
+ from time import time
10
+ import torch
11
+ from torch._dynamo import OptimizedModule
12
+ from tqdm import tqdm
13
+
14
+ import openvino as ov
15
+ import openvino.properties.hint as hints
16
+
17
+ from acvl_utils.cropping_and_padding.bounding_boxes import bounding_box_to_slice
18
+ from acvl_utils.cropping_and_padding.padding import pad_nd_image
19
+ from batchgenerators.utilities.file_and_folder_operations import load_json
20
+
21
+ from nnunetv2.utilities.label_handling.label_handling import LabelManager
22
+ import nnunetv2
23
+ from nnunetv2.configuration import default_num_processes
24
+ from nnunetv2.inference.data_iterators import PreprocessAdapterFromNpy
25
+ from nnunetv2.inference.sliding_window_prediction import compute_gaussian
26
+ from nnunetv2.utilities.find_class_by_name import recursive_find_python_class
27
+ from nnunetv2.utilities.helpers import empty_cache, dummy_context
28
+ from nnunetv2.utilities.label_handling.label_handling import determine_num_input_channels
29
+ from nnunetv2.utilities.plans_handling.plans_handler import PlansManager, ConfigurationManager
30
+ from nnunetv2.inference.predict_from_raw_data import nnUNetPredictor
31
+
32
+
33
+ torch.set_num_threads(multiprocessing.cpu_count())
34
+
35
+
36
+ class FlarePredictor(nnUNetPredictor):
37
+ def __init__(self,
38
+ tile_step_size: float = 0.5,
39
+ use_gaussian: bool = True,
40
+ use_mirroring: bool = True,
41
+ perform_everything_on_device: bool = False,
42
+ device: torch.device = torch.device('cpu'),
43
+ verbose: bool = False,
44
+ verbose_preprocessing: bool = False,
45
+ allow_tqdm: bool = True,
46
+ ):
47
+ super().__init__(tile_step_size, use_gaussian, use_mirroring, perform_everything_on_device, device, verbose,
48
+ verbose_preprocessing, allow_tqdm)
49
+ if self.device == torch.device('cuda') or self.device == 'cuda':
50
+ raise RuntimeError('CUDA is not supported for this task')
51
+
52
+ def initialize_from_trained_model_folder(self, model_training_output_dir: str,
53
+ use_folds: Union[Tuple[Union[int, str]], None],
54
+ checkpoint_name: str = 'checkpoint_final.pth',
55
+ save_model: bool = True):
56
+ """
57
+ This is used when making predictions with a trained model
58
+ """
59
+ if use_folds is None:
60
+ use_folds = nnUNetPredictor.auto_detect_available_folds(model_training_output_dir, checkpoint_name)
61
+
62
+ dataset_json = load_json(join(model_training_output_dir, 'dataset.json'))
63
+ plans = load_json(join(model_training_output_dir, 'plans.json'))
64
+ plans_manager = PlansManager(plans)
65
+
66
+ if isinstance(use_folds, str):
67
+ use_folds = [use_folds]
68
+ assert len(use_folds) == 1, 'Only one fold is supported for this task'
69
+
70
+ parameters = []
71
+ for i, f in enumerate(use_folds):
72
+ f = int(f) if f != 'all' else f
73
+ checkpoint = torch.load(join(model_training_output_dir, f'fold_{f}', checkpoint_name),
74
+ map_location=torch.device('cpu'))
75
+ if i == 0:
76
+ trainer_name = checkpoint['trainer_name']
77
+ configuration_name = checkpoint['init_args']['configuration']
78
+ inference_allowed_mirroring_axes = checkpoint['inference_allowed_mirroring_axes'] if \
79
+ 'inference_allowed_mirroring_axes' in checkpoint.keys() else None
80
+
81
+ if save_model:
82
+ parameters.append(checkpoint['network_weights'])
83
+
84
+ configuration_manager = plans_manager.get_configuration(configuration_name)
85
+ if save_model:
86
+ num_input_channels = determine_num_input_channels(plans_manager, configuration_manager, dataset_json)
87
+ trainer_class = recursive_find_python_class(join(nnunetv2.__path__[0], "training", "nnUNetTrainer"),
88
+ trainer_name, 'nnunetv2.training.nnUNetTrainer')
89
+ if trainer_class is None:
90
+ raise RuntimeError(f'Unable to locate trainer class {trainer_name} in nnunetv2.training.nnUNetTrainer. '
91
+ f'Please place it there (in any .py file)!')
92
+ network = trainer_class.build_network_architecture(
93
+ configuration_manager.network_arch_class_name,
94
+ configuration_manager.network_arch_init_kwargs,
95
+ configuration_manager.network_arch_init_kwargs_req_import,
96
+ num_input_channels,
97
+ plans_manager.get_label_manager(dataset_json).num_segmentation_heads,
98
+ enable_deep_supervision=False
99
+ )
100
+ self.network = network
101
+ self.allowed_mirroring_axes = inference_allowed_mirroring_axes
102
+
103
+ self.plans_manager = plans_manager
104
+ self.configuration_manager = configuration_manager
105
+ self.dataset_json = dataset_json
106
+ self.label_manager = plans_manager.get_label_manager(dataset_json)
107
+
108
+ if save_model:
109
+ if not isinstance(self.network, OptimizedModule):
110
+ self.network.load_state_dict(parameters[0])
111
+ else:
112
+ self.network._orig_mod.load_state_dict(parameters[0])
113
+ self.network.eval()
114
+
115
+ config = {hints.performance_mode: hints.PerformanceMode.LATENCY,
116
+ hints.enable_cpu_pinning(): True,
117
+ }
118
+ core = ov.Core()
119
+ core.set_property(
120
+ "CPU",
121
+ {hints.execution_mode: hints.ExecutionMode.PERFORMANCE},
122
+ )
123
+ if save_model:
124
+ input_tensor = torch.randn(1, num_input_channels, *configuration_manager.patch_size, requires_grad=False)
125
+ ov_model = ov.convert_model(self.network, example_input=input_tensor)
126
+ ov.save_model(ov_model, f"{model_training_output_dir}/model.xml")
127
+ import sys
128
+ sys.exit(0)
129
+ ov_model = core.read_model(f"{model_training_output_dir}/model.xml")
130
+ self.network = core.compile_model(ov_model, "CPU", config= config)
131
+
132
+ def predict_from_files(self,
133
+ list_of_lists_or_source_folder: Union[str, List[List[str]]],
134
+ output_folder_or_list_of_truncated_output_files: Union[str, None, List[str]],
135
+ save_probabilities: bool = False,
136
+ overwrite: bool = True,
137
+ num_processes_preprocessing: int = 1,
138
+ num_processes_segmentation_export: int = 1,
139
+ folder_with_segs_from_prev_stage: str = None,
140
+ num_parts: int = 1,
141
+ part_id: int = 0):
142
+ """
143
+ This is nnU-Net's default function for making predictions. It works best for batch predictions
144
+ (predicting many images at once).
145
+ """
146
+
147
+ list_of_lists_or_source_folder, output_filename_truncated, seg_from_prev_stage_files = \
148
+ self._manage_input_and_output_lists(list_of_lists_or_source_folder,
149
+ output_folder_or_list_of_truncated_output_files,
150
+ folder_with_segs_from_prev_stage, overwrite, part_id, num_parts,
151
+ save_probabilities)
152
+ if len(list_of_lists_or_source_folder) == 0:
153
+ return
154
+
155
+ data_iterator = self._internal_get_data_iterator_from_lists_of_filenames(list_of_lists_or_source_folder,
156
+ seg_from_prev_stage_files,
157
+ output_filename_truncated,
158
+ num_processes_preprocessing)
159
+
160
+ return self.predict_from_data_iterator(data_iterator, save_probabilities, num_processes_segmentation_export)
161
+
162
+ def _internal_maybe_mirror_and_predict(self, x: torch.Tensor) -> torch.Tensor:
163
+ mirror_axes = self.allowed_mirroring_axes if self.use_mirroring else None
164
+ if self.use_openvino:
165
+ prediction = torch.from_numpy(self.network(x)[0])
166
+ else:
167
+ prediction = self.network(x)
168
+
169
+ if mirror_axes is not None:
170
+ assert max(mirror_axes) <= x.ndim - 3, 'mirror_axes does not match the dimension of the input!'
171
+
172
+ mirror_axes = [m + 2 for m in mirror_axes]
173
+ axes_combinations = [
174
+ c for i in range(len(mirror_axes)) for c in itertools.combinations(mirror_axes, i + 1)
175
+ ]
176
+ for axes in axes_combinations:
177
+ if not self.is_openvino:
178
+ prediction += torch.flip(self.network(torch.flip(x, axes)), axes)
179
+ else:
180
+ temp_pred = torch.from_numpy(self.network(torch.flip(x, axes))[0])
181
+ prediction += torch.flip(temp_pred, axes)
182
+
183
+ prediction /= (len(axes_combinations) + 1)
184
+ return prediction
185
+
186
+ def _internal_predict_sliding_window_return_logits(self,
187
+ data: torch.Tensor,
188
+ slicers,
189
+ do_on_device: bool = True,
190
+ ):
191
+ predicted_logits = n_predictions = prediction = gaussian = workon = None
192
+ results_device = self.device if do_on_device else torch.device('cpu')
193
+
194
+ try:
195
+ empty_cache(self.device)
196
+
197
+ # move data to device
198
+ if self.verbose:
199
+ print(f'move image to device {results_device}')
200
+ data = data.to(results_device)
201
+
202
+ # preallocate arrays
203
+ if self.verbose:
204
+ print(f'preallocating results arrays on device {results_device}')
205
+ predicted_logits = torch.zeros((self.label_manager.num_segmentation_heads, *data.shape[1:]),
206
+ dtype=torch.half,
207
+ device=results_device)
208
+ n_predictions = torch.zeros(data.shape[1:], dtype=torch.half, device=results_device)
209
+
210
+ if self.use_gaussian:
211
+ gaussian = compute_gaussian(tuple(self.configuration_manager.patch_size), sigma_scale=1. / 8,
212
+ value_scaling_factor=10,
213
+ device=results_device)
214
+ else:
215
+ gaussian = 1
216
+
217
+ if not self.allow_tqdm and self.verbose:
218
+ print(f'running prediction: {len(slicers)} steps')
219
+ for sl in tqdm(slicers, disable=not self.allow_tqdm):
220
+ workon = data[sl][None]
221
+ workon = workon.to(self.device)
222
+
223
+ prediction = self._internal_maybe_mirror_and_predict(workon)[0].to(results_device)
224
+
225
+ if self.use_gaussian:
226
+ prediction *= gaussian
227
+ predicted_logits[sl] += prediction
228
+ n_predictions[sl[1:]] += gaussian
229
+
230
+ predicted_logits /= n_predictions
231
+ # check for infs
232
+ if torch.any(torch.isinf(predicted_logits)):
233
+ raise RuntimeError('Encountered inf in predicted array. Aborting... If this problem persists, '
234
+ 'reduce value_scaling_factor in compute_gaussian or increase the dtype of '
235
+ 'predicted_logits to fp32')
236
+ except Exception as e:
237
+ del predicted_logits, n_predictions, prediction, gaussian, workon
238
+ empty_cache(self.device)
239
+ empty_cache(results_device)
240
+ raise e
241
+ return predicted_logits
242
+
243
+ def predict_logits_from_preprocessed_data(self, data: torch.Tensor) -> torch.Tensor:
244
+ """
245
+ IMPORTANT! IF YOU ARE RUNNING THE CASCADE, THE SEGMENTATION FROM THE PREVIOUS STAGE MUST ALREADY BE STACKED ON
246
+ TOP OF THE IMAGE AS ONE-HOT REPRESENTATION! SEE PreprocessAdapter ON HOW THIS SHOULD BE DONE!
247
+
248
+ RETURNED LOGITS HAVE THE SHAPE OF THE INPUT. THEY MUST BE CONVERTED BACK TO THE ORIGINAL IMAGE SIZE.
249
+ SEE convert_predicted_logits_to_segmentation_with_correct_shape
250
+ """
251
+ prediction = None
252
+
253
+ if not self.use_openvino:
254
+ for params in self.list_of_parameters:
255
+
256
+ # messing with state dict names...
257
+ if not isinstance(self.network, OptimizedModule):
258
+ self.network.load_state_dict(params)
259
+ else:
260
+ self.network._orig_mod.load_state_dict(params)
261
+
262
+ # why not leave prediction on device if perform_everything_on_device? Because this may cause the
263
+ # second iteration to crash due to OOM. Grabbing that with try except cause way more bloated code than
264
+ # this actually saves computation time
265
+ if prediction is None:
266
+ prediction = self.predict_sliding_window_return_logits(data).to('cpu')
267
+ else:
268
+ prediction += self.predict_sliding_window_return_logits(data).to('cpu')
269
+
270
+ if len(self.list_of_parameters) > 1:
271
+ prediction /= len(self.list_of_parameters)
272
+
273
+ else:
274
+ if prediction is None:
275
+ prediction = self.predict_sliding_window_return_logits(data)
276
+ else:
277
+ prediction += self.predict_sliding_window_return_logits(data)
278
+
279
+ if self.verbose: print('Prediction done')
280
+ return prediction
281
+
282
+ @torch.inference_mode()
283
+ def predict_sliding_window_return_logits(self, input_image: torch.Tensor) \
284
+ -> Union[np.ndarray, torch.Tensor]:
285
+ assert isinstance(input_image, torch.Tensor)
286
+ if self.device not in [torch.device('cpu'), 'cpu']:
287
+ self.network = self.network.to(self.device)
288
+ self.network.eval()
289
+
290
+ empty_cache(self.device)
291
+
292
+ # Autocast can be annoying
293
+ # If the device_type is 'cpu' then it's slow as heck on some CPUs (no auto bfloat16 support detection)
294
+ # and needs to be disabled.
295
+ # If the device_type is 'mps' then it will complain that mps is not implemented, even if enabled=False
296
+ # is set. Whyyyyyyy. (this is why we don't make use of enabled=False)
297
+ # So autocast will only be active if we have a cuda device.
298
+ with torch.autocast(self.device.type, enabled=True) if self.device.type == 'cuda' else dummy_context():
299
+ assert input_image.ndim == 4, 'input_image must be a 4D np.ndarray or torch.Tensor (c, x, y, z)'
300
+
301
+ if self.verbose:
302
+ print(f'Input shape: {input_image.shape}')
303
+ print("step_size:", self.tile_step_size)
304
+ print("mirror_axes:", self.allowed_mirroring_axes if self.use_mirroring else None)
305
+
306
+ # if input_image is smaller than tile_size we need to pad it to tile_size.
307
+ data, slicer_revert_padding = pad_nd_image(input_image, self.configuration_manager.patch_size,
308
+ 'constant', {'value': 0}, True,
309
+ None)
310
+
311
+ slicers = self._internal_get_sliding_window_slicers(data.shape[1:])
312
+
313
+ if self.perform_everything_on_device and self.device != 'cpu':
314
+ # we need to try except here because we can run OOM in which case we need to fall back to CPU as a results device
315
+ try:
316
+ predicted_logits = self._internal_predict_sliding_window_return_logits(data, slicers,
317
+ self.perform_everything_on_device)
318
+ except RuntimeError:
319
+ print(
320
+ 'Prediction on device was unsuccessful, probably due to a lack of memory. Moving results arrays to CPU')
321
+ empty_cache(self.device)
322
+ predicted_logits = self._internal_predict_sliding_window_return_logits(data, slicers, False)
323
+ else:
324
+ predicted_logits = self._internal_predict_sliding_window_return_logits(data, slicers,
325
+ self.perform_everything_on_device)
326
+
327
+ empty_cache(self.device)
328
+ # revert padding
329
+ predicted_logits = predicted_logits[(slice(None), *slicer_revert_padding[1:])]
330
+ return predicted_logits
331
+
332
+ def convert_predicted_logits_to_segmentation_with_correct_shape(self, predicted_logits: Union[torch.Tensor, np.ndarray],
333
+ plans_manager: PlansManager,
334
+ configuration_manager: ConfigurationManager,
335
+ label_manager: LabelManager,
336
+ properties_dict: dict,
337
+ return_probabilities: bool = False,
338
+ num_threads_torch: int = default_num_processes):
339
+
340
+ # resample to original shape
341
+ current_spacing = configuration_manager.spacing if \
342
+ len(configuration_manager.spacing) == \
343
+ len(properties_dict['shape_after_cropping_and_before_resampling']) else \
344
+ [properties_dict['spacing'][0], *configuration_manager.spacing]
345
+ predicted_logits = configuration_manager.resampling_fn_probabilities(predicted_logits,
346
+ properties_dict['shape_after_cropping_and_before_resampling'],
347
+ current_spacing,
348
+ properties_dict['spacing'])
349
+ segmentation = predicted_logits.argmax(0)
350
+ del predicted_logits
351
+
352
+ # segmentation may be torch.Tensor but we continue with numpy
353
+ if isinstance(segmentation, torch.Tensor):
354
+ segmentation = segmentation.cpu().numpy()
355
+
356
+ # put segmentation in bbox (revert cropping)
357
+ segmentation_reverted_cropping = np.zeros(properties_dict['shape_before_cropping'],
358
+ dtype=np.uint8 if len(label_manager.foreground_labels) < 255 else np.uint16)
359
+ slicer = bounding_box_to_slice(properties_dict['bbox_used_for_cropping'])
360
+ segmentation_reverted_cropping[slicer] = segmentation
361
+ del segmentation
362
+
363
+ # revert transpose
364
+ segmentation_reverted_cropping = segmentation_reverted_cropping.transpose(plans_manager.transpose_backward)
365
+ return segmentation_reverted_cropping
366
+
367
+ def export_prediction_from_logits(self, predicted_array_or_file: Union[np.ndarray, torch.Tensor], properties_dict: dict,
368
+ configuration_manager: ConfigurationManager,
369
+ plans_manager: PlansManager,
370
+ dataset_json_dict_or_file: Union[dict, str], output_file_truncated: str,
371
+ save_probabilities: bool = False):
372
+
373
+ if isinstance(dataset_json_dict_or_file, str):
374
+ dataset_json_dict_or_file = load_json(dataset_json_dict_or_file)
375
+
376
+ label_manager = plans_manager.get_label_manager(dataset_json_dict_or_file)
377
+ ret = self.convert_predicted_logits_to_segmentation_with_correct_shape(
378
+ predicted_array_or_file, plans_manager, configuration_manager, label_manager, properties_dict,
379
+ return_probabilities=save_probabilities
380
+ )
381
+ del predicted_array_or_file
382
+
383
+ segmentation_final = ret
384
+
385
+ rw = plans_manager.image_reader_writer_class()
386
+ rw.write_seg(segmentation_final, output_file_truncated + dataset_json_dict_or_file['file_ending'],
387
+ properties_dict)
388
+
389
+ def predict_single_npy_array(self, input_image: np.ndarray, image_properties: dict,
390
+ segmentation_previous_stage: np.ndarray = None,
391
+ output_file_truncated: str = None,
392
+ save_or_return_probabilities: bool = False):
393
+ """
394
+ WARNING: SLOW. ONLY USE THIS IF YOU CANNOT GIVE NNUNET MULTIPLE IMAGES AT ONCE FOR SOME REASON.
395
+
396
+
397
+ input_image: Make sure to load the image in the way nnU-Net expects! nnU-Net is trained on a certain axis
398
+ ordering which cannot be disturbed in inference,
399
+ otherwise you will get bad results. The easiest way to achieve that is to use the same I/O class
400
+ for loading images as was used during nnU-Net preprocessing! You can find that class in your
401
+ plans.json file under the key "image_reader_writer". If you decide to freestyle, know that the
402
+ default axis ordering for medical images is the one from SimpleITK. If you load with nibabel,
403
+ you need to transpose your axes AND your spacing from [x,y,z] to [z,y,x]!
404
+ image_properties must only have a 'spacing' key!
405
+ """
406
+ ppa = PreprocessAdapterFromNpy([input_image], [segmentation_previous_stage], [image_properties],
407
+ [output_file_truncated],
408
+ self.plans_manager, self.dataset_json, self.configuration_manager,
409
+ num_threads_in_multithreaded=1, verbose=self.verbose)
410
+ if self.verbose:
411
+ print('preprocessing')
412
+ dct = next(ppa)
413
+
414
+ if self.verbose:
415
+ print('predicting')
416
+ predicted_logits = self.predict_logits_from_preprocessed_data(dct['data']).cpu()
417
+
418
+ if self.verbose:
419
+ print('resampling to original shape')
420
+ self.export_prediction_from_logits(predicted_logits, dct['data_properties'], self.configuration_manager,
421
+ self.plans_manager, self.dataset_json, output_file_truncated,
422
+ save_or_return_probabilities)
423
+
424
+
425
+ def predict_flare(input_dir, output_dir, model_folder, save_model):
426
+ input_dir = Path(input_dir)
427
+ output_dir = Path(output_dir)
428
+ output_dir.mkdir(exist_ok=True, parents=True)
429
+ input_files = list(input_dir.glob("*.nii.gz"))
430
+ output_files = [str(output_dir / f.name[:-12]) for f in input_files]
431
+ for input_file, output_file in zip(input_files, output_files):
432
+ print(f"Predicting {input_file.name}")
433
+ start = time()
434
+ predictor = FlarePredictor(tile_step_size=0.5, use_mirroring=False, device=torch.device("cpu"))
435
+ predictor.initialize_from_trained_model_folder(model_folder, ("all",), save_model=save_model)
436
+ rw = predictor.plans_manager.image_reader_writer_class()
437
+ image, props = rw.read_images([input_file,])
438
+ _ = predictor.predict_single_npy_array(image, props, None, output_file, False)
439
+ print(f"Prediction time: {time() - start:.2f}s")
440
+
441
+
442
+ if __name__ == '__main__':
443
+ parser = argparse.ArgumentParser()
444
+ parser.add_argument("-i", "--input", default="/workspace/inputs")
445
+ parser.add_argument("-o", "--output", default="/workspace/outputs")
446
+ parser.add_argument("-m", "--model", default="/opt/app/_trained_model")
447
+ parser.add_argument("-save_model", action="store_true")
448
+ args = parser.parse_args()
449
+ predict_flare(args.input, args.output, args.model, args.save_model)
nnUNet/documentation/competitions/FLARE24/Task_2/readme.md ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Authors: \
2
+ Yannick Kirchhoff*, Ashis Ravindran*, Maximilian Rouven Rokuss, Benjamin Hamm, Constantin Ulrich, Klaus Maier-Hein<sup>&#8224;</sup>, Fabian Isensee<sup>&#8224;</sup>
3
+
4
+ *: equal contribution \
5
+ &#8224;: equal contribution
6
+
7
+ # Introduction
8
+
9
+ This document describes our contribution to [Task 2 of the FLARE24 Challenge](https://www.codabench.org/competitions/2320/).
10
+ Our model is basically is a default nnU-Net with a custom low resolution setting and OpenVINO optimizations for faster CPU inference.
11
+
12
+ # Experiment Planning and Preprocessing
13
+
14
+ Bring the downloaded data into the [nnU-Net format](../../../nnUNet/documentation/dataset_format.md) and add the dataset.json file as given here:
15
+
16
+ ```json
17
+ {
18
+ "name": "Dataset311_FLARE24Task2_labeled",
19
+ "description": "Abdominal Organ Segmentation",
20
+ "labels": {
21
+ "background": 0,
22
+ "liver": 1,
23
+ "right kidney": 2,
24
+ "spleen": 3,
25
+ "pancreas": 4,
26
+ "aorta": 5,
27
+ "ivc": 6,
28
+ "rag": 7,
29
+ "lag": 8,
30
+ "gallbladder": 9,
31
+ "esophagus": 10,
32
+ "stomach": 11,
33
+ "duodenum": 12,
34
+ "left kidney": 13
35
+ },
36
+ "file_ending": ".nii.gz",
37
+ "channel_names": {
38
+ "0": "CT"
39
+ },
40
+ "overwrite_image_reader_writer": "NibabelIOWithReorient",
41
+ "numTraining": 50
42
+ }
43
+ ```
44
+
45
+ Afterwards you can run the default nnU-Net planning and preprocessing
46
+
47
+ ```bash
48
+ nnUNetv2_plan_and_preprocess -d 311 -c 3d_fullres
49
+ ```
50
+
51
+ ## Edit the plans files
52
+
53
+ The generated `nnUNetPlans.json` file needs to be edited to incorporate the custom low resolution setting.
54
+
55
+ ```json
56
+ "3d_halfres": {
57
+ "inherits_from": "3d_fullres",
58
+ "data_identifier": "nnUNetPlans_3d_halfres",
59
+ "spacing": [
60
+ 5,
61
+ 1.6,
62
+ 1.6
63
+ ]
64
+ },
65
+ "3d_halfiso": {
66
+ "inherits_from": "3d_fullres",
67
+ "data_identifier": "nnUNetPlans_3d_halfiso",
68
+ "spacing": [
69
+ 2.5,
70
+ 2.5,
71
+ 2.5
72
+ ]
73
+ },
74
+ ```
75
+
76
+ `3d_halfres` is a configuration with exactly half resolution, used as an ablation of our submission, `3d_halfiso` is the isotropic configuration we submitted as a final solution.
77
+
78
+ # Model training
79
+
80
+ Run one of the following commands to train the respective configurations. `3d_halfiso` yielded significantly better results in our experiments as well as on the final test set and is the recommended configuration.
81
+
82
+ ```bash
83
+ nnUNetv2_train 311 3d_halfres all
84
+
85
+ nnUNetv2_train 311 3d_halfiso all
86
+ ```
87
+
88
+ # Inference
89
+
90
+ Our inference is optimized for efficient single scan prediction. For best performance, we strongly recommend running inference using the default `nnUNetv2_predict` command!
91
+
92
+ Inference using the provided script requires OpenVINO, which can easily be installed via
93
+
94
+ ```bash
95
+ pip install openvino
96
+ ```
97
+
98
+ To run inference simply run the following commands. `model_folder` is the folder containing the training results, i.e. for example `nnUNetTrainer__nnUNetPlans__3d_halfiso`. `-save_model` needs to be set to precompile the model once using OpenVINO. If not precompiled model exists, the inference script will fail!
99
+
100
+ ```bash
101
+ python inference_flare_task1.py -i input_folder -o output_folder -m model_folder [-save_model]
102
+ ```
nnUNet/documentation/competitions/FLARE24/__init__.py ADDED
File without changes
nnUNet/documentation/competitions/Toothfairy2/__init__.py ADDED
File without changes
nnUNet/documentation/competitions/Toothfairy2/inference_script_semseg_only_customInf2.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import gc
3
+ import os
4
+ from pathlib import Path
5
+ from queue import Queue
6
+ from threading import Thread
7
+ from typing import Union, Tuple
8
+
9
+ import nnunetv2
10
+ import numpy as np
11
+ import torch
12
+ from acvl_utils.cropping_and_padding.bounding_boxes import bounding_box_to_slice
13
+ from acvl_utils.cropping_and_padding.padding import pad_nd_image
14
+ from batchgenerators.utilities.file_and_folder_operations import load_json, join
15
+ from nnunetv2.imageio.simpleitk_reader_writer import SimpleITKIO
16
+ from nnunetv2.inference.predict_from_raw_data import nnUNetPredictor
17
+ from nnunetv2.inference.sliding_window_prediction import compute_gaussian
18
+ from nnunetv2.utilities.find_class_by_name import recursive_find_python_class
19
+ from nnunetv2.utilities.helpers import empty_cache, dummy_context
20
+ from nnunetv2.utilities.label_handling.label_handling import determine_num_input_channels
21
+ from nnunetv2.utilities.plans_handling.plans_handler import PlansManager
22
+ from torch._dynamo import OptimizedModule
23
+ from torch.backends import cudnn
24
+ from tqdm import tqdm
25
+
26
+
27
+ class CustomPredictor(nnUNetPredictor):
28
+ def predict_single_npy_array(self, input_image: np.ndarray, image_properties: dict,
29
+ segmentation_previous_stage: np.ndarray = None):
30
+ torch.set_num_threads(7)
31
+ with torch.no_grad():
32
+ self.network = self.network.to(self.device)
33
+ self.network.eval()
34
+
35
+ if self.verbose:
36
+ print('preprocessing')
37
+ preprocessor = self.configuration_manager.preprocessor_class(verbose=self.verbose)
38
+ data, _, image_properties = preprocessor.run_case_npy(input_image, None, image_properties,
39
+ self.plans_manager,
40
+ self.configuration_manager,
41
+ self.dataset_json)
42
+
43
+ data = torch.from_numpy(data)
44
+ del input_image
45
+ if self.verbose:
46
+ print('predicting')
47
+
48
+ predicted_logits = self.predict_preprocessed_image(data)
49
+
50
+ if self.verbose: print('Prediction done')
51
+
52
+ segmentation = self.convert_predicted_logits_to_segmentation_with_correct_shape(predicted_logits,
53
+ image_properties)
54
+ return segmentation
55
+
56
+ def initialize_from_trained_model_folder(self, model_training_output_dir: str,
57
+ use_folds: Union[Tuple[Union[int, str]], None],
58
+ checkpoint_name: str = 'checkpoint_final.pth'):
59
+ """
60
+ This is used when making predictions with a trained model
61
+ """
62
+ if use_folds is None:
63
+ use_folds = nnUNetPredictor.auto_detect_available_folds(model_training_output_dir, checkpoint_name)
64
+
65
+ dataset_json = load_json(join(model_training_output_dir, 'dataset.json'))
66
+ plans = load_json(join(model_training_output_dir, 'plans.json'))
67
+ plans_manager = PlansManager(plans)
68
+
69
+ if isinstance(use_folds, str):
70
+ use_folds = [use_folds]
71
+
72
+ parameters = []
73
+ for i, f in enumerate(use_folds):
74
+ f = int(f) if f != 'all' else f
75
+ checkpoint = torch.load(join(model_training_output_dir, f'fold_{f}', checkpoint_name),
76
+ map_location=torch.device('cpu'), weights_only=False)
77
+ if i == 0:
78
+ trainer_name = checkpoint['trainer_name']
79
+ configuration_name = checkpoint['init_args']['configuration']
80
+ inference_allowed_mirroring_axes = checkpoint['inference_allowed_mirroring_axes'] if \
81
+ 'inference_allowed_mirroring_axes' in checkpoint.keys() else None
82
+
83
+ parameters.append(join(model_training_output_dir, f'fold_{f}', checkpoint_name))
84
+
85
+ configuration_manager = plans_manager.get_configuration(configuration_name)
86
+ # restore network
87
+ num_input_channels = determine_num_input_channels(plans_manager, configuration_manager, dataset_json)
88
+ trainer_class = recursive_find_python_class(join(nnunetv2.__path__[0], "training", "nnUNetTrainer"),
89
+ trainer_name, 'nnunetv2.training.nnUNetTrainer')
90
+ if trainer_class is None:
91
+ raise RuntimeError(f'Unable to locate trainer class {trainer_name} in nnunetv2.training.nnUNetTrainer. '
92
+ f'Please place it there (in any .py file)!')
93
+ network = trainer_class.build_network_architecture(
94
+ configuration_manager.network_arch_class_name,
95
+ configuration_manager.network_arch_init_kwargs,
96
+ configuration_manager.network_arch_init_kwargs_req_import,
97
+ num_input_channels,
98
+ plans_manager.get_label_manager(dataset_json).num_segmentation_heads,
99
+ enable_deep_supervision=False
100
+ )
101
+
102
+ self.plans_manager = plans_manager
103
+ self.configuration_manager = configuration_manager
104
+ self.list_of_parameters = parameters
105
+ self.network = network
106
+ self.dataset_json = dataset_json
107
+ self.trainer_name = trainer_name
108
+ self.allowed_mirroring_axes = inference_allowed_mirroring_axes
109
+ self.label_manager = plans_manager.get_label_manager(dataset_json)
110
+ if ('nnUNet_compile' in os.environ.keys()) and (os.environ['nnUNet_compile'].lower() in ('true', '1', 't')) \
111
+ and not isinstance(self.network, OptimizedModule):
112
+ print('Using torch.compile')
113
+ self.network = torch.compile(self.network)
114
+
115
+ @torch.inference_mode(mode=True)
116
+ def predict_preprocessed_image(self, image):
117
+ empty_cache(self.device)
118
+ data_device = torch.device('cpu')
119
+ predicted_logits_device = torch.device('cpu')
120
+ gaussian_device = torch.device('cpu')
121
+ compute_device = torch.device('cuda:0')
122
+
123
+ data, slicer_revert_padding = pad_nd_image(image, self.configuration_manager.patch_size,
124
+ 'constant', {'value': 0}, True,
125
+ None)
126
+ del image
127
+
128
+ slicers = self._internal_get_sliding_window_slicers(data.shape[1:])
129
+
130
+ empty_cache(self.device)
131
+
132
+ data = data.to(data_device)
133
+ predicted_logits = torch.zeros((self.label_manager.num_segmentation_heads, *data.shape[1:]),
134
+ dtype=torch.half,
135
+ device=predicted_logits_device)
136
+ gaussian = compute_gaussian(tuple(self.configuration_manager.patch_size), sigma_scale=1. / 8,
137
+ value_scaling_factor=10,
138
+ device=gaussian_device, dtype=torch.float16)
139
+
140
+ if not self.allow_tqdm and self.verbose:
141
+ print(f'running prediction: {len(slicers)} steps')
142
+
143
+ for p in self.list_of_parameters:
144
+ # network weights have to be updated outside autocast!
145
+ # we are loading parameters on demand instead of loading them upfront. This reduces memory footprint a lot.
146
+ # each set of parameters is only used once on the test set (one image) so run time wise this is almost the
147
+ # same
148
+ self.network.load_state_dict(torch.load(p, map_location=compute_device)['network_weights'], weights_only=False)
149
+ with torch.autocast(self.device.type, enabled=True):
150
+ for sl in tqdm(slicers, disable=not self.allow_tqdm):
151
+ pred = self._internal_maybe_mirror_and_predict(data[sl][None].to(compute_device))[0].to(
152
+ predicted_logits_device)
153
+ pred /= (pred.max() / 100)
154
+ predicted_logits[sl] += (pred * gaussian)
155
+ del pred
156
+ empty_cache(self.device)
157
+ return predicted_logits
158
+
159
+ def convert_predicted_logits_to_segmentation_with_correct_shape(self, predicted_logits, props):
160
+ old = torch.get_num_threads()
161
+ torch.set_num_threads(7)
162
+
163
+ # resample to original shape
164
+ spacing_transposed = [props['spacing'][i] for i in self.plans_manager.transpose_forward]
165
+ current_spacing = self.configuration_manager.spacing if \
166
+ len(self.configuration_manager.spacing) == \
167
+ len(props['shape_after_cropping_and_before_resampling']) else \
168
+ [spacing_transposed[0], *self.configuration_manager.spacing]
169
+ predicted_logits = self.configuration_manager.resampling_fn_probabilities(predicted_logits,
170
+ props[
171
+ 'shape_after_cropping_and_before_resampling'],
172
+ current_spacing,
173
+ [props['spacing'][i] for i in
174
+ self.plans_manager.transpose_forward])
175
+
176
+ segmentation = None
177
+ pp = None
178
+ try:
179
+ with torch.no_grad():
180
+ pp = predicted_logits.to('cuda:0')
181
+ segmentation = pp.argmax(0).cpu()
182
+ del pp
183
+ except RuntimeError:
184
+ del segmentation, pp
185
+ torch.cuda.empty_cache()
186
+ segmentation = predicted_logits.argmax(0)
187
+ del predicted_logits
188
+
189
+ # segmentation may be torch.Tensor but we continue with numpy
190
+ if isinstance(segmentation, torch.Tensor):
191
+ segmentation = segmentation.cpu().numpy()
192
+
193
+ # put segmentation in bbox (revert cropping)
194
+ segmentation_reverted_cropping = np.zeros(props['shape_before_cropping'],
195
+ dtype=np.uint8 if len(
196
+ self.label_manager.foreground_labels) < 255 else np.uint16)
197
+ slicer = bounding_box_to_slice(props['bbox_used_for_cropping'])
198
+ segmentation_reverted_cropping[slicer] = segmentation
199
+ del segmentation
200
+
201
+ # revert transpose
202
+ segmentation_reverted_cropping = segmentation_reverted_cropping.transpose(self.plans_manager.transpose_backward)
203
+ torch.set_num_threads(old)
204
+ return segmentation_reverted_cropping
205
+
206
+
207
+ def predict_semseg(im, prop, semseg_trained_model, semseg_folds):
208
+ # initialize predictors
209
+ pred_semseg = CustomPredictor(
210
+ tile_step_size=0.5,
211
+ use_mirroring=True,
212
+ use_gaussian=True,
213
+ perform_everything_on_device=False,
214
+ allow_tqdm=True
215
+ )
216
+ pred_semseg.initialize_from_trained_model_folder(
217
+ semseg_trained_model,
218
+ use_folds=semseg_folds,
219
+ checkpoint_name='checkpoint_final.pth'
220
+ )
221
+
222
+ semseg_pred = pred_semseg.predict_single_npy_array(
223
+ im, prop, None
224
+ )
225
+ torch.cuda.empty_cache()
226
+ gc.collect()
227
+ return semseg_pred
228
+
229
+
230
+ def map_labels_to_toothfairy(predicted_seg: np.ndarray) -> np.ndarray:
231
+ # Create an array that maps the labels directly
232
+ max_label = 42
233
+ mapping = np.arange(max_label + 1)
234
+
235
+ # Define the specific remapping
236
+ remapping = {19: 21, 20: 22, 21: 23, 22: 24, 23: 25, 24: 26, 25: 27, 26: 28,
237
+ 27: 31, 28: 32, 29: 33, 30: 34, 31: 35, 32: 36, 33: 37, 34: 38,
238
+ 35: 41, 36: 42, 37: 43, 38: 44, 39: 45, 40: 46, 41: 47, 42: 48}
239
+
240
+ # Apply the remapping
241
+ for k, v in remapping.items():
242
+ mapping[k] = v
243
+
244
+ return mapping[predicted_seg]
245
+
246
+
247
+ def postprocess(prediction_npy, vol_per_voxel, verbose: bool = False):
248
+ cutoffs = {1: 0.0,
249
+ 2: 78411.5,
250
+ 3: 0.0,
251
+ 4: 0.0,
252
+ 5: 2800.0,
253
+ 6: 1216.5,
254
+ 7: 0.0,
255
+ 8: 6222.0,
256
+ 9: 1573.0,
257
+ 10: 946.0,
258
+ 11: 0.0,
259
+ 12: 6783.5,
260
+ 13: 9469.5,
261
+ 14: 0.0,
262
+ 15: 2260.0,
263
+ 16: 3566.0,
264
+ 17: 6321.0,
265
+ 18: 4221.5,
266
+ 19: 5829.0,
267
+ 20: 0.0,
268
+ 21: 0.0,
269
+ 22: 468.0,
270
+ 23: 1555.0,
271
+ 24: 1291.5,
272
+ 25: 2834.5,
273
+ 26: 584.5,
274
+ 27: 0.0,
275
+ 28: 0.0,
276
+ 29: 0.0,
277
+ 30: 0.0,
278
+ 31: 1935.5,
279
+ 32: 0.0,
280
+ 33: 0.0,
281
+ 34: 6140.0,
282
+ 35: 0.0,
283
+ 36: 0.0,
284
+ 37: 0.0,
285
+ 38: 2710.0,
286
+ 39: 0.0,
287
+ 40: 0.0,
288
+ 41: 0.0,
289
+ 42: 970.0}
290
+
291
+ vol_per_voxel_cutoffs = 0.3 * 0.3 * 0.3
292
+ for c in cutoffs.keys():
293
+ co = cutoffs[c]
294
+ if co > 0:
295
+ mask = prediction_npy == c
296
+ pred_vol = np.sum(mask) * vol_per_voxel
297
+ if 0 < pred_vol < (co * vol_per_voxel_cutoffs):
298
+ prediction_npy[mask] = 0
299
+ if verbose:
300
+ print(
301
+ f'removed label {c} because predicted volume of {pred_vol} is less than the cutoff {co * vol_per_voxel_cutoffs}')
302
+ return prediction_npy
303
+
304
+
305
+ if __name__ == '__main__':
306
+ os.environ['nnUNet_compile'] = 'f'
307
+
308
+ parser = argparse.ArgumentParser()
309
+ parser.add_argument('-i', '--input_folder', type=Path, default="/input/images/cbct/")
310
+ parser.add_argument('-o', '--output_folder', type=Path, default="/output/images/oral-pharyngeal-segmentation/")
311
+ parser.add_argument('-sem_mod', '--semseg_trained_model', type=str,
312
+ default="/opt/app/_trained_model/semseg_trained_model")
313
+ parser.add_argument('--semseg_folds', type=str, nargs='+', default=[0, 1])
314
+ args = parser.parse_args()
315
+
316
+ args.output_folder.mkdir(exist_ok=True, parents=True)
317
+
318
+ semseg_folds = [i if i == 'all' else int(i) for i in args.semseg_folds]
319
+ semseg_trained_model = args.semseg_trained_model
320
+
321
+ rw = SimpleITKIO()
322
+
323
+ input_files = list(args.input_folder.glob('*.nii.gz')) + list(args.input_folder.glob('*.mha'))
324
+
325
+ for input_fname in input_files:
326
+ output_fname = args.output_folder / input_fname.name
327
+
328
+ # we start with the instance seg because we can then start converting that while semseg is being predicted
329
+ # load test image
330
+ im, prop = rw.read_images([input_fname])
331
+
332
+ with torch.no_grad():
333
+ semseg_pred = predict_semseg(im, prop, semseg_trained_model, semseg_folds)
334
+ torch.cuda.empty_cache()
335
+ gc.collect()
336
+
337
+ # now postprocess
338
+ semseg_pred = postprocess(semseg_pred, np.prod(prop['spacing']), True)
339
+
340
+ semseg_pred = map_labels_to_toothfairy(semseg_pred)
341
+
342
+ # now save
343
+ rw.write_seg(semseg_pred, output_fname, prop)
nnUNet/documentation/competitions/Toothfairy2/readme.md ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Authors: \
2
+ Fabian Isensee*, Yannick Kirchhoff*, Lars Kraemer, Max Rokuss, Constantin Ulrich, Klaus H. Maier-Hein
3
+
4
+ *: equal contribution
5
+
6
+ Author Affiliations:\
7
+ Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg \
8
+ Helmholtz Imaging
9
+
10
+ # Introduction
11
+
12
+ This document describes our submission to the [Toothfairy2 Challenge](https://toothfairy2.grand-challenge.org/toothfairy2/).
13
+ Our model is essentially a nnU-Net ResEnc L with the patch size upscaled to 160x320x320 pixels. We disable left/right
14
+ mirroring and train for 1500 instead of the standard 1000 epochs. Training was either done on 2xA100 40GB or one GH200 96GB.
15
+
16
+ # Dataset Conversion
17
+
18
+ # Experiment Planning and Preprocessing
19
+ Adapt and run the [dataset conversion script](../../../nnunetv2/dataset_conversion/Dataset119_ToothFairy2_All.py).
20
+ This script just converts the mha files to nifti (smaller file size) and removes the unused label ids.
21
+
22
+ ## Extract fingerprint:
23
+ `nnUNetv2_extract_fingerprint -d 119 -np 48`
24
+
25
+ ## Run planning:
26
+ `nnUNetv2_plan_experiment -d 119 -pl nnUNetPlannerResEncL_torchres`
27
+
28
+ This planner not only uses the ResEncL configuration but also replaces the default resampling scheme with one that is
29
+ faster (but less precise). Since all images in the challenge (train and test) should already have 0.3x0.3x0.3 spacing
30
+ resampling is not required. This is just here as a safety measure. The speed is needed at inference time because grand
31
+ challenge imposes a limit of 10 minutes per case.
32
+
33
+ ## Edit the plans files
34
+ Add the following configuration to the generated plans file:
35
+
36
+ ```json
37
+ "3d_fullres_torchres_ps160x320x320_bs2": {
38
+ "inherits_from": "3d_fullres",
39
+ "data_identifier": "nnUNetPlans_3d_fullres_torchres_ctnorm",
40
+ "patch_size": [
41
+ 160,
42
+ 320,
43
+ 320
44
+ ],
45
+ "normalization_schemes": [
46
+ "CTNormalization"
47
+ ],
48
+ "architecture": {
49
+ "network_class_name": "dynamic_network_architectures.architectures.unet.ResidualEncoderUNet",
50
+ "arch_kwargs": {
51
+ "n_stages": 7,
52
+ "features_per_stage": [
53
+ 32,
54
+ 64,
55
+ 128,
56
+ 256,
57
+ 320,
58
+ 320,
59
+ 320
60
+ ],
61
+ "conv_op": "torch.nn.modules.conv.Conv3d",
62
+ "kernel_sizes": [
63
+ [
64
+ 3,
65
+ 3,
66
+ 3
67
+ ],
68
+ [
69
+ 3,
70
+ 3,
71
+ 3
72
+ ],
73
+ [
74
+ 3,
75
+ 3,
76
+ 3
77
+ ],
78
+ [
79
+ 3,
80
+ 3,
81
+ 3
82
+ ],
83
+ [
84
+ 3,
85
+ 3,
86
+ 3
87
+ ],
88
+ [
89
+ 3,
90
+ 3,
91
+ 3
92
+ ],
93
+ [
94
+ 3,
95
+ 3,
96
+ 3
97
+ ]
98
+ ],
99
+ "strides": [
100
+ [
101
+ 1,
102
+ 1,
103
+ 1
104
+ ],
105
+ [
106
+ 2,
107
+ 2,
108
+ 2
109
+ ],
110
+ [
111
+ 2,
112
+ 2,
113
+ 2
114
+ ],
115
+ [
116
+ 2,
117
+ 2,
118
+ 2
119
+ ],
120
+ [
121
+ 2,
122
+ 2,
123
+ 2
124
+ ],
125
+ [
126
+ 2,
127
+ 2,
128
+ 2
129
+ ],
130
+ [
131
+ 1,
132
+ 2,
133
+ 2
134
+ ]
135
+ ],
136
+ "n_blocks_per_stage": [
137
+ 1,
138
+ 3,
139
+ 4,
140
+ 6,
141
+ 6,
142
+ 6,
143
+ 6
144
+ ],
145
+ "n_conv_per_stage_decoder": [
146
+ 1,
147
+ 1,
148
+ 1,
149
+ 1,
150
+ 1,
151
+ 1
152
+ ],
153
+ "conv_bias": true,
154
+ "norm_op": "torch.nn.modules.instancenorm.InstanceNorm3d",
155
+ "norm_op_kwargs": {
156
+ "eps": 1e-05,
157
+ "affine": true
158
+ },
159
+ "dropout_op": null,
160
+ "dropout_op_kwargs": null,
161
+ "nonlin": "torch.nn.LeakyReLU",
162
+ "nonlin_kwargs": {
163
+ "inplace": true
164
+ }
165
+ },
166
+ "_kw_requires_import": [
167
+ "conv_op",
168
+ "norm_op",
169
+ "dropout_op",
170
+ "nonlin"
171
+ ]
172
+ }
173
+ }
174
+ ```
175
+ Aside from changing the patch size this makes the architecture one stage deeper (one more pooling + res blocks), enabling
176
+ it to make effective use of the larger input
177
+
178
+ # Preprocessing
179
+ `nnUNetv2_preprocess -d 119 -c 3d_fullres_torchres_ps160x320x320_bs2 -plans_name nnUNetResEncUNetLPlans_torchres -np 48`
180
+
181
+ # Training
182
+ We train two models on all training cases:
183
+
184
+ ```bash
185
+ nnUNetv2_train 119 3d_fullres_torchres_ps160x320x320_bs2 all -p nnUNetResEncUNetLPlans_torchres -tr nnUNetTrainer_onlyMirror01_1500ep
186
+ nnUNet_results=${nnUNet_results}_2 nnUNetv2_train 119 3d_fullres_torchres_ps160x320x320_bs2 all -p nnUNetResEncUNetLPlans_torchres -tr nnUNetTrainer_onlyMirror01_1500ep
187
+ ```
188
+ Models are trained from scratch.
189
+
190
+ Note how in the second line we overwrite the nnUNet_results variable in order to be able to train the same model twice without overwriting the results
191
+
192
+ We recommend to increase the number of processes used for data augmentation. Otherwise you can run into CPU bottlenecks.
193
+ Use `export nnUNet_n_proc_DA=32` or higher (if your system permits!).
194
+
195
+ # Inference
196
+ We ensemble the two models from above. On a technical level we copy the two fold_all folders into one training output
197
+ directory and rename them to fold_0 and fold_1. This lets us use nnU-Net's cross-validation ensembling strategy which
198
+ is more computationally efficient (needed for time limit on grand-challenge.org).
199
+
200
+ Run inference with the [inference script](inference_script_semseg_only_customInf2.py)
201
+
202
+ # Postprocessing
203
+ If the prediction of a class on some test case is smaller than the corresponding cutoff size then it is removed
204
+ (replaced with background).
205
+
206
+ Cutoff values were optimized using a five-fold cross-validation on the Toothfairy2 training data. We optimize HD95 and Dice separately.
207
+ The final cutoff for each class is then the smaller value between the two metrics. You can find our volume cutoffs in the inference
208
+ script as part of our `postprocess` function.
nnUNet/documentation/competitions/__init__.py ADDED
File without changes
nnUNet/documentation/convert_msd_dataset.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ Use `nnUNetv2_convert_MSD_dataset`.
2
+
3
+ Read `nnUNetv2_convert_MSD_dataset -h` for usage instructions.
nnUNet/documentation/dataset_format.md ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # nnU-Net dataset format
2
+ The only way to bring your data into nnU-Net is by storing it in a specific format. Due to nnU-Net's roots in the
3
+ [Medical Segmentation Decathlon](http://medicaldecathlon.com/) (MSD), its dataset is heavily inspired but has since
4
+ diverged (see also [here](#how-to-use-decathlon-datasets)) from the format used in the MSD.
5
+
6
+ Datasets consist of three components: raw images, corresponding segmentation maps and a dataset.json file specifying
7
+ some metadata.
8
+
9
+ If you are migrating from nnU-Net v1, read [this](#how-to-use-nnu-net-v1-tasks) to convert your existing Tasks.
10
+
11
+
12
+ ## What do training cases look like?
13
+ Each training case is associated with an identifier = a unique name for that case. This identifier is used by nnU-Net to
14
+ connect images with the correct segmentation.
15
+
16
+ A training case consists of images and their corresponding segmentation.
17
+
18
+ **Images** is plural because nnU-Net supports arbitrarily many input channels. In order to be as flexible as possible,
19
+ nnU-net requires each input channel to be stored in a separate image (with the sole exception being RGB natural
20
+ images). So these images could for example be a T1 and a T2 MRI (or whatever else you want). The different input
21
+ channels MUST have the same geometry (same shape, spacing (if applicable) etc.) and
22
+ must be co-registered (if applicable). Input channels are identified by nnU-Net by their FILE_ENDING: a four-digit integer at the end
23
+ of the filename. Image files must therefore follow the following naming convention: {CASE_IDENTIFIER}_{XXXX}.{FILE_ENDING}.
24
+ Hereby, XXXX is the 4-digit modality/channel identifier (should be unique for each modality/channel, e.g., β€œ0000” for T1, β€œ0001” for
25
+ T2 MRI, …) and FILE_ENDING is the file extension used by your image format (.png, .nii.gz, ...). See below for concrete examples.
26
+ The dataset.json file connects channel names with the channel identifiers in the 'channel_names' key (see below for details).
27
+
28
+ Side note: Typically, each channel/modality needs to be stored in a separate file and is accessed with the XXXX channel identifier.
29
+ Exception are natural images (RGB; .png) where the three color channels can all be stored in one file (see the
30
+ [road segmentation](../nnunetv2/dataset_conversion/Dataset120_RoadSegmentation.py) dataset as an example).
31
+
32
+ **Segmentations** must share the same geometry with their corresponding images (same shape etc.). Segmentations are
33
+ integer maps with each value representing a semantic class. The background must be 0. If there is no background, then
34
+ do not use the label 0 for something else! Integer values of your semantic classes must be consecutive (0, 1, 2, 3,
35
+ ...). Of course, not all labels have to be present in each training case. Segmentations are saved as {CASE_IDENTIFER}.{FILE_ENDING} .
36
+
37
+ Within a training case, all image geometries (input channels, corresponding segmentation) must match. Between training
38
+ cases, they can of course differ. nnU-Net takes care of that.
39
+
40
+ Important: The input channels must be consistent! Concretely, **all images need the same input channels in the same
41
+ order and all input channels have to be present every time**. This is also true for inference!
42
+
43
+
44
+ ## Supported file formats
45
+ nnU-Net expects the same file format for images and segmentations! These will also be used for inference. For now, it
46
+ is thus not possible to train .png and then run inference on .jpg.
47
+
48
+ One big change in nnU-Net V2 is the support of multiple input file types. Gone are the days of converting everything to .nii.gz!
49
+ This is implemented by abstracting the input and output of images + segmentations through `BaseReaderWriter`. nnU-Net
50
+ comes with a broad collection of Readers+Writers and you can even add your own to support your data format!
51
+ See [here](../nnunetv2/imageio/readme.md).
52
+
53
+ As a nice bonus, nnU-Net now also natively supports 2D input images and you no longer have to mess around with
54
+ conversions to pseudo 3D niftis. Yuck. That was disgusting.
55
+
56
+ Note that internally (for storing and accessing preprocessed images) nnU-Net will use its own file format, irrespective
57
+ of what the raw data was provided in! This is for performance reasons.
58
+
59
+
60
+ By default, the following file formats are supported:
61
+
62
+ - NaturalImage2DIO: .png, .bmp, .tif
63
+ - NibabelIO: .nii.gz, .nrrd, .mha
64
+ - NibabelIOWithReorient: .nii.gz, .nrrd, .mha. This reader will reorient images to RAS!
65
+ - SimpleITKIO: .nii.gz, .nrrd, .mha
66
+ - Tiff3DIO: .tif, .tiff. 3D tif images! Since TIF does not have a standardized way of storing spacing information,
67
+ nnU-Net expects each TIF file to be accompanied by an identically named .json file that contains this information (see
68
+ [here](#datasetjson)).
69
+
70
+ The file extension lists are not exhaustive and depend on what the backend supports. For example, nibabel and SimpleITK
71
+ support more than the three given here. The file endings given here are just the ones we tested!
72
+
73
+ IMPORTANT: nnU-Net can only be used with file formats that use lossless (or no) compression! Because the file
74
+ format is defined for an entire dataset (and not separately for images and segmentations, this could be a todo for
75
+ the future), we must ensure that there are no compression artifacts that destroy the segmentation maps. So no .jpg and
76
+ the likes!
77
+
78
+ ## Dataset folder structure
79
+ Datasets must be located in the `nnUNet_raw` folder (which you either define when installing nnU-Net or export/set every
80
+ time you intend to run nnU-Net commands!).
81
+ Each segmentation dataset is stored as a separate 'Dataset'. Datasets are associated with a dataset ID, a three digit
82
+ integer, and a dataset name (which you can freely choose): For example, Dataset005_Prostate has 'Prostate' as dataset name and
83
+ the dataset id is 5. Datasets are stored in the `nnUNet_raw` folder like this:
84
+
85
+ nnUNet_raw/
86
+ β”œβ”€β”€ Dataset001_BrainTumour
87
+ β”œβ”€β”€ Dataset002_Heart
88
+ β”œβ”€β”€ Dataset003_Liver
89
+ β”œβ”€β”€ Dataset004_Hippocampus
90
+ β”œβ”€β”€ Dataset005_Prostate
91
+ β”œβ”€β”€ ...
92
+
93
+ Within each dataset folder, the following structure is expected:
94
+
95
+ Dataset001_BrainTumour/
96
+ β”œβ”€β”€ dataset.json
97
+ β”œβ”€β”€ imagesTr
98
+ β”œβ”€β”€ imagesTs # optional
99
+ └── labelsTr
100
+
101
+
102
+ When adding your custom dataset, take a look at the [dataset_conversion](../nnunetv2/dataset_conversion) folder and
103
+ pick an id that is not already taken. IDs 001-010 are for the Medical Segmentation Decathlon.
104
+
105
+ - **imagesTr** contains the images belonging to the training cases. nnU-Net will perform pipeline configuration, training with
106
+ cross-validation, as well as finding postprocessing and the best ensemble using this data.
107
+ - **imagesTs** (optional) contains the images that belong to the test cases. nnU-Net does not use them! This could just
108
+ be a convenient location for you to store these images. Remnant of the Medical Segmentation Decathlon folder structure.
109
+ - **labelsTr** contains the images with the ground truth segmentation maps for the training cases.
110
+ - **dataset.json** contains metadata of the dataset.
111
+
112
+ The scheme introduced [above](#what-do-training-cases-look-like) results in the following folder structure. Given
113
+ is an example for the first Dataset of the MSD: BrainTumour. This dataset hat four input channels: FLAIR (0000),
114
+ T1w (0001), T1gd (0002) and T2w (0003). Note that the imagesTs folder is optional and does not have to be present.
115
+
116
+ nnUNet_raw/Dataset001_BrainTumour/
117
+ β”œβ”€β”€ dataset.json
118
+ β”œβ”€β”€ imagesTr
119
+ β”‚Β Β  β”œβ”€β”€ BRATS_001_0000.nii.gz
120
+ β”‚Β Β  β”œβ”€β”€ BRATS_001_0001.nii.gz
121
+ β”‚Β Β  β”œβ”€β”€ BRATS_001_0002.nii.gz
122
+ β”‚Β Β  β”œβ”€β”€ BRATS_001_0003.nii.gz
123
+ β”‚Β Β  β”œβ”€β”€ BRATS_002_0000.nii.gz
124
+ β”‚Β Β  β”œβ”€β”€ BRATS_002_0001.nii.gz
125
+ β”‚Β Β  β”œβ”€β”€ BRATS_002_0002.nii.gz
126
+ β”‚Β Β  β”œβ”€β”€ BRATS_002_0003.nii.gz
127
+ β”‚Β Β  β”œβ”€β”€ ...
128
+ β”œβ”€β”€ imagesTs
129
+ β”‚Β Β  β”œβ”€β”€ BRATS_485_0000.nii.gz
130
+ β”‚Β Β  β”œβ”€β”€ BRATS_485_0001.nii.gz
131
+ β”‚Β Β  β”œβ”€β”€ BRATS_485_0002.nii.gz
132
+ β”‚Β Β  β”œβ”€β”€ BRATS_485_0003.nii.gz
133
+ β”‚Β Β  β”œβ”€β”€ BRATS_486_0000.nii.gz
134
+ β”‚Β Β  β”œβ”€β”€ BRATS_486_0001.nii.gz
135
+ β”‚Β Β  β”œβ”€β”€ BRATS_486_0002.nii.gz
136
+ β”‚Β Β  β”œβ”€β”€ BRATS_486_0003.nii.gz
137
+ β”‚Β Β  β”œβ”€β”€ ...
138
+ └── labelsTr
139
+ β”œβ”€β”€ BRATS_001.nii.gz
140
+ β”œβ”€β”€ BRATS_002.nii.gz
141
+ β”œβ”€β”€ ...
142
+
143
+ Here is another example of the second dataset of the MSD, which has only one input channel:
144
+
145
+ nnUNet_raw/Dataset002_Heart/
146
+ β”œβ”€β”€ dataset.json
147
+ β”œβ”€β”€ imagesTr
148
+ β”‚Β Β  β”œβ”€β”€ la_003_0000.nii.gz
149
+ β”‚Β Β  β”œβ”€β”€ la_004_0000.nii.gz
150
+ β”‚Β Β  β”œβ”€β”€ ...
151
+ β”œβ”€β”€ imagesTs
152
+ β”‚Β Β  β”œβ”€β”€ la_001_0000.nii.gz
153
+ β”‚Β Β  β”œβ”€β”€ la_002_0000.nii.gz
154
+ β”‚Β Β  β”œβ”€β”€ ...
155
+ └── labelsTr
156
+ β”œβ”€β”€ la_003.nii.gz
157
+ β”œβ”€β”€ la_004.nii.gz
158
+ β”œβ”€β”€ ...
159
+
160
+ Remember: For each training case, all images must have the same geometry to ensure that their pixel arrays are aligned. Also
161
+ make sure that all your data is co-registered!
162
+
163
+ See also [dataset format inference](dataset_format_inference.md)!!
164
+
165
+ ## dataset.json
166
+ The dataset.json contains metadata that nnU-Net needs for training. We have greatly reduced the number of required
167
+ fields since version 1!
168
+
169
+ Here is what the dataset.json should look like at the example of the Dataset005_Prostate from the MSD:
170
+
171
+ {
172
+ "channel_names": { # formerly modalities
173
+ "0": "T2",
174
+ "1": "ADC"
175
+ },
176
+ "labels": { # THIS IS DIFFERENT NOW!
177
+ "background": 0,
178
+ "PZ": 1,
179
+ "TZ": 2
180
+ },
181
+ "numTraining": 32,
182
+ "file_ending": ".nii.gz"
183
+ "overwrite_image_reader_writer": "SimpleITKIO" # optional! If not provided nnU-Net will automatically determine the ReaderWriter
184
+ }
185
+
186
+ The channel_names determine the normalization used by nnU-Net. If a channel is marked as 'CT', then a global
187
+ normalization based on the intensities in the foreground pixels will be used. If it is something else, per-channel
188
+ z-scoring will be used. Refer to the methods section in [our paper](https://www.nature.com/articles/s41592-020-01008-z)
189
+ for more details. nnU-Net v2 introduces a few more normalization schemes to
190
+ choose from and allows you to define your own, see [here](explanation_normalization.md) for more information.
191
+
192
+ Important changes relative to nnU-Net v1:
193
+ - "modality" is now called "channel_names" to remove strong bias to medical images
194
+ - labels are structured differently (name -> int instead of int -> name). This was needed to support [region-based training](region_based_training.md)
195
+ - "file_ending" is added to support different input file types
196
+ - "overwrite_image_reader_writer" optional! Can be used to specify a certain (custom) ReaderWriter class that should
197
+ be used with this dataset. If not provided, nnU-Net will automatically determine the ReaderWriter
198
+ - "regions_class_order" only used in [region-based training](region_based_training.md)
199
+
200
+ There is a utility with which you can generate the dataset.json automatically. You can find it
201
+ [here](../nnunetv2/dataset_conversion/generate_dataset_json.py).
202
+ See our examples in [dataset_conversion](../nnunetv2/dataset_conversion) for how to use it. And read its documentation!
203
+
204
+ As described above, a json file that contains spacing information is required for TIFF files.
205
+ An example for a 3D TIFF stack with units corresponding to 7.6 in x and y, 80 in z is:
206
+
207
+ ```
208
+ {
209
+ "spacing": [7.6, 7.6, 80.0]
210
+ }
211
+ ```
212
+
213
+ Within the dataset folder, this file (named `cell6.json` in this example) would be placed in the following folders:
214
+
215
+ nnUNet_raw/Dataset123_Foo/
216
+ β”œβ”€β”€ dataset.json
217
+ β”œβ”€β”€ imagesTr
218
+ β”‚Β Β  β”œβ”€β”€ cell6.json
219
+ β”‚Β Β  └── cell6_0000.tif
220
+ └── labelsTr
221
+ β”œβ”€β”€ cell6.json
222
+ └── cell6.tif
223
+
224
+
225
+ ## How to use nnU-Net v1 Tasks
226
+ If you are migrating from the old nnU-Net, convert your existing datasets with `nnUNetv2_convert_old_nnUNet_dataset`!
227
+
228
+ Example for migrating a nnU-Net v1 Task:
229
+ ```bash
230
+ nnUNetv2_convert_old_nnUNet_dataset /media/isensee/raw_data/nnUNet_raw_data_base/nnUNet_raw_data/Task027_ACDC Dataset027_ACDC
231
+ ```
232
+ Use `nnUNetv2_convert_old_nnUNet_dataset -h` for detailed usage instructions.
233
+
234
+
235
+ ## How to use decathlon datasets
236
+ See [convert_msd_dataset.md](convert_msd_dataset.md)
237
+
238
+ ## How to use 2D data with nnU-Net
239
+ 2D is now natively supported (yay!). See [here](#supported-file-formats) as well as the example dataset in this
240
+ [script](../nnunetv2/dataset_conversion/Dataset120_RoadSegmentation.py).
241
+
242
+
243
+ ## How to update an existing dataset
244
+ When updating a dataset it is best practice to remove the preprocessed data in `nnUNet_preprocessed/DatasetXXX_NAME`
245
+ to ensure a fresh start. Then replace the data in `nnUNet_raw` and rerun `nnUNetv2_plan_and_preprocess`. Optionally,
246
+ also remove the results from old trainings.
247
+
248
+ # Example dataset conversion scripts
249
+ In the `dataset_conversion` folder (see [here](../nnunetv2/dataset_conversion)) are multiple example scripts for
250
+ converting datasets into nnU-Net format. These scripts cannot be run as they are (you need to open them and change
251
+ some paths) but they are excellent examples for you to learn how to convert your own datasets into nnU-Net format.
252
+ Just pick the dataset that is closest to yours as a starting point.
253
+ The list of dataset conversion scripts is continually updated. If you find that some publicly available dataset is
254
+ missing, feel free to open a PR to add it!
nnUNet/documentation/dataset_format_inference.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data format for Inference
2
+ Read the documentation on the overall [data format](dataset_format.md) first!
3
+
4
+ The data format for inference must match the one used for the raw data (**specifically, the images must be in exactly
5
+ the same format as in the imagesTr folder**). As before, the filenames must start with a
6
+ unique identifier, followed by a 4-digit modality identifier. Here is an example for two different datasets:
7
+
8
+ 1) Task005_Prostate:
9
+
10
+ This task has 2 modalities, so the files in the input folder must look like this:
11
+
12
+ input_folder
13
+ β”œβ”€β”€ prostate_03_0000.nii.gz
14
+ β”œβ”€β”€ prostate_03_0001.nii.gz
15
+ β”œβ”€β”€ prostate_05_0000.nii.gz
16
+ β”œβ”€β”€ prostate_05_0001.nii.gz
17
+ β”œβ”€β”€ prostate_08_0000.nii.gz
18
+ β”œβ”€β”€ prostate_08_0001.nii.gz
19
+ β”œβ”€β”€ ...
20
+
21
+ _0000 has to be the T2 image and _0001 has to be the ADC image (as specified by 'channel_names' in the
22
+ dataset.json), exactly the same as was used for training.
23
+
24
+ 2) Task002_Heart:
25
+
26
+ imagesTs
27
+ β”œβ”€β”€ la_001_0000.nii.gz
28
+ β”œβ”€β”€ la_002_0000.nii.gz
29
+ β”œβ”€β”€ la_006_0000.nii.gz
30
+ β”œβ”€β”€ ...
31
+
32
+ Task002 only has one modality, so each case only has one _0000.nii.gz file.
33
+
34
+
35
+ The segmentations in the output folder will be named {CASE_IDENTIFIER}.nii.gz (omitting the modality identifier).
36
+
37
+ Remember that the file format used for inference (.nii.gz in this example) must be the same as was used for training
38
+ (and as was specified in 'file_ending' in the dataset.json)!
39
+
nnUNet/documentation/explanation_logging.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logging in nnU-Net v2
2
+
3
+ ## Introduction
4
+
5
+ Logging in nnU-Net is intentionally simple and centralized in
6
+ `nnunetv2/training/logging/nnunet_logger.py`.
7
+
8
+ The trainer talks to one object, `MetaLogger`, and `MetaLogger` fans out logs to:
9
+
10
+ - `LocalLogger` (always enabled): the source of truth for training curves, checkpoint logging state, and `progress.png`
11
+ - optional external loggers (currently `WandbLogger`)
12
+
13
+ This keeps training code clean while still allowing external tracking backends.
14
+
15
+ ## Default behaviour
16
+
17
+ Without any setup, nnU-Net uses only `LocalLogger`.
18
+
19
+ Per epoch, it stores:
20
+
21
+ - `mean_fg_dice` and `ema_fg_dice` (EMA is computed automatically)
22
+ - `dice_per_class_or_region`
23
+ - `train_losses`, `val_losses`
24
+ - `lrs`
25
+ - `epoch_start_timestamps`, `epoch_end_timestamps`
26
+
27
+ From these values, `progress.png` is updated in the fold output folder.
28
+ On checkpoint save/load, the local logging state is also saved/restored, so curves continue correctly after resume.
29
+
30
+ ## How to enable W&B
31
+
32
+ 1. Install W&B:
33
+
34
+ ```bash
35
+ pip install wandb
36
+ ```
37
+
38
+ 2. Enable the backend via environment variables:
39
+
40
+ ```bash
41
+ export nnUNet_wandb_enabled=1
42
+ export nnUNet_wandb_project=nnunet
43
+ export nnUNet_wandb_mode=online # or offline
44
+ ```
45
+
46
+ 3. Run training normally:
47
+
48
+ ```bash
49
+ nnUNetv2_train DATASET_NAME_OR_ID 3d_fullres 0
50
+ ```
51
+
52
+ Notes:
53
+
54
+ - `nnUNet_wandb_enabled` accepts `0/1` and `false/true` (case-insensitive). Other values raise an error.
55
+ - When resuming (`--c`), W&B resume metadata in `fold_x/wandb/latest-run` is reused and duplicate older steps are skipped.
56
+
57
+ ## How to integrate a custom logger
58
+
59
+ Add a new logger class with the same minimal interface used by `MetaLogger`:
60
+
61
+ - `update_config(self, config: dict)`
62
+ - `log(self, key, value, step: int)`
63
+ - `log_summary(self, key, value)`
64
+
65
+ Example skeleton:
66
+
67
+ ```python
68
+ class MyLogger:
69
+ def __init__(self, output_folder, resume):
70
+ self.output_folder = output_folder
71
+ self.resume = resume
72
+
73
+ def update_config(self, config: dict):
74
+ ...
75
+
76
+ def log(self, key, value, step: int):
77
+ ...
78
+
79
+ def log_summary(self, key, value):
80
+ ...
81
+ ```
82
+
83
+ Then register it in `MetaLogger.__init__` (for example behind an env var switch), similar to how `WandbLogger` is added.
84
+
85
+ Important integration detail:
86
+
87
+ - `MetaLogger.log(...)` always writes to `LocalLogger` first.
88
+ - If you introduce a brand-new per-epoch key, also add that key to `LocalLogger.my_fantastic_logging`, otherwise the local assertion will fail.
nnUNet/documentation/explanation_normalization.md ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Intensity normalization in nnU-Net
2
+
3
+ The type of intensity normalization applied in nnU-Net can be controlled via the `channel_names` (former `modalities`)
4
+ entry in the dataset.json. Just like the old nnU-Net, per-channel z-scoring as well as dataset-wide z-scoring based on
5
+ foreground intensities are supported. However, there have been a few additions as well.
6
+
7
+ Reminder: The `channel_names` entry typically looks like this:
8
+
9
+ "channel_names": {
10
+ "0": "T2",
11
+ "1": "ADC"
12
+ },
13
+
14
+ It has as many entries as there are input channels for the given dataset.
15
+
16
+ To tell you a secret, nnU-Net does not really care what your channels are called. We just use this to determine what normalization
17
+ scheme will be used for the given dataset. nnU-Net requires you to specify a normalization strategy for each of your input channels!
18
+ If you enter a channel name that is not in the following list, the default (`zscore`) will be used.
19
+
20
+ Here is a list of currently available normalization schemes:
21
+
22
+ - `CT`: Perform CT normalization. Specifically, collect intensity values from the foreground classes (all but the
23
+ background and ignore) from all training cases, compute the mean, standard deviation as well as the 0.5 and
24
+ 99.5 percentile of the values. Then clip to the percentiles, followed by subtraction of the mean and division with the
25
+ standard deviation. The normalization that is applied is the same for each training case (for this input channel).
26
+ The values used by nnU-Net for normalization are stored in the `foreground_intensity_properties_per_channel` entry in the
27
+ corresponding plans file. This normalization is suitable for modalities presenting physical quantities such as CT
28
+ images and ADC maps.
29
+ - `noNorm` : do not perform any normalization at all
30
+ - `rescale_to_0_1`: rescale the intensities to [0, 1]
31
+ - `rgb_to_0_1`: assumes uint8 inputs. Divides by 255 to rescale uint8 to [0, 1]
32
+ - `zscore`/anything else: perform z-scoring (subtract mean and standard deviation) separately for each train case
33
+
34
+ **Important:** The nnU-Net default is to perform 'CT' normalization for CT images and 'zscore' for everything else! If
35
+ you deviate from that path, make sure to benchmark whether that actually improves results!
36
+
37
+ # How to implement custom normalization strategies?
38
+ - Head over to nnunetv2/preprocessing/normalization
39
+ - implement a new image normalization class by deriving from ImageNormalization
40
+ - register it in nnunetv2/preprocessing/normalization/map_channel_name_to_normalization.py:channel_name_to_normalization_mapping.
41
+ This is where you specify a channel name that should be associated with it
42
+ - use it by specifying the correct channel_name
43
+
44
+ Normalization can only be applied to one channel at a time. There is currently no way of implementing a normalization scheme
45
+ that gets multiple channels as input to be used jointly!
nnUNet/documentation/explanation_plans_files.md ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Modifying the nnU-Net Configurations
2
+
3
+ nnU-Net provides unprecedented out-of-the-box segmentation performance for essentially any dataset we have evaluated
4
+ it on. That said, there is always room for improvements. A fool-proof strategy for squeezing out the last bit of
5
+ performance is to start with the default nnU-Net, and then further tune it manually to a concrete dataset at hand.
6
+ **This guide is about changes to the nnU-Net configuration you can make via the plans files. It does not cover code
7
+ extensions of nnU-Net. For that, take a look [here](extending_nnunet.md)**
8
+
9
+ In nnU-Net V2, plans files are SO MUCH MORE powerful than they were in v1. There are a lot more knobs that you can
10
+ turn without resorting to hacky solutions or even having to touch the nnU-Net code at all! And as an added bonus:
11
+ plans files are now also .json files and no longer require users to fiddle with pickle. Just open them in your text
12
+ editor of choice!
13
+
14
+ If overwhelmed, look at our [Examples](#examples)!
15
+
16
+ # plans.json structure
17
+
18
+ Plans have global and local settings. Global settings are applied to all configurations in that plans file while
19
+ local settings are attached to a specific configuration.
20
+
21
+ ## Global settings
22
+
23
+ - `foreground_intensity_properties_by_modality`: Intensity statistics of the foreground regions (all labels except
24
+ background and ignore label), computed over all training cases. Used by [CT normalization scheme](explanation_normalization.md).
25
+ - `image_reader_writer`: Name of the image reader/writer class that should be used with this dataset. You might want
26
+ to change this if, for example, you would like to run inference with files that have a different file format. The
27
+ class that is named here must be located in nnunetv2.imageio!
28
+ - `label_manager`: The name of the class that does label handling. Take a look at
29
+ nnunetv2.utilities.label_handling.LabelManager to see what it does. If you decide to change it, place your version
30
+ in nnunetv2.utilities.label_handling!
31
+ - `transpose_forward`: nnU-Net transposes the input data so that the axes with the highest resolution (lowest spacing)
32
+ come last. This is because the 2D U-Net operates on the trailing dimensions (more efficient slicing due to internal
33
+ memory layout of arrays). Future work might move this setting to affect only individual configurations.
34
+ - transpose_backward is what numpy.transpose gets as new axis ordering.
35
+ - `transpose_backward`: the axis ordering that inverts "transpose_forward"
36
+ - \[`original_median_shape_after_transp`\]: just here for your information
37
+ - \[`original_median_spacing_after_transp`\]: just here for your information
38
+ - \[`plans_name`\]: do not change. Used internally
39
+ - \[`experiment_planner_used`\]: just here as metadata so that we know what planner originally generated this file
40
+ - \[`dataset_name`\]: do not change. This is the dataset these plans are intended for
41
+
42
+ ## Local settings
43
+ Plans also have a `configurations` key in which the actual configurations are stored. `configurations` are again a
44
+ dictionary, where the keys are the configuration names and the values are the local settings for each configuration.
45
+
46
+ To better understand the components describing the network topology in our plans files, please read section 6.2
47
+ in the [supplementary information](https://static-content.springer.com/esm/art%3A10.1038%2Fs41592-020-01008-z/MediaObjects/41592_2020_1008_MOESM1_ESM.pdf)
48
+ (page 13) of our paper!
49
+
50
+ Local settings:
51
+ - `spacing`: the target spacing used in this configuration
52
+ - `patch_size`: the patch size used for training this configuration
53
+ - `data_identifier`: the preprocessed data for this configuration will be saved in
54
+ nnUNet_preprocessed/DATASET_NAME/_data_identifier_. If you add a new configuration, remember to set a unique
55
+ data_identifier in order to not create conflicts with other configurations (unless you plan to reuse the data from
56
+ another configuration, for example as is done in the cascade)
57
+ - `batch_size`: batch size used for training
58
+ - `batch_dice`: whether to use batch dice (pretend all samples in the batch are one image, compute dice loss over that)
59
+ or not (each sample in the batch is a separate image, compute dice loss for each sample and average over samples)
60
+ - `preprocessor_name`: Name of the preprocessor class used for running preprocessing. Class must be located in
61
+ nnunetv2.preprocessing.preprocessors
62
+ - `use_mask_for_norm`: whether to use the nonzero mask for normalization or not (relevant for BraTS and the like,
63
+ probably False for all other datasets). Interacts with ImageNormalization class
64
+ - `normalization_schemes`: mapping of channel identifier to ImageNormalization class name. ImageNormalization
65
+ classes must be located in nnunetv2.preprocessing.normalization. Also see [here](explanation_normalization.md)
66
+ - `resampling_fn_data`: name of resampling function to be used for resizing image data. resampling function must be
67
+ callable(data, current_spacing, new_spacing, **kwargs). It must be located in nnunetv2.preprocessing.resampling
68
+ - `resampling_fn_data_kwargs`: kwargs for resampling_fn_data
69
+ - `resampling_fn_probabilities`: name of resampling function to be used for resizing predicted class probabilities/logits.
70
+ resampling function must be `callable(data: Union[np.ndarray, torch.Tensor], current_spacing, new_spacing, **kwargs)`. It must be located in
71
+ nnunetv2.preprocessing.resampling
72
+ - `resampling_fn_probabilities_kwargs`: kwargs for resampling_fn_probabilities
73
+ - `resampling_fn_seg`: name of resampling function to be used for resizing segmentation maps (integer: 0, 1, 2, 3, etc).
74
+ resampling function must be callable(data, current_spacing, new_spacing, **kwargs). It must be located in
75
+ nnunetv2.preprocessing.resampling
76
+ - `resampling_fn_seg_kwargs`: kwargs for resampling_fn_seg
77
+ - `network_arch_class_name`: UNet class name, can be used to integrate custom dynamic architectures
78
+ - `UNet_base_num_features`: The number of starting features for the UNet architecture. Default is 32. Default: Features
79
+ are doubled with each downsampling
80
+ - `unet_max_num_features`: Maximum number of features (default: capped at 320 for 3D and 512 for 2d). The purpose is to
81
+ prevent parameters from exploding too much.
82
+ - `conv_kernel_sizes`: the convolutional kernel sizes used by nnU-Net in each stage of the encoder. The decoder
83
+ mirrors the encoder and is therefore not explicitly listed here! The list is as long as `n_conv_per_stage_encoder` has
84
+ entries
85
+ - `n_conv_per_stage_encoder`: number of convolutions used per stage (=at a feature map resolution in the encoder) in the encoder.
86
+ Default is 2. The list has as many entries as the encoder has stages
87
+ - `n_conv_per_stage_decoder`: number of convolutions used per stage in the decoder. Also see `n_conv_per_stage_encoder`
88
+ - `num_pool_per_axis`: number of times each of the spatial axes is pooled in the network. Needed to know how to pad
89
+ image sizes during inference (num_pool = 5 means input must be divisible by 2**5=32)
90
+ - `pool_op_kernel_sizes`: the pooling kernel sizes (and at the same time strides) for each stage of the encoder
91
+ - \[`median_image_size_in_voxels`\]: the median size of the images of the training set at the current target spacing.
92
+ Do not modify this as this is not used. It is just here for your information.
93
+
94
+ Special local settings:
95
+ - `inherits_from`: configurations can inherit from each other. This makes it easy to add new configurations that only
96
+ differ in a few local settings from another. If using this, remember to set a new `data_identifier` (if needed)!
97
+ - `previous_stage`: if this configuration is part of a cascade, we need to know what the previous stage (for example
98
+ the low resolution configuration) was. This needs to be specified here.
99
+ - `next_stage`: if this configuration is part of a cascade, we need to know what possible subsequent stages are! This
100
+ is because we need to export predictions in the correct spacing when running the validation. `next_stage` can either
101
+ be a string or a list of strings
102
+
103
+ # Examples
104
+
105
+ ## Increasing the batch size for large datasets
106
+ If your dataset is large the training can benefit from larger batch_sizes. To do this, simply create a new
107
+ configuration in the `configurations` dict
108
+
109
+ "configurations": {
110
+ "3d_fullres_bs40": {
111
+ "inherits_from": "3d_fullres",
112
+ "batch_size": 40
113
+ }
114
+ }
115
+
116
+ No need to change the data_identifier. `3d_fullres_bs40` will just use the preprocessed data from `3d_fullres`.
117
+ No need to rerun `nnUNetv2_preprocess` because we can use already existing data (if available) from `3d_fullres`.
118
+
119
+ ## Using custom preprocessors
120
+ If you would like to use a different preprocessor class then this can be specified as follows:
121
+
122
+ "configurations": {
123
+ "3d_fullres_my_preprocesor": {
124
+ "inherits_from": "3d_fullres",
125
+ "preprocessor_name": MY_PREPROCESSOR,
126
+ "data_identifier": "3d_fullres_my_preprocesor"
127
+ }
128
+ }
129
+
130
+ You need to run preprocessing for this new configuration:
131
+ `nnUNetv2_preprocess -d DATASET_ID -c 3d_fullres_my_preprocesor` because it changes the preprocessing. Remember to
132
+ set a unique `data_identifier` whenever you make modifications to the preprocessed data!
133
+
134
+ ## Change target spacing
135
+
136
+ "configurations": {
137
+ "3d_fullres_my_spacing": {
138
+ "inherits_from": "3d_fullres",
139
+ "spacing": [X, Y, Z],
140
+ "data_identifier": "3d_fullres_my_spacing"
141
+ }
142
+ }
143
+
144
+ You need to run preprocessing for this new configuration:
145
+ `nnUNetv2_preprocess -d DATASET_ID -c 3d_fullres_my_spacing` because it changes the preprocessing. Remember to
146
+ set a unique `data_identifier` whenever you make modifications to the preprocessed data!
147
+
148
+ ## Adding a cascade to a dataset where it does not exist
149
+ Hippocampus is small. It doesn't have a cascade. It also doesn't really make sense to add a cascade here but hey for
150
+ the sake of demonstration we can do that.
151
+ We change the following things here:
152
+
153
+ - `spacing`: The lowres stage should operate at a lower resolution
154
+ - we modify the `median_image_size_in_voxels` entry as a guide for what original image sizes we deal with
155
+ - we set some patch size that is inspired by `median_image_size_in_voxels`
156
+ - we need to remember that the patch size must be divisible by 2**num_pool in each axis!
157
+ - network parameters such as kernel sizes, pooling operations are changed accordingly
158
+ - we need to specify the name of the next stage
159
+ - we need to add the highres stage
160
+
161
+ This is how this would look like (comparisons with 3d_fullres given as reference):
162
+
163
+ "configurations": {
164
+ "3d_lowres": {
165
+ "inherits_from": "3d_fullres",
166
+ "data_identifier": "3d_lowres"
167
+ "spacing": [2.0, 2.0, 2.0], # from [1.0, 1.0, 1.0] in 3d_fullres
168
+ "median_image_size_in_voxels": [18, 25, 18], # from [36, 50, 35]
169
+ "patch_size": [20, 28, 20], # from [40, 56, 40]
170
+ "n_conv_per_stage_encoder": [2, 2, 2], # one less entry than 3d_fullres ([2, 2, 2, 2])
171
+ "n_conv_per_stage_decoder": [2, 2], # one less entry than 3d_fullres
172
+ "num_pool_per_axis": [2, 2, 2], # one less pooling than 3d_fullres in each dimension (3d_fullres: [3, 3, 3])
173
+ "pool_op_kernel_sizes": [[1, 1, 1], [2, 2, 2], [2, 2, 2]], # one less [2, 2, 2]
174
+ "conv_kernel_sizes": [[3, 3, 3], [3, 3, 3], [3, 3, 3]], # one less [3, 3, 3]
175
+ "next_stage": "3d_cascade_fullres" # name of the next stage in the cascade
176
+ },
177
+ "3d_cascade_fullres": { # does not need a data_identifier because we can use the data of 3d_fullres
178
+ "inherits_from": "3d_fullres",
179
+ "previous_stage": "3d_lowres" # name of the previous stage
180
+ }
181
+ }
182
+
183
+ To better understand the components describing the network topology in our plans files, please read section 6.2
184
+ in the [supplementary information](https://static-content.springer.com/esm/art%3A10.1038%2Fs41592-020-01008-z/MediaObjects/41592_2020_1008_MOESM1_ESM.pdf)
185
+ (page 13) of our paper!
nnUNet/documentation/extending_nnunet.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Extending nnU-Net
2
+ We hope that the new structure of nnU-Net v2 makes it much more intuitive on how to modify it! We cannot give an
3
+ extensive tutorial on how each and every bit of it can be modified. It is better for you to search for the position
4
+ in the repository where the thing you intend to change is implemented and start working your way through the code from
5
+ there. Setting breakpoints and debugging into nnU-Net really helps in understanding it and thus will help you make the
6
+ necessary modifications!
7
+
8
+ Here are some things you might want to read before you start:
9
+ - Editing nnU-Net configurations through plans files is really powerful now and allows you to change a lot of things regarding
10
+ preprocessing, resampling, network topology etc. Read [this](explanation_plans_files.md)!
11
+ - [Image normalization](explanation_normalization.md) and [i/o formats](dataset_format.md#supported-file-formats) are easy to extend!
12
+ - Manual data splits can be defined as described [here](manual_data_splits.md)
13
+ - You can chain arbitrary configurations together into cascades, see [this again](explanation_plans_files.md)
14
+ - Read about our support for [region-based training](region_based_training.md)
15
+ - If you intend to modify the training procedure (loss, sampling, data augmentation, lr scheduler, etc) then you need
16
+ to implement your own trainer class. Best practice is to create a class that inherits from nnUNetTrainer and
17
+ implements the necessary changes. Head over to our [trainer classes folder](../nnunetv2/training/nnUNetTrainer) for
18
+ inspiration! There will be similar trainers for what you intend to change and you can take them as a guide. nnUNetTrainer
19
+ are structured similarly to PyTorch lightning trainers, this should also make things easier!
20
+ - Integrating new network architectures can be done in two ways:
21
+ - Quick and dirty: implement a new nnUNetTrainer class and overwrite its `build_network_architecture` function.
22
+ Make sure your architecture is compatible with deep supervision (if not, use `nnUNetTrainerNoDeepSupervision`
23
+ as basis!) and that it can handle the patch sizes that are thrown at it! Your architecture should NOT apply any
24
+ nonlinearities at the end (softmax, sigmoid etc). nnU-Net does that!
25
+ - The 'proper' (but difficult) way: Build a dynamically configurable architecture such as the `PlainConvUNet` class
26
+ used by default. It needs to have some sort of GPU memory estimation method that can be used to evaluate whether
27
+ certain patch sizes and
28
+ topologies fit into a specified GPU memory target. Build a new `ExperimentPlanner` that can configure your new
29
+ class and communicate with its memory budget estimation. Run `nnUNetv2_plan_and_preprocess` while specifying your
30
+ custom `ExperimentPlanner` and a custom `plans_name`. Implement a nnUNetTrainer that can use the plans generated by
31
+ your `ExperimentPlanner` to instantiate the network architecture. Specify your plans and trainer when running `nnUNetv2_train`.
32
+ It always pays off to first read and understand the corresponding nnU-Net code and use it as a template for your implementation!
33
+ - Remember that multi-GPU training, region-based training, ignore label and cascaded training are now simply integrated
34
+ into one unified nnUNetTrainer class. No separate classes needed (remember that when implementing your own trainer
35
+ classes and ensure support for all of these features! Or raise `NotImplementedError`)
36
+
37
+ [//]: # (- Read about our support for [ignore label]&#40;ignore_label.md&#41; and [region-based training]&#40;region_based_training.md&#41;)
nnUNet/documentation/how_to_use_nnunet.md ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## **2024-04-18 UPDATE: New residual encoder UNet presets available!**
2
+ The recommended nnU-Net presets have changed! See [here](resenc_presets.md) how to unlock them!
3
+
4
+
5
+ ## How to run nnU-Net on a new dataset
6
+
7
+
8
+ Given some dataset, nnU-Net fully automatically configures an entire segmentation pipeline that matches its properties.
9
+ nnU-Net covers the entire pipeline, from preprocessing to model configuration, model training, postprocessing
10
+ all the way to ensembling. After running nnU-Net, the trained model(s) can be applied to the test cases for inference.
11
+
12
+ ### Dataset Format
13
+ nnU-Net expects datasets in a structured format. This format is inspired by the data structure of
14
+ the [Medical Segmentation Decthlon](http://medicaldecathlon.com/). Please read
15
+ [this](dataset_format.md) for information on how to set up datasets to be compatible with nnU-Net.
16
+
17
+ **Since version 2 we support multiple image file formats (.nii.gz, .png, .tif, ...)! Read the dataset_format
18
+ documentation to learn more!**
19
+
20
+ **Datasets from nnU-Net v1 can be converted to V2 by running `nnUNetv2_convert_old_nnUNet_dataset INPUT_FOLDER
21
+ OUTPUT_DATASET_NAME`.** Remember that v2 calls datasets DatasetXXX_Name (not Task) where XXX is a 3-digit number.
22
+ Please provide the **path** to the old task, not just the Task name. nnU-Net V2 doesn't know where v1 tasks were!
23
+
24
+ ### Experiment planning and preprocessing
25
+ Given a new dataset, nnU-Net will extract a dataset fingerprint (a set of dataset-specific properties such as
26
+ image sizes, voxel spacings, intensity information etc). This information is used to design three U-Net configurations.
27
+ Each of these pipelines operates on its own preprocessed version of the dataset.
28
+
29
+ The easiest way to run fingerprint extraction, experiment planning and preprocessing is to use:
30
+
31
+ ```bash
32
+ nnUNetv2_plan_and_preprocess -d DATASET_ID --verify_dataset_integrity
33
+ ```
34
+
35
+ Where `DATASET_ID` is the dataset id (duh). We recommend `--verify_dataset_integrity` whenever it's the first time
36
+ you run this command. This will check for some of the most common error sources! Fingerprint extraction and
37
+ preprocessing show a progress bar by default. Use `--no_pbar` if you want to suppress it, for example in
38
+ non-interactive cluster environments.
39
+
40
+ You can also process several datasets at once by giving `-d 1 2 3 [...]`. If you already know what U-Net configuration
41
+ you need you can also specify that with `-c 3d_fullres` (make sure to adapt -np in this case!). For more information
42
+ about all the options available to you please run `nnUNetv2_plan_and_preprocess -h`.
43
+
44
+ nnUNetv2_plan_and_preprocess will create a new subfolder in your nnUNet_preprocessed folder named after the dataset.
45
+ Once the command is completed there will be a dataset_fingerprint.json file as well as a nnUNetPlans.json file for you to look at
46
+ (in case you are interested!). There will also be subfolders containing the preprocessed data for your UNet configurations.
47
+
48
+ [Optional]
49
+ If you prefer to keep things separate, you can also use `nnUNetv2_extract_fingerprint`, `nnUNetv2_plan_experiment`
50
+ and `nnUNetv2_preprocess` (in that order). `nnUNetv2_extract_fingerprint` and `nnUNetv2_preprocess` also support
51
+ `--no_pbar`.
52
+
53
+ ### Model training
54
+ #### Overview
55
+ You pick which configurations (2d, 3d_fullres, 3d_lowres, 3d_cascade_fullres) should be trained! If you have no idea
56
+ what performs best on your data, just run all of them and let nnU-Net identify the best one. It's up to you!
57
+
58
+ nnU-Net trains all configurations in a 5-fold cross-validation over the training cases. This is 1) needed so that
59
+ nnU-Net can estimate the performance of each configuration and tell you which one should be used for your
60
+ segmentation problem and 2) a natural way of obtaining a good model ensemble (average the output of these 5 models
61
+ for prediction) to boost performance.
62
+
63
+ You can influence the splits nnU-Net uses for 5-fold cross-validation (see [here](manual_data_splits.md)). If you
64
+ prefer to train a single model on all training cases, this is also possible (see below).
65
+
66
+ **Note that not all U-Net configurations are created for all datasets. In datasets with small image sizes, the U-Net
67
+ cascade (and with it the 3d_lowres configuration) is omitted because the patch size of the full resolution U-Net
68
+ already covers a large part of the input images.**
69
+
70
+ Training models is done with the `nnUNetv2_train` command. The general structure of the command is:
71
+ ```bash
72
+ nnUNetv2_train DATASET_NAME_OR_ID UNET_CONFIGURATION FOLD [additional options, see -h]
73
+ ```
74
+
75
+ UNET_CONFIGURATION is a string that identifies the requested U-Net configuration (defaults: 2d, 3d_fullres, 3d_lowres,
76
+ 3d_cascade_lowres). DATASET_NAME_OR_ID specifies what dataset should be trained on and FOLD specifies which fold of
77
+ the 5-fold-cross-validation is trained.
78
+
79
+ nnU-Net stores a checkpoint every 50 epochs. If you need to continue a previous training, just add a `--c` to the
80
+ training command.
81
+
82
+ IMPORTANT: If you plan to use `nnUNetv2_find_best_configuration` (see below) add the `--npz` flag. This makes
83
+ nnU-Net save the softmax outputs during the final validation. They are needed for that. Exported softmax
84
+ predictions are very large and therefore can take up a lot of disk space, which is why this is not enabled by default.
85
+ If you ran initially without the `--npz` flag but now require the softmax predictions, simply rerun the validation with:
86
+ ```bash
87
+ nnUNetv2_train DATASET_NAME_OR_ID UNET_CONFIGURATION FOLD --val --npz
88
+ ```
89
+
90
+ You can specify the device nnU-net should use by using `-device DEVICE`. DEVICE can only be cpu, cuda or mps. If
91
+ you have multiple GPUs, please select the gpu id using `CUDA_VISIBLE_DEVICES=X nnUNetv2_train [...]` (requires device to be cuda).
92
+
93
+ See `nnUNetv2_train -h` for additional options.
94
+
95
+ ### 2D U-Net
96
+ For FOLD in [0, 1, 2, 3, 4], run:
97
+ ```bash
98
+ nnUNetv2_train DATASET_NAME_OR_ID 2d FOLD [--npz]
99
+ ```
100
+
101
+ ### 3D full resolution U-Net
102
+ For FOLD in [0, 1, 2, 3, 4], run:
103
+ ```bash
104
+ nnUNetv2_train DATASET_NAME_OR_ID 3d_fullres FOLD [--npz]
105
+ ```
106
+
107
+ ### 3D U-Net cascade
108
+ #### 3D low resolution U-Net
109
+ For FOLD in [0, 1, 2, 3, 4], run:
110
+ ```bash
111
+ nnUNetv2_train DATASET_NAME_OR_ID 3d_lowres FOLD [--npz]
112
+ ```
113
+
114
+ #### 3D full resolution U-Net
115
+ For FOLD in [0, 1, 2, 3, 4], run:
116
+ ```bash
117
+ nnUNetv2_train DATASET_NAME_OR_ID 3d_cascade_fullres FOLD [--npz]
118
+ ```
119
+ **Note that the 3D full resolution U-Net of the cascade requires the five folds of the low resolution U-Net to be
120
+ completed!**
121
+
122
+ The trained models will be written to the nnUNet_results folder. Each training obtains an automatically generated
123
+ output folder name:
124
+
125
+ nnUNet_results/DatasetXXX_MYNAME/TRAINER_CLASS_NAME__PLANS_NAME__CONFIGURATION/FOLD
126
+
127
+ For Dataset002_Heart (from the MSD), for example, this looks like this:
128
+
129
+ nnUNet_results/
130
+ β”œβ”€β”€ Dataset002_Heart
131
+ │── nnUNetTrainer__nnUNetPlans__2d
132
+ β”‚ β”œβ”€β”€ fold_0
133
+ β”‚ β”œβ”€β”€ fold_1
134
+ β”‚ β”œβ”€β”€ fold_2
135
+ β”‚ β”œβ”€β”€ fold_3
136
+ β”‚ β”œβ”€β”€ fold_4
137
+ β”‚ β”œβ”€β”€ dataset.json
138
+ β”‚ β”œβ”€β”€ dataset_fingerprint.json
139
+ β”‚ └── plans.json
140
+ └── nnUNetTrainer__nnUNetPlans__3d_fullres
141
+ β”œβ”€β”€ fold_0
142
+ β”œβ”€β”€ fold_1
143
+ β”œβ”€β”€ fold_2
144
+ β”œβ”€β”€ fold_3
145
+ β”œβ”€β”€ fold_4
146
+ β”œβ”€β”€ dataset.json
147
+ β”œβ”€β”€ dataset_fingerprint.json
148
+ └── plans.json
149
+
150
+ Note that 3d_lowres and 3d_cascade_fullres do not exist here because this dataset did not trigger the cascade. In each
151
+ model training output folder (each of the fold_x folder), the following files will be created:
152
+ - debug.json: Contains a summary of blueprint and inferred parameters used for training this model as well as a
153
+ bunch of additional stuff. Not easy to read, but very useful for debugging ;-)
154
+ - checkpoint_best.pth: checkpoint files of the best model identified during training. Not used right now unless you
155
+ explicitly tell nnU-Net to use it.
156
+ - checkpoint_final.pth: checkpoint file of the final model (after training has ended). This is what is used for both
157
+ validation and inference.
158
+ - network_architecture.pdf (only if hiddenlayer is installed!): a pdf document with a figure of the network architecture in it.
159
+ - progress.png: Shows losses, pseudo dice, learning rate and epoch times ofer the course of the training. At the top is
160
+ a plot of the training (blue) and validation (red) loss during training. Also shows an approximation of
161
+ the dice (green) as well as a moving average of it (dotted green line). This approximation is the average Dice score
162
+ of the foreground classes. **It needs to be taken with a big (!)
163
+ grain of salt** because it is computed on randomly drawn patches from the validation
164
+ data at the end of each epoch, and the aggregation of TP, FP and FN for the Dice computation treats the patches as if
165
+ they all originate from the same volume ('global Dice'; we do not compute a Dice for each validation case and then
166
+ average over all cases but pretend that there is only one validation case from which we sample patches). The reason for
167
+ this is that the 'global Dice' is easy to compute during training and is still quite useful to evaluate whether a model
168
+ is training at all or not. A proper validation takes way too long to be done each epoch. It is run at the end of the training.
169
+ - validation: in this folder are the predicted validation cases after the training has finished. The summary.json file in here
170
+ contains the validation metrics (a mean over all cases is provided at the start of the file). If `--npz` was set then
171
+ the compressed softmax outputs (saved as .npz files) are in here as well.
172
+
173
+ During training it is often useful to watch the progress. We therefore recommend that you have a look at the generated
174
+ progress.png when running the first training. It will be updated after each epoch.
175
+
176
+ Training times largely depend on the GPU. The smallest GPU we recommend for training is the Nvidia RTX 2080ti. With
177
+ that all network trainings take less than 2 days. Refer to our [benchmarks](benchmarking.md) to see if your system is
178
+ performing as expected.
179
+
180
+ ### Using multiple GPUs for training
181
+
182
+ If multiple GPUs are at your disposal, the best way of using them is to train multiple nnU-Net trainings at once, one
183
+ on each GPU. This is because data parallelism never scales perfectly linearly, especially not with small networks such
184
+ as the ones used by nnU-Net.
185
+
186
+ Example:
187
+
188
+ ```bash
189
+ CUDA_VISIBLE_DEVICES=0 nnUNetv2_train DATASET_NAME_OR_ID 2d 0 [--npz] & # train on GPU 0
190
+ CUDA_VISIBLE_DEVICES=1 nnUNetv2_train DATASET_NAME_OR_ID 2d 1 [--npz] & # train on GPU 1
191
+ CUDA_VISIBLE_DEVICES=2 nnUNetv2_train DATASET_NAME_OR_ID 2d 2 [--npz] & # train on GPU 2
192
+ CUDA_VISIBLE_DEVICES=3 nnUNetv2_train DATASET_NAME_OR_ID 2d 3 [--npz] & # train on GPU 3
193
+ CUDA_VISIBLE_DEVICES=4 nnUNetv2_train DATASET_NAME_OR_ID 2d 4 [--npz] & # train on GPU 4
194
+ ...
195
+ wait
196
+ ```
197
+
198
+ **Important: The first time a training is run nnU-Net will extract the preprocessed data into uncompressed numpy
199
+ arrays for speed reasons! This operation must be completed before starting more than one training of the same
200
+ configuration! Wait with starting subsequent folds until the first training is using the GPU! Depending on the
201
+ dataset size and your System this should only take a couple of minutes at most.**
202
+
203
+ If you insist on running DDP multi-GPU training, we got you covered:
204
+
205
+ `nnUNetv2_train DATASET_NAME_OR_ID 2d 0 [--npz] -num_gpus X`
206
+
207
+ Again, note that this will be slower than running separate training on separate GPUs. DDP only makes sense if you have
208
+ manually interfered with the nnU-Net configuration and are training larger models with larger patch and/or batch sizes!
209
+
210
+ Important when using `-num_gpus`:
211
+ 1) If you train using, say, 2 GPUs but have more GPUs in the system you need to specify which GPUs should be used via
212
+ CUDA_VISIBLE_DEVICES=0,1 (or whatever your ids are).
213
+ 2) You cannot specify more GPUs than you have samples in your minibatches. If the batch size is 2, 2 GPUs is the maximum!
214
+ 3) Make sure your batch size is divisible by the numbers of GPUs you use or you will not make good use of your hardware.
215
+
216
+ In contrast to the old nnU-Net, DDP is now completely hassle free. Enjoy!
217
+
218
+ ### Automatically determine the best configuration
219
+ Once the desired configurations were trained (full cross-validation) you can tell nnU-Net to automatically identify
220
+ the best combination for you:
221
+
222
+ ```commandline
223
+ nnUNetv2_find_best_configuration DATASET_NAME_OR_ID -c CONFIGURATIONS
224
+ ```
225
+
226
+ `CONFIGURATIONS` hereby is the list of configurations you would like to explore. Per default, ensembling is enabled
227
+ meaning that nnU-Net will generate all possible combinations of ensembles (2 configurations per ensemble). This requires
228
+ the .npz files containing the predicted probabilities of the validation set to be present (use `nnUNetv2_train` with
229
+ `--npz` flag, see above). You can disable ensembling by setting the `--disable_ensembling` flag.
230
+
231
+ See `nnUNetv2_find_best_configuration -h` for more options.
232
+
233
+ nnUNetv2_find_best_configuration will also automatically determine the postprocessing that should be used.
234
+ Postprocessing in nnU-Net only considers the removal of all but the largest component in the prediction (once for
235
+ foreground vs background and once for each label/region).
236
+
237
+ Once completed, the command will print to your console exactly what commands you need to run to make predictions. It
238
+ will also create two files in the `nnUNet_results/DATASET_NAME` folder for you to inspect:
239
+ - `inference_instructions.txt` again contains the exact commands you need to use for predictions
240
+ - `inference_information.json` can be inspected to see the performance of all configurations and ensembles, as well
241
+ as the effect of the postprocessing plus some debug information.
242
+
243
+ ### Run inference
244
+ Remember that the data located in the input folder must have the file endings as the dataset you trained the model on
245
+ and must adhere to the nnU-Net naming scheme for image files (see [dataset format](dataset_format.md) and
246
+ [inference data format](dataset_format_inference.md)!)
247
+
248
+ `nnUNetv2_find_best_configuration` (see above) will print a string to the terminal with the inference commands you need to use.
249
+ The easiest way to run inference is to simply use these commands.
250
+
251
+ If you wish to manually specify the configuration(s) used for inference, use the following commands:
252
+
253
+ #### Run prediction
254
+ For each of the desired configurations, run:
255
+ ```
256
+ nnUNetv2_predict -i INPUT_FOLDER -o OUTPUT_FOLDER -d DATASET_NAME_OR_ID -c CONFIGURATION --save_probabilities
257
+ ```
258
+
259
+ Only specify `--save_probabilities` if you intend to use ensembling. `--save_probabilities` will make the command save the predicted
260
+ probabilities alongside of the predicted segmentation masks requiring a lot of disk space.
261
+
262
+ Please select a separate `OUTPUT_FOLDER` for each configuration!
263
+
264
+ Note that per default, inference will be done with all 5 folds from the cross-validation as an ensemble. We very
265
+ strongly recommend you use all 5 folds. Thus, all 5 folds must have been trained prior to running inference.
266
+
267
+ If you wish to make predictions with a single model, train the `all` fold and specify it in `nnUNetv2_predict`
268
+ with `-f all`
269
+
270
+ #### Ensembling multiple configurations
271
+ If you wish to ensemble multiple predictions (typically form different configurations), you can do so with the following command:
272
+ ```bash
273
+ nnUNetv2_ensemble -i FOLDER1 FOLDER2 ... -o OUTPUT_FOLDER -np NUM_PROCESSES
274
+ ```
275
+
276
+ You can specify an arbitrary number of folders, but remember that each folder needs to contain npz files that were
277
+ generated by `nnUNetv2_predict`. Again, `nnUNetv2_ensemble -h` will tell you more about additional options.
278
+
279
+ #### Apply postprocessing
280
+ Finally, apply the previously determined postprocessing to the (ensembled) predictions:
281
+
282
+ ```commandline
283
+ nnUNetv2_apply_postprocessing -i FOLDER_WITH_PREDICTIONS -o OUTPUT_FOLDER --pp_pkl_file POSTPROCESSING_FILE -plans_json PLANS_FILE -dataset_json DATASET_JSON_FILE
284
+ ```
285
+
286
+ `nnUNetv2_find_best_configuration` (or its generated `inference_instructions.txt` file) will tell you where to find
287
+ the postprocessing file. If not you can just look for it in your results folder (it's creatively named
288
+ `postprocessing.pkl`). If your source folder is from an ensemble, you also need to specify a `-plans_json` file and
289
+ a `-dataset_json` file that should be used (for single configuration predictions these are automatically copied
290
+ from the respective training). You can pick these files from any of the ensemble members.
291
+
292
+
293
+ ## How to run inference with pretrained models
294
+ See [here](run_inference_with_pretrained_models.md)
295
+
296
+ ## How to Deploy and Run Inference with YOUR Pretrained Models
297
+ To facilitate the use of pretrained models on a different computer for inference purposes, follow these streamlined steps:
298
+ 1. Exporting the Model: Utilize the `nnUNetv2_export_model_to_zip` function to package your trained model into a .zip file. This file will contain all necessary model files.
299
+ 2. Transferring the Model: Transfer the .zip file to the target computer where inference will be performed.
300
+ 3. Importing the Model: On the new PC, use the `nnUNetv2_install_pretrained_model_from_zip` to load the pretrained model from the .zip file.
301
+ Please note that both computers must have nnU-Net installed along with all its dependencies to ensure compatibility and functionality of the model.
302
+
303
+ [//]: # (## Examples)
304
+
305
+ [//]: # ()
306
+ [//]: # (To get you started we compiled two simple to follow examples:)
307
+
308
+ [//]: # (- run a training with the 3d full resolution U-Net on the Hippocampus dataset. See [here]&#40;documentation/training_example_Hippocampus.md&#41;.)
309
+
310
+ [//]: # (- run inference with nnU-Net's pretrained models on the Prostate dataset. See [here]&#40;documentation/inference_example_Prostate.md&#41;.)
311
+
312
+ [//]: # ()
313
+ [//]: # (Usability not good enough? Let us know!)
nnUNet/documentation/ignore_label.md ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ignore Label
2
+
3
+ The _ignore label_ can be used to mark regions that should be ignored by nnU-Net. This can be used to
4
+ learn from images where only sparse annotations are available, for example in the form of scribbles or a limited
5
+ amount of annotated slices. Internally, this is accomplished by using partial losses, i.e. losses that are only
6
+ computed on annotated pixels while ignoring the rest. Take a look at our
7
+ [`DC_and_BCE_loss` loss](../nnunetv2/training/loss/compound_losses.py) to see how this is done.
8
+ During inference (validation and prediction), nnU-Net will always predict dense segmentations. Metric computation in
9
+ validation is of course only done on annotated pixels.
10
+
11
+ Using sparse annotations can be used to train a model for application to new, unseen images or to autocomplete the
12
+ provided training cases given the sparse labels.
13
+
14
+ (See our [paper](https://arxiv.org/abs/2403.12834) for more information)
15
+
16
+ Typical use-cases for the ignore label are:
17
+ - Save annotation time through sparse annotation schemes
18
+ - Annotation of all or a subset of slices with scribbles (Scribble Supervision)
19
+ - Dense annotation of a subset of slices
20
+ - Dense annotation of chosen patches/cubes within an image
21
+ - Coarsly masking out faulty segmentations in the reference segmentations
22
+ - Masking areas for other reasons
23
+
24
+ If you are using nnU-Net's ignore label, please cite the following paper in addition to the original nnU-net paper:
25
+
26
+ ```
27
+ Gotkowski, K., LΓΌth, C., JΓ€ger, P. F., Ziegler, S., KrΓ€mer, L., Denner, S., Xiao, S., Disch, N., H., K., & Isensee, F.
28
+ (2024). Embarrassingly Simple Scribble Supervision for 3D Medical Segmentation. ArXiv. /abs/2403.12834
29
+ ```
30
+
31
+ ## Usecases
32
+
33
+ ### Scribble Supervision
34
+
35
+ Scribbles are free-form drawings to coarsly annotate an image. As we have demonstrated in our recent [paper](https://arxiv.org/abs/2403.12834), nnU-Net's partial loss implementation enables state-of-the-art learning from partially annotated data and even surpasses many purpose-built methods for learning from scribbles. As a starting point, for each image slice and each class (including background), an interior and a border scribble should be generated:
36
+
37
+ - Interior Scribble: A scribble placed randomly within the class interior of a class instance
38
+ - Border Scribble: A scribble roughly delineating a small part of the class border of a class instance
39
+
40
+ An example of such scribble annotations is depicted in Figure 1 and an animation in Animation 1.
41
+ Depending on the availability of data and their variability it is also possible to only annotated a subset of selected slices.
42
+
43
+ <p align="center">
44
+ <img src="assets/scribble_example.png" width="1024px" />
45
+ <figcaption>Figure 1: Examples of segmentation types with (A) depicting a dense segmentation and (B) a scribble segmentation.</figcaption>
46
+ </figure>
47
+ </p>
48
+
49
+ <p align="center">
50
+ <img width="512px" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbmdndHQwMG96M3FqZWtwbHR2enUwZXhwNHVsbndzNmNpZnVlbHJ6OSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/KRJ48evmroDlIgcqcO/giphy.gif">
51
+ <img width="512px" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExem10Z3ZqZHQ2MWNsMjdibG1zc3M2NzNqbG9mazdudG5raTk4d3h4MSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ifVxQQfco5ro1gH6bQ/giphy.gif">
52
+ <figcaption>Animation 1: Depiction of a dense segmentation and a scribble annotation. Background scribbles have been excluded for better visualization.</figcaption>
53
+ </p>
54
+
55
+ ### Dense annotation of a subset of slices
56
+
57
+ Another form of sparse annotation is the dense annotation of a subset of slices. These slices should be selected by the user either randomly, based on visual class variation between slices or in an active learning setting. An example with only 10% of slices annotated is depicted in Figure 2.
58
+
59
+ <p align="center">
60
+ <img src="assets/amos2022_sparseseg10_2d.png" width="512px" />
61
+ <img src="assets/amos2022_sparseseg10.png" width="512px" />
62
+ <figcaption>Figure 2: Examples of a dense annotation of a subset of slices. The ignored areas are shown in red.</figcaption>
63
+ </figure>
64
+ </p>
65
+
66
+
67
+ ## Usage within nnU-Net
68
+
69
+ Usage of the ignore label in nnU-Net is straightforward and only requires the definition of an _ignore_ label in the _dataset.json_.
70
+ This ignore label MUST be the highest integer label value in the segmentation. Exemplary, given the classes background and two foreground classes, then the ignore label must have the integer 3. The ignore label must be named _ignore_ in the _dataset.json_. Given the BraTS dataset as an example the labels dict of the _dataset.json_ must look like this:
71
+
72
+ ```python
73
+ ...
74
+ "labels": {
75
+ "background": 0,
76
+ "edema": 1,
77
+ "non_enhancing_and_necrosis": 2,
78
+ "enhancing_tumor": 3,
79
+ "ignore": 4
80
+ },
81
+ ...
82
+ ```
83
+
84
+ Of course, the ignore label is compatible with [region-based training](region_based_training.md):
85
+
86
+ ```python
87
+ ...
88
+ "labels": {
89
+ "background": 0,
90
+ "whole_tumor": (1, 2, 3),
91
+ "tumor_core": (2, 3),
92
+ "enhancing_tumor": 3, # or (3, )
93
+ "ignore": 4
94
+ },
95
+ "regions_class_order": (1, 2, 3), # don't declare ignore label here! It is not predicted
96
+ ...
97
+ ```
98
+
99
+ Then use the dataset as you would any other.
100
+
101
+ Remember that nnU-Net runs a cross-validation. Thus, it will also evaluate on your partially annotated data. This
102
+ will of course work! If you wish to compare different sparse annotation strategies (through simulations for example),
103
+ we recommend evaluating on densely annotated images by running inference and then using `nnUNetv2_evaluate_folder` or
104
+ `nnUNetv2_evaluate_simple`.
nnUNet/documentation/installation_instructions.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # System requirements
2
+
3
+ ## Operating System
4
+ nnU-Net has been tested on Linux (Ubuntu 18.04, 20.04, 22.04; centOS, RHEL), Windows and MacOS! It should work out of the box!
5
+
6
+ ## Hardware requirements
7
+ We support GPU (recommended), CPU and Apple M1/M2 as devices (currently Apple mps does not implement 3D
8
+ convolutions, so you might have to use the CPU on those devices).
9
+
10
+ ### Hardware requirements for Training
11
+ We recommend you use a GPU for training as this will take a really long time on CPU or MPS (Apple M1/M2).
12
+ For training a GPU with at least 10 GB (popular non-datacenter options are the RTX 2080ti, RTX 3080/3090 or RTX 4080/4090) is
13
+ required. We also recommend a strong CPU to go along with the GPU. 6 cores (12 threads)
14
+ are the bare minimum! CPU requirements are mostly related to data augmentation and scale with the number of
15
+ input channels and target structures. Plus, the faster the GPU, the better the CPU should be!
16
+
17
+ ### Hardware Requirements for inference
18
+ Again we recommend a GPU to make predictions as this will be substantially faster than the other options. However,
19
+ inference times are typically still manageable on CPU and MPS (Apple M1/M2). If using a GPU, it should have at least
20
+ 4 GB of available (unused) VRAM.
21
+
22
+ ### Example hardware configurations
23
+ Example workstation configurations for training:
24
+ - CPU: Ryzen 5800X - 5900X or 7900X would be even better! We have not yet tested Intel Alder/Raptor lake but they will likely work as well.
25
+ - GPU: RTX 3090 or RTX 4090
26
+ - RAM: 64GB
27
+ - Storage: SSD (M.2 PCIe Gen 3 or better!)
28
+
29
+ Example Server configuration for training:
30
+ - CPU: 2x AMD EPYC7763 for a total of 128C/256T. 16C/GPU are highly recommended for fast GPUs such as the A100!
31
+ - GPU: 8xA100 PCIe (price/performance superior to SXM variant + they use less power)
32
+ - RAM: 1 TB
33
+ - Storage: local SSD storage (PCIe Gen 3 or better) or ultra fast network storage
34
+
35
+ (nnU-net by default uses one GPU per training. The server configuration can run up to 8 model trainings simultaneously)
36
+
37
+ ### Setting the correct number of Workers for data augmentation (training only)
38
+ Note that you will need to manually set the number of processes nnU-Net uses for data augmentation according to your
39
+ CPU/GPU ratio. For the server above (256 threads for 8 GPUs), a good value would be 24-30. You can do this by
40
+ setting the `nnUNet_n_proc_DA` environment variable (`export nnUNet_n_proc_DA=XX`).
41
+ Recommended values (assuming a recent CPU with good IPC) are 10-12 for RTX 2080 ti, 12 for a RTX 3090, 16-18 for
42
+ RTX 4090, 28-32 for A100. Optimal values may vary depending on the number of input channels/modalities and number of classes.
43
+
44
+ # Installation instructions
45
+ We strongly recommend that you install nnU-Net in a virtual environment! Pip or anaconda are both fine. If you choose to
46
+ compile PyTorch from source (see below), you will need to use conda instead of pip.
47
+
48
+ Use a recent version of Python! 3.9 or newer is guaranteed to work!
49
+
50
+ **nnU-Net v2 can coexist with nnU-Net v1! Both can be installed at the same time.**
51
+
52
+ 1) Install [PyTorch](https://pytorch.org/get-started/locally/) as described on their website (conda/pip). Please
53
+ install the latest version with support for your hardware (cuda, mps, cpu).
54
+ **DO NOT JUST `pip install nnunetv2` WITHOUT PROPERLY INSTALLING PYTORCH FIRST**. For maximum speed, consider
55
+ [compiling pytorch yourself](https://github.com/pytorch/pytorch#from-source) (experienced users only!).
56
+ 2) Install nnU-Net depending on your use case:
57
+ 1) For use as **standardized baseline**, **out-of-the-box segmentation algorithm** or for running
58
+ **inference with pretrained models**:
59
+
60
+ ```pip install nnunetv2```
61
+
62
+ 2) For use as integrative **framework** (this will create a copy of the nnU-Net code on your computer so that you
63
+ can modify it as needed):
64
+ ```bash
65
+ git clone https://github.com/MIC-DKFZ/nnUNet.git
66
+ cd nnUNet
67
+ pip install -e .
68
+ ```
69
+ 3) nnU-Net needs to know where you intend to save raw data, preprocessed data and trained models. For this you need to
70
+ set a few environment variables. Please follow the instructions [here](setting_up_paths.md).
71
+ 4) (OPTIONAL) Install [hiddenlayer](https://github.com/waleedka/hiddenlayer). hiddenlayer enables nnU-net to generate
72
+ plots of the network topologies it generates (see [Model training](how_to_use_nnunet.md#model-training)).
73
+ To install hiddenlayer,
74
+ run the following command:
75
+ ```bash
76
+ pip install --upgrade git+https://github.com/FabianIsensee/hiddenlayer.git
77
+ ```
78
+
79
+ Installing nnU-Net will add several new commands to your terminal. These commands are used to run the entire nnU-Net
80
+ pipeline. You can execute them from any location on your system. All nnU-Net commands have the prefix `nnUNetv2_` for
81
+ easy identification.
82
+
83
+ Note that these commands simply execute python scripts. If you installed nnU-Net in a virtual environment, this
84
+ environment must be activated when executing the commands. You can see what scripts/functions are executed by
85
+ checking the project.scripts in the [pyproject.toml](../pyproject.toml) file.
86
+
87
+ All nnU-Net commands have a `-h` option which gives information on how to use them.
nnUNet/documentation/manual_data_splits.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to generate custom splits in nnU-Net
2
+
3
+ Sometimes, the default 5-fold cross-validation split by nnU-Net does not fit a project. Maybe you want to run 3-fold
4
+ cross-validation instead? Or maybe your training cases cannot be split randomly and require careful stratification.
5
+ Fear not, for nnU-Net has got you covered (it really can do anything <3).
6
+
7
+ The splits nnU-Net uses are generated in the `do_split` function of nnUNetTrainer. This function will first look for
8
+ existing splits, stored as a file, and if no split exists it will create one. So if you wish to influence the split,
9
+ manually creating a split file that will then be recognized and used is the way to go!
10
+
11
+ The split file is located in the `nnUNet_preprocessed/DATASETXXX_NAME` folder. So it is best practice to first
12
+ populate this folder by running `nnUNetv2_plan_and_preproccess`.
13
+
14
+ Splits are stored as a .json file. They are a simple python list. The length of that list is the number of splits it
15
+ contains (so it's 5 in the default nnU-Net). Each list entry is a dictionary with keys 'train' and 'val'. Values are
16
+ again simply lists with the train identifiers in each set. To illustrate this, I am just messing with the Dataset002
17
+ file as an example:
18
+
19
+ ```commandline
20
+ In [1]: from batchgenerators.utilities.file_and_folder_operations import load_json
21
+
22
+ In [2]: splits = load_json('splits_final.json')
23
+
24
+ In [3]: len(splits)
25
+ Out[3]: 5
26
+
27
+ In [4]: splits[0].keys()
28
+ Out[4]: dict_keys(['train', 'val'])
29
+
30
+ In [5]: len(splits[0]['train'])
31
+ Out[5]: 16
32
+
33
+ In [6]: len(splits[0]['val'])
34
+ Out[6]: 4
35
+
36
+ In [7]: print(splits[0])
37
+ {'train': ['la_003', 'la_004', 'la_005', 'la_009', 'la_010', 'la_011', 'la_014', 'la_017', 'la_018', 'la_019', 'la_020', 'la_022', 'la_023', 'la_026', 'la_029', 'la_030'],
38
+ 'val': ['la_007', 'la_016', 'la_021', 'la_024']}
39
+ ```
40
+
41
+ If you are still not sure what splits are supposed to look like, simply download some reference dataset from the
42
+ [Medical Decathlon](http://medicaldecathlon.com/), start some training (to generate the splits) and manually inspect
43
+ the .json file with your text editor of choice!
44
+
45
+ In order to generate your custom splits, all you need to do is reproduce the data structure explained above and save it as
46
+ `splits_final.json` in the `nnUNet_preprocessed/DATASETXXX_NAME` folder. Then use `nnUNetv2_train` etc. as usual.
nnUNet/documentation/pretraining_and_finetuning.md ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pretraining with nnU-Net
2
+
3
+ ## Intro
4
+
5
+ So far nnU-Net only supports supervised pre-training, meaning that you train a regular nnU-Net on some pretraining dataset
6
+ and then use the final network weights as initialization for your target dataset.
7
+
8
+ As a reminder, many training hyperparameters such as patch size and network topology differ between datasets as a
9
+ result of the automated dataset analysis and experiment planning nnU-Net is known for. So, out of the box, it is not
10
+ possible to simply take the network weights from some dataset and then reuse them for another.
11
+
12
+ Consequently, the plans need to be aligned between the two tasks. In this README we show how this can be achieved and
13
+ how the resulting weights can then be used for initialization.
14
+
15
+ ### Terminology
16
+
17
+ Throughout this README we use the following terminology:
18
+
19
+ - `pretraining dataset` is the dataset you intend to run the pretraining on
20
+ - `finetuning dataset` is the dataset you are interested in; the one you wish to fine tune on
21
+
22
+
23
+ ## Training on the pretraining dataset
24
+
25
+ In order to obtain matching network topologies we need to transfer the plans from one dataset to another. Since we are
26
+ only interested in the finetuning dataset, we first need to run experiment planning (and preprocessing) for it:
27
+
28
+ ```bash
29
+ nnUNetv2_plan_and_preprocess -d FINETUNING_DATASET
30
+ ```
31
+
32
+ Then we need to extract the dataset fingerprint of the pretraining dataset, if not yet available:
33
+
34
+ ```bash
35
+ nnUNetv2_extract_fingerprint -d PRETRAINING_DATASET
36
+ ```
37
+
38
+ Now we can take the plans from the finetuning dataset and transfer it to the pretraining dataset:
39
+
40
+ ```bash
41
+ nnUNetv2_move_plans_between_datasets -s FINETUNING_DATASET -t PRETRAINING_DATASET -sp FINETUNING_PLANS_IDENTIFIER -tp PRETRAINING_PLANS_IDENTIFIER
42
+ ```
43
+
44
+ `FINETUNING_PLANS_IDENTIFIER` is hereby probably nnUNetPlans unless you changed the experiment planner in
45
+ nnUNetv2_plan_and_preprocess. For `PRETRAINING_PLANS_IDENTIFIER` we recommend you set something custom in order to not
46
+ overwrite default plans.
47
+
48
+ Note that EVERYTHING is transferred between the datasets. Not just the network topology, batch size and patch size but
49
+ also the normalization scheme! Therefore, a transfer between datasets that use different normalization schemes may not
50
+ work well (but it could, depending on the schemes!).
51
+
52
+ Note on CT normalization: Yes, also the clip values, mean and std are transferred!
53
+
54
+ Now you can run the preprocessing on the pretraining dataset:
55
+
56
+ ```bash
57
+ nnUNetv2_preprocess -d PRETRAINING_DATASET -plans_name PRETRAINING_PLANS_IDENTIFIER
58
+ ```
59
+
60
+ And run the training as usual:
61
+
62
+ ```bash
63
+ nnUNetv2_train PRETRAINING_DATASET CONFIG all -p PRETRAINING_PLANS_IDENTIFIER
64
+ ```
65
+
66
+ Note how we use the 'all' fold to train on all available data. For pretraining it does not make sense to split the data.
67
+
68
+ ## Using pretrained weights
69
+
70
+ Once pretraining is completed (or you obtain compatible weights by other means) you can use them to initialize your model:
71
+
72
+ ```bash
73
+ nnUNetv2_train FINETUNING_DATASET CONFIG FOLD -pretrained_weights PATH_TO_CHECKPOINT
74
+ ```
75
+
76
+ Specify the checkpoint in PATH_TO_CHECKPOINT.
77
+
78
+ When loading pretrained weights, all layers except the segmentation layers will be used!
79
+
80
+ So far there are no specific nnUNet trainers for fine tuning, so the current recommendation is to just use
81
+ nnUNetTrainer. You can however easily write your own trainers with learning rate ramp up, fine-tuning of segmentation
82
+ heads or shorter training time.
nnUNet/documentation/region_based_training.md ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Region-based training
2
+
3
+ ## What is this about?
4
+ In some segmentation tasks, most prominently the
5
+ [Brain Tumor Segmentation Challenge](http://braintumorsegmentation.org/), the target areas (based on which the metric
6
+ will be computed) are different from the labels provided in the training data. This is the case because for some
7
+ clinical applications, it is more relevant to detect the whole tumor, tumor core and enhancing tumor instead of the
8
+ individual labels (edema, necrosis and non-enhancing tumor, enhancing tumor).
9
+
10
+ <img src="assets/regions_vs_labels.png" width="768px" />
11
+
12
+ The figure shows an example BraTS case along with label-based representation of the task (top) and region-based
13
+ representation (bottom). The challenge evaluation is done on the regions. As we have shown in our
14
+ [BraTS 2018 contribution](https://arxiv.org/abs/1809.10483), directly optimizing those
15
+ overlapping areas over the individual labels yields better scoring models!
16
+
17
+ ## What can nnU-Net do?
18
+ nnU-Net's region-based training allows you to learn areas that are constructed by merging individual labels. For
19
+ some segmentation tasks this provides a benefit, as this shifts the importance allocated to different labels during training.
20
+ Most prominently, this feature can be used to represent **hierarchical classes**, for example when organs +
21
+ substructures are to be segmented. Imagine a liver segmentation problem, where vessels and tumors are also to be
22
+ segmented. The first target region could thus be the entire liver (including the substructures), while the remaining
23
+ targets are the individual substructues.
24
+
25
+ Important: nnU-Net still requires integer label maps as input and will produce integer label maps as output!
26
+ Region-based training can be used to learn overlapping labels, but there must be a way to model these overlaps
27
+ for nnU-Net to work (see below how this is done).
28
+
29
+ ## How do you use it?
30
+
31
+ When declaring the labels in the `dataset.json` file, BraTS would typically look like this:
32
+
33
+ ```python
34
+ ...
35
+ "labels": {
36
+ "background": 0,
37
+ "edema": 1,
38
+ "non_enhancing_and_necrosis": 2,
39
+ "enhancing_tumor": 3
40
+ },
41
+ ...
42
+ ```
43
+ (we use different int values than the challenge because nnU-Net needs consecutive integers!)
44
+
45
+ This representation corresponds to the upper row in the figure above.
46
+
47
+ For region-based training, the labels need to be changed to the following:
48
+
49
+ ```python
50
+ ...
51
+ "labels": {
52
+ "background": 0,
53
+ "whole_tumor": [1, 2, 3],
54
+ "tumor_core": [2, 3],
55
+ "enhancing_tumor": 3 # or [3]
56
+ },
57
+ "regions_class_order": [1, 2, 3],
58
+ ...
59
+ ```
60
+ This corresponds to the bottom row in the figure above. Note how an additional entry in the dataset.json is
61
+ required: `regions_class_order`. This tells nnU-Net how to convert the region representations back to an integer map.
62
+ It essentially just tells nnU-Net what labels to place for which region in what order. The length of the
63
+ list here needs to be the same as the number of regions (excl background). Each element in the list corresponds
64
+ to the label that is placed instead of the region into the final segmentation. Later entries will overwrite earlier ones!
65
+ Concretely, for the example given here, nnU-Net
66
+ will firstly place the label 1 (edema) where the 'whole_tumor' region was predicted, then place the label 2
67
+ (non-enhancing tumor and necrosis) where the "tumor_core" was predicted and finally place the label 3 in the
68
+ predicted 'enhancing_tumor' area. With each step, part of the previously set pixels
69
+ will be overwritten with the new label! So when setting your `regions_class_order`, place encompassing regions
70
+ (like whole tumor etc) first, followed by substructures.
71
+
72
+ **IMPORTANT** Because the conversion back to a segmentation map is sensitive to the order in which the regions are
73
+ declared ("place label X in the first region") you need to make sure that this order is not perturbed! When
74
+ automatically generating the dataset.json, make sure the dictionary keys do not get sorted alphabetically! Set
75
+ `sort_keys=False` in `json.dump()`!!!
76
+
77
+ nnU-Net will perform the evaluation + model selection also on the regions, not the individual labels!
78
+
79
+ That's all. Easy, huh?
nnUNet/documentation/resenc_presets.md ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Residual Encoder Presets in nnU-Net
2
+
3
+ When using these presets, please cite our recent paper on the need for rigorous validation in 3D medical image segmentation:
4
+
5
+ > Isensee, F.<sup>* </sup>, Wald, T.<sup>* </sup>, Ulrich, C.<sup>* </sup>, Baumgartner, M.<sup>* </sup>, Roy, S., Maier-Hein, K.<sup>†</sup>, Jaeger, P.<sup>†</sup> (2024). nnU-Net Revisited: A Call for Rigorous Validation in 3D Medical Image Segmentation. arXiv preprint arXiv:2404.09556.
6
+
7
+ *: shared first authors\
8
+ <sup>†</sup>: shared last authors
9
+
10
+ [PAPER LINK](https://arxiv.org/pdf/2404.09556.pdf)
11
+
12
+
13
+ Residual Encoder UNets have been supported by nnU-Net since our participation in KiTS2019, but have flown under the radar.
14
+ This is bound to change with our new nnUNetResEncUNet presets :raised_hands:! Especially on large datasets such as KiTS2023 and AMOS2022
15
+ they offer improved segmentation performance!
16
+
17
+ | | BTCV | ACDC | LiTS | BraTS | KiTS | AMOS | VRAM | RT | Arch. | nnU |
18
+ |------------------------|-------|-------|-------|-------|-------|-------|-------|-----|-------|-----|
19
+ | | n=30 | n=200 | n=131 | n=1251| n=489 | n=360 | | | | |
20
+ | nnU-Net (org.) [1] | 83.08 | 91.54 | 80.09 | 91.24 | 86.04 | 88.64 | 7.70 | 9 | CNN | Yes |
21
+ | nnU-Net ResEnc M | 83.31 | 91.99 | 80.75 | 91.26 | 86.79 | 88.77 | 9.10 | 12 | CNN | Yes |
22
+ | nnU-Net ResEnc L | 83.35 | 91.69 | 81.60 | 91.13 | 88.17 | 89.41 | 22.70 | 35 | CNN | Yes |
23
+ | nnU-Net ResEnc XL | 83.28 | 91.48 | 81.19 | 91.18 | 88.67 | 89.68 | 36.60 | 66 | CNN | Yes |
24
+ | MedNeXt L k3 [2] | 84.70 | 92.65 | 82.14 | 91.35 | 88.25 | 89.62 | 17.30 | 68 | CNN | Yes |
25
+ | MedNeXt L k5 [2] | 85.04 | 92.62 | 82.34 | 91.50 | 87.74 | 89.73 | 18.00 | 233 | CNN | Yes |
26
+ | STU-Net S [3] | 82.92 | 91.04 | 78.50 | 90.55 | 84.93 | 88.08 | 5.20 | 10 | CNN | Yes |
27
+ | STU-Net B [3] | 83.05 | 91.30 | 79.19 | 90.85 | 86.32 | 88.46 | 8.80 | 15 | CNN | Yes |
28
+ | STU-Net L [3] | 83.36 | 91.31 | 80.31 | 91.26 | 85.84 | 89.34 | 26.50 | 51 | CNN | Yes |
29
+ | SwinUNETR [4] | 78.89 | 91.29 | 76.50 | 90.68 | 81.27 | 83.81 | 13.10 | 15 | TF | Yes |
30
+ | SwinUNETRV2 [5] | 80.85 | 92.01 | 77.85 | 90.74 | 84.14 | 86.24 | 13.40 | 15 | TF | Yes |
31
+ | nnFormer [6] | 80.86 | 92.40 | 77.40 | 90.22 | 75.85 | 81.55 | 5.70 | 8 | TF | Yes |
32
+ | CoTr [7] | 81.95 | 90.56 | 79.10 | 90.73 | 84.59 | 88.02 | 8.20 | 18 | TF | Yes |
33
+ | No-Mamba Base | 83.69 | 91.89 | 80.57 | 91.26 | 85.98 | 89.04 | 12.0 | 24 | CNN | Yes |
34
+ | U-Mamba Bot [8] | 83.51 | 91.79 | 80.40 | 91.26 | 86.22 | 89.13 | 12.40 | 24 | Mam | Yes |
35
+ | U-Mamba Enc [8] | 82.41 | 91.22 | 80.27 | 90.91 | 86.34 | 88.38 | 24.90 | 47 | Mam | Yes |
36
+ | A3DS SegResNet [9,11] | 80.69 | 90.69 | 79.28 | 90.79 | 81.11 | 87.27 | 20.00 | 22 | CNN | No |
37
+ | A3DS DiNTS [10, 11] | 78.18 | 82.97 | 69.05 | 87.75 | 65.28 | 82.35 | 29.20 | 16 | CNN | No |
38
+ | A3DS SwinUNETR [4, 11] | 76.54 | 82.68 | 68.59 | 89.90 | 52.82 | 85.05 | 34.50 | 9 | TF | No |
39
+
40
+ Results taken from our paper (see above), reported values are Dice scores computed over 5-fold cross-validation on each
41
+ dataset. All models trained from scratch.
42
+
43
+ RT: training run time (measured on 1x Nvidia A100 PCIe 40GB)\
44
+ VRAM: GPU VRAM used during training, as reported by nvidia-smi\
45
+ Arch.: CNN = convolutional neural network; TF = transformer; Mam = Mamba\
46
+ nnU: whether the architectrue was integrated and tested with the nnU-Net framework (either by us or the original authors)
47
+
48
+ ## How to use the new presets
49
+
50
+ We offer three new presets, each targeted for a different GPU VRAM and compute budget:
51
+ - **nnU-Net ResEnc M**: similar GPU budget to the standard UNet configuration. Best suited for GPUs with 9-11GB VRAM. Training time: ~12h on A100
52
+ - **nnU-Net ResEnc L**: requires a GPU with 24GB VRAM. Training time: ~35h on A100
53
+ - **nnU-Net ResEnc XL**: requires a GPU with 40GB VRAM. Training time: ~66h on A100
54
+
55
+ ### **:point_right: We recommend **nnU-Net ResEnc L** as the new default nnU-Net configuration! :point_left:**
56
+
57
+ The new presets are available as follows ((M/L/XL) = pick one!):
58
+ 1. Specify the desired configuration when running experiment planning and preprocessing:
59
+ `nnUNetv2_plan_and_preprocess -d DATASET -pl nnUNetPlannerResEnc(M/L/XL)`. These planners use the same preprocessed
60
+ data folder as the standard 2d and 3d_fullres configurations since the preprocessed data is identical. Only the
61
+ 3d_lowres differs and will be saved in a different folder to allow all configurations to coexist! If you are only
62
+ planning to run 3d_fullres/2d and you already have this data preprocessed, you can just run
63
+ `nnUNetv2_plan_experiment -d DATASET -pl nnUNetPlannerResEnc(M/L/XL)` to avoid preprocessing again!
64
+ 2. Now, just specify the correct plans when running `nnUNetv2_train`, `nnUNetv2_predict` etc. The interface is
65
+ consistent across all nnU-Net commands: `-p nnUNetResEncUNet(M/L/XL)Plans`
66
+
67
+ Training results for the new presets will be stored in a dedicated folder and will not overwrite standard nnU-Net
68
+ results! So don't be afraid to give it a go!
69
+
70
+ ## Scaling ResEnc nnU-Net beyond the Presets
71
+ The presets differ from `ResEncUNetPlanner` in two ways:
72
+ - They set new default values for `gpu_memory_target_in_gb` to target the respective VRAM consumptions
73
+ - They remove the batch size cap of 0.05 (= previously one batch could not cover mode pixels than 5% of the entire dataset, not it can be arbitrarily large)
74
+
75
+ The presets are merely there to make life easier, and to provide standardized configurations people can benchmark with.
76
+ You can easily adapt the GPU memory target to match your GPU, and to scale beyond 40GB of GPU memory.
77
+
78
+ Here is an example for how to scale to 80GB VRAM on Dataset003_Liver:
79
+
80
+ `nnUNetv2_plan_experiment -d 3 -pl nnUNetPlannerResEncM -gpu_memory_target 80 -overwrite_plans_name nnUNetResEncUNetPlans_80G`
81
+
82
+ Just use `-p nnUNetResEncUNetPlans_80G` moving forward as outlined above! Running the example above will yield a
83
+ warning ("You are running nnUNetPlannerM with a non-standard gpu_memory_target_in_gb"). This warning can be ignored here.
84
+ **Always change the plans identifier with `-overwrite_plans_name NEW_PLANS_NAME` when messing with the VRAM target in
85
+ order to not overwrite preset plans!**
86
+
87
+ Why not use `ResEncUNetPlanner` -> because that one still has the 5% cap in place!
88
+
89
+ ### Scaling to multiple GPUs
90
+ When scaling to multiple GPUs, do not just specify the combined amount of VRAM to `nnUNetv2_plan_experiment` as this
91
+ may result in patch sizes that are too large to be processed by individual GPUs. It is best to let this command run for
92
+ the VRAM budget of one GPU, and then manually edit the plans file to increase the batch size. You can use [configuration inheritance](explanation_plans_files.md).
93
+ In the configurations dictionary of the generated plans JSON file, add the following entry:
94
+
95
+ ```json
96
+ "3d_fullres_bsXX": {
97
+ "inherits_from": "3d_fullres",
98
+ "batch_size": XX
99
+ },
100
+ ```
101
+ Where XX is the new batch size. If 3d_fullres has a batch size of 2 for one GPU and you are planning to scale to 8 GPUs, make the new batch size 2x8=16!
102
+ You can then train the new configuration using nnU-Net's multi-GPU settings:
103
+
104
+ ```bash
105
+ nnUNetv2_train DATASETID 3d_fullres_bsXX FOLD -p nnUNetResEncUNetPlans_80G -num_gpus 8
106
+ ```
107
+
108
+ ## Proposing a new segmentation method? Benchmark the right way!
109
+ When benchmarking new segmentation methods against nnU-Net, we encourage to benchmark against the residual encoder
110
+ variants. For a fair comparison, pick the variant that most closely matches the GPU memory and compute
111
+ requirements of your method!
112
+
113
+
114
+ ## References
115
+ [1] Isensee, Fabian, et al. "nnU-Net: a self-configuring method for deep learning-based biomedical image segmentation." Nature methods 18.2 (2021): 203-211.\
116
+ [2] Roy, Saikat, et al. "Mednext: transformer-driven scaling of convnets for medical image segmentation." International Conference on Medical Image Computing and Computer-Assisted Intervention. Cham: Springer Nature Switzerland, 2023.\
117
+ [3] Huang, Ziyan, et al. "Stu-net: Scalable and transferable medical image segmentation models empowered by large-scale supervised pre-training." arXiv preprint arXiv:2304.06716 (2023).\
118
+ [4] Hatamizadeh, Ali, et al. "Swin unetr: Swin transformers for semantic segmentation of brain tumors in mri images." International MICCAI Brainlesion Workshop. Cham: Springer International Publishing, 2021.\
119
+ [5] He, Yufan, et al. "Swinunetr-v2: Stronger swin transformers with stagewise convolutions for 3d medical image segmentation." International Conference on Medical Image Computing and Computer-Assisted Intervention. Cham: Springer Nature Switzerland, 2023.\
120
+ [6] Zhou, Hong-Yu, et al. "nnformer: Interleaved transformer for volumetric segmentation." arXiv preprint arXiv:2109.03201 (2021).\
121
+ [7] Xie, Yutong, et al. "Cotr: Efficiently bridging cnn and transformer for 3d medical image segmentation." Medical Image Computing and Computer Assisted Intervention–MICCAI 2021: 24th International Conference, Strasbourg, France, September 27–October 1, 2021, Proceedings, Part III 24. Springer International Publishing, 2021.\
122
+ [8] Ma, Jun, Feifei Li, and Bo Wang. "U-mamba: Enhancing long-range dependency for biomedical image segmentation." arXiv preprint arXiv:2401.04722 (2024).\
123
+ [9] Myronenko, Andriy. "3D MRI brain tumor segmentation using autoencoder regularization." Brainlesion: Glioma, Multiple Sclerosis, Stroke and Traumatic Brain Injuries: 4th International Workshop, BrainLes 2018, Held in Conjunction with MICCAI 2018, Granada, Spain, September 16, 2018, Revised Selected Papers, Part II 4. Springer International Publishing, 2019.\
124
+ [10] He, Yufan, et al. "Dints: Differentiable neural network topology search for 3d medical image segmentation." Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2021.\
125
+ [11] Auto3DSeg, MONAI 1.3.0, [LINK](https://github.com/Project-MONAI/tutorials/tree/ed8854fa19faa49083f48abf25a2c30ab9ac1c6b/auto3dseg)
126
+
nnUNet/documentation/run_inference_with_pretrained_models.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # How to run inference with pretrained models
2
+ **Important:** Pretrained weights from nnU-Net v1 are NOT compatible with V2. You will need to retrain with the new
3
+ version. But honestly, you already have a fully trained model with which you can run inference (in v1), so
4
+ just continue using that!
5
+
6
+ Not yet available for V2 :-(
7
+ If you wish to run inference with pretrained models, check out the old nnU-Net for now. We are working on this full steam!
nnUNet/documentation/set_environment_variables.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to set environment variables
2
+
3
+ nnU-Net requires some environment variables so that it always knows where the raw data, preprocessed data and trained
4
+ models are. Depending on the operating system, these environment variables need to be set in different ways.
5
+
6
+ Variables can either be set permanently (recommended!) or you can decide to set them every time you call nnU-Net.
7
+
8
+ # Linux & MacOS
9
+
10
+ ## Permanent
11
+ Locate the `.bashrc` file in your home folder and add the following lines to the bottom:
12
+
13
+ ```bash
14
+ export nnUNet_raw="/media/fabian/nnUNet_raw"
15
+ export nnUNet_preprocessed="/media/fabian/nnUNet_preprocessed"
16
+ export nnUNet_results="/media/fabian/nnUNet_results"
17
+ ```
18
+
19
+ (Of course you need to adapt the paths to the actual folders you intend to use).
20
+ If you are using a different shell, such as zsh, you will need to find the correct script for it. For zsh this is `.zshrc`.
21
+
22
+ ## Temporary
23
+ Just execute the following lines whenever you run nnU-Net:
24
+ ```bash
25
+ export nnUNet_raw="/media/fabian/nnUNet_raw"
26
+ export nnUNet_preprocessed="/media/fabian/nnUNet_preprocessed"
27
+ export nnUNet_results="/media/fabian/nnUNet_results"
28
+ ```
29
+ (Of course you need to adapt the paths to the actual folders you intend to use).
30
+
31
+ Important: These variables will be deleted if you close your terminal! They will also only apply to the current
32
+ terminal window and DO NOT transfer to other terminals!
33
+
34
+ Alternatively you can also just prefix them to your nnU-Net commands:
35
+
36
+ `nnUNet_results="/media/fabian/nnUNet_results" nnUNet_preprocessed="/media/fabian/nnUNet_preprocessed" nnUNetv2_train[...]`
37
+
38
+ ## Verify that environment parameters are set
39
+ You can always execute `echo ${nnUNet_raw}` etc to print the environment variables. This will return an empty string if
40
+ they were not set.
41
+
42
+ # Windows
43
+ Useful links:
44
+ - [https://www3.ntu.edu.sg](https://www3.ntu.edu.sg/home/ehchua/programming/howto/Environment_Variables.html#:~:text=To%20set%20(or%20change)%20a,it%20to%20an%20empty%20string.)
45
+ - [https://phoenixnap.com](https://phoenixnap.com/kb/windows-set-environment-variable)
46
+
47
+ ## Permanent
48
+ See `Set Environment Variable in Windows via GUI` [here](https://phoenixnap.com/kb/windows-set-environment-variable).
49
+ Or read about setx (command prompt).
50
+
51
+ ## Temporary
52
+ Just execute the following before you run nnU-Net:
53
+
54
+ (PowerShell)
55
+ ```PowerShell
56
+ $Env:nnUNet_raw = "C:/Users/fabian/nnUNet_raw"
57
+ $Env:nnUNet_preprocessed = "C:/Users/fabian/nnUNet_preprocessed"
58
+ $Env:nnUNet_results = "C:/Users/fabian/nnUNet_results"
59
+ ```
60
+
61
+ (Command Prompt)
62
+ ```Command Prompt
63
+ set nnUNet_raw=C:/Users/fabian/nnUNet_raw
64
+ set nnUNet_preprocessed=C:/Users/fabian/nnUNet_preprocessed
65
+ set nnUNet_results=C:/Users/fabian/fabian/nnUNet_results
66
+ ```
67
+
68
+ (Of course you need to adapt the paths to the actual folders you intend to use).
69
+
70
+ Important: These variables will be deleted if you close your session! They will also only apply to the current
71
+ window and DO NOT transfer to other sessions!
72
+
73
+ ## Verify that environment parameters are set
74
+ Printing in Windows works differently depending on the environment you are in:
75
+
76
+ PowerShell: `echo $Env:[variable_name]`
77
+
78
+ Command Prompt: `echo %variable_name%`
nnUNet/documentation/setting_up_paths.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Setting up Paths
2
+
3
+ nnU-Net relies on environment variables to know where raw data, preprocessed data and trained model weights are stored.
4
+ To use the full functionality of nnU-Net, the following three environment variables must be set:
5
+
6
+ 1) `nnUNet_raw`: This is where you place the raw datasets. This folder will have one subfolder for each dataset names
7
+ DatasetXXX_YYY where XXX is a 3-digit identifier (such as 001, 002, 043, 999, ...) and YYY is the (unique)
8
+ dataset name. The datasets must be in nnU-Net format, see [here](dataset_format.md).
9
+
10
+ Example tree structure:
11
+ ```
12
+ nnUNet_raw/Dataset001_NAME1
13
+ β”œβ”€β”€ dataset.json
14
+ β”œβ”€β”€ imagesTr
15
+ β”‚Β Β  β”œβ”€β”€ ...
16
+ β”œβ”€β”€ imagesTs
17
+ β”‚Β Β  β”œβ”€β”€ ...
18
+ └── labelsTr
19
+ β”œβ”€β”€ ...
20
+ nnUNet_raw/Dataset002_NAME2
21
+ β”œβ”€β”€ dataset.json
22
+ β”œβ”€β”€ imagesTr
23
+ β”‚Β Β  β”œβ”€β”€ ...
24
+ β”œβ”€β”€ imagesTs
25
+ β”‚Β Β  β”œβ”€β”€ ...
26
+ └── labelsTr
27
+ β”œβ”€β”€ ...
28
+ ```
29
+
30
+ 2) `nnUNet_preprocessed`: This is the folder where the preprocessed data will be saved. The data will also be read from
31
+ this folder during training. It is important that this folder is located on a drive with low access latency and high
32
+ throughput (such as a nvme SSD (PCIe gen 3 is sufficient)).
33
+
34
+ 3) `nnUNet_results`: This specifies where nnU-Net will save the model weights. If pretrained models are downloaded, this
35
+ is where it will save them.
36
+
37
+ ### How to set environment variables
38
+
39
+ See [here](set_environment_variables.md).
nnUNet/documentation/tldr_migration_guide_from_v1.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TLDR Migration Guide from nnU-Net V1
2
+
3
+ - nnU-Net V2 can be installed simultaneously with V1. They won't get in each other's way
4
+ - The environment variables needed for V2 have slightly different names. Read [this](setting_up_paths.md).
5
+ - nnU-Net V2 datasets are called DatasetXXX_NAME. Not Task.
6
+ - Datasets have the same structure (imagesTr, labelsTr, dataset.json) but we now support more
7
+ [file types](dataset_format.md#supported-file-formats). The dataset.json is simplified. Use `generate_dataset_json`
8
+ from nnunetv2.dataset_conversion.generate_dataset_json.py.
9
+ - Careful: labels are now no longer declared as value:name but name:value. This has to do with [hierarchical labels](region_based_training.md).
10
+ - nnU-Net v2 commands start with `nnUNetv2...`. They work mostly (but not entirely) the same. Just use the `-h` option.
11
+ - You can transfer your V1 raw datasets to V2 with `nnUNetv2_convert_old_nnUNet_dataset`. You cannot transfer trained
12
+ models. Continue to use the old nnU-Net Version for making inference with those.
13
+ - These are the commands you are most likely to be using (in that order)
14
+ - `nnUNetv2_plan_and_preprocess`. Example: `nnUNetv2_plan_and_preprocess -d 2`
15
+ - `nnUNetv2_train`. Example: `nnUNetv2_train 2 3d_fullres 0`
16
+ - `nnUNetv2_find_best_configuration`. Example: `nnUNetv2_find_best_configuration 2 -c 2d 3d_fullres`. This command
17
+ will now create a `inference_instructions.txt` file in your `nnUNet_preprocessed/DatasetXXX_NAME/` folder which
18
+ tells you exactly how to do inference.
19
+ - `nnUNetv2_predict`. Example: `nnUNetv2_predict -i INPUT_FOLDER -o OUTPUT_FOLDER -c 3d_fullres -d 2`
20
+ - `nnUNetv2_apply_postprocessing` (see inference_instructions.txt)