├── setup.cfg ├── testpytorch.py ├── nnunet ├── run │ ├── __init__.py │ ├── default_configuration.py │ └── run_training.py ├── evaluation │ ├── __init__.py │ ├── model_selection │ │ ├── __init__.py │ │ ├── collect_all_fold0_results_and_summarize_in_one_csv.py │ │ ├── figure_out_what_to_submit.py │ │ ├── summarize_results_in_one_json.py │ │ ├── ensemble.py │ │ └── summarize_results_with_plans.py │ ├── collect_results_files.py │ ├── add_mean_dice_to_json.py │ ├── surface_dice.py │ └── add_dummy_task_with_mean_over_all_tasks.py ├── inference │ ├── __init__.py │ └── ensemble_predictions.py ├── training │ ├── __init__.py │ ├── dataloading │ │ └── __init__.py │ ├── cascade_stuff │ │ ├── __init__.py │ │ └── predict_next_stage.py │ ├── data_augmentation │ │ ├── __init__.py │ │ ├── custom_transforms.py │ │ └── pyramid_augmentations.py │ ├── loss_functions │ │ ├── __init__.py │ │ ├── ND_Crossentropy.py │ │ ├── TopK_loss.py │ │ ├── GDL.py │ │ └── dice_loss.py │ ├── network_training │ │ ├── __init__.py │ │ └── nnUNet_variants │ │ │ ├── __init__.py │ │ │ ├── nnUNetTrainerCE.py │ │ │ ├── nnUNetTrainerNoMirroring.py │ │ │ └── nnUNetTrainerNoDA.py │ └── model_restore.py ├── utilities │ ├── __init__.py │ ├── nd_softmax.py │ ├── one_hot_encoding.py │ ├── to_torch.py │ ├── tensor_utilities.py │ └── online_evaluation_metrics.py ├── preprocessing │ └── __init__.py ├── experiment_planning │ ├── __init__.py │ ├── configuration.py │ ├── find_classes_in_slice.py │ └── summarize_plans.py ├── network_architecture │ ├── __init__.py │ └── initialization.py ├── dataset_conversion │ ├── __init__.py │ ├── BeyondCranialVaultAbdominalOrganSegmentation.py │ ├── readme.md │ ├── Promise2012.py │ ├── BraTS_2019.py │ ├── LiverTumorSegmentationChallenge.py │ └── AutomaticCardiacDetectionChallenge.py ├── nnUNet_base │ ├── nnUNet_raw │ │ ├── Task02_Heart │ │ │ ├── ._imagesTr │ │ │ ├── ._imagesTs │ │ │ ├── ._labelsTr │ │ │ ├── ._dataset.json │ │ │ └── dataset.json │ │ └── Task11_LiverTumor │ │ │ ├── ._dataset.json │ │ │ ├── imagesTs │ │ │ ├── la_2732090.json │ │ │ ├── la_2724908.json │ │ │ ├── la_2461545.json │ │ │ ├── la_2714255.json │ │ │ ├── la_2725093.json │ │ │ ├── la_2727814.json │ │ │ ├── la_2726620.json │ │ │ └── la_2723303.json │ │ │ └── dataset.json │ ├── nnUNet_raw_cropped │ │ ├── Task02_Heart │ │ │ └── dataset.json │ │ └── Task11_LiverTumor │ │ │ └── dataset.json │ └── nnUNet_raw_splitted │ │ ├── Task02_Heart │ │ └── dataset.json │ │ └── Task11_LiverTumor │ │ └── dataset.json ├── testtorch.py ├── __init__.py ├── .vscode │ └── launch.json ├── common.py ├── nnUNet_preprocessed │ └── Task02_Heart │ │ └── dataset.json ├── paths.py └── nnUNet_result │ └── nnUNet │ └── 3d_fullres │ └── Task02_Heart │ └── nnUNetTrainer__nnUNetPlans │ └── fold_2 │ └── debug.json ├── docker ├── predict.sh ├── Dockerfile └── readme.md ├── setup.py └── .gitignore /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = readme.md -------------------------------------------------------------------------------- /testpytorch.py: -------------------------------------------------------------------------------- 1 | import torch 2 | a = torch.cuda.is_available() 3 | print (a) -------------------------------------------------------------------------------- /nnunet/run/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/evaluation/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/inference/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/utilities/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/preprocessing/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/experiment_planning/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/network_architecture/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/dataloading/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/dataset_conversion/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from . import * -------------------------------------------------------------------------------- /nnunet/evaluation/model_selection/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/cascade_stuff/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/data_augmentation/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/loss_functions/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/network_training/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/training/network_training/nnUNet_variants/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from . import * -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task02_Heart/._imagesTr: -------------------------------------------------------------------------------- 1 | Mac OS X  2FxATTRxx -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task02_Heart/._imagesTs: -------------------------------------------------------------------------------- 1 | Mac OS X  2FxATTRxx -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task02_Heart/._labelsTr: -------------------------------------------------------------------------------- 1 | Mac OS X  2FxATTRxx -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task02_Heart/._dataset.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyiLu/nnUNetLearnOnWindows/HEAD/nnunet/nnUNet_base/nnUNet_raw/Task02_Heart/._dataset.json -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/._dataset.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyiLu/nnUNetLearnOnWindows/HEAD/nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/._dataset.json -------------------------------------------------------------------------------- /docker/predict.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo '===============================' 4 | echo 'Run nnUnet Prediction' 5 | echo '===============================' 6 | cd nnUNet/nnunet 7 | # Change the call according to your parameters. 8 | python3 inference/predict_simple.py -i $INPUTDIR -o $OUTPUTDIR -t $TASK_NAME -tr nnUNetTrainer -m 3d_fullres 9 | -------------------------------------------------------------------------------- /nnunet/testtorch.py: -------------------------------------------------------------------------------- 1 | #import torch 2 | #a = torch.cuda.is_available() 3 | #print(a) 4 | 5 | 6 | import nibabel as nib 7 | import skimage.io as io 8 | import numpy as np 9 | img=nib.load('/nnUNet/nnunet/nnUNet_result/la_002.nii') 10 | img_arr=img.get_data() 11 | 12 | img_arr=np.squeeze(img_arr) 13 | list_a = img_arr.tolist() 14 | 15 | print(max(list_a)) 16 | 17 | io.imshow(img_arr[1]) -------------------------------------------------------------------------------- /nnunet/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | print("Please cite the following paper when using nnUNet:\n\nIsensee, Fabian, et al. \"nnU-Net: Breaking the Spell on " 3 | "Successful Medical Image Segmentation.\" arXiv preprint arXiv:1904.08128 (2019).") 4 | print("\nIf you have questions or suggestions, feel free to open an issue at https://github.com/MIC-DKFZ/nnUNet") 5 | 6 | from . import * -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:10.0-runtime-ubuntu18.04 2 | 3 | RUN apt-get update -y && apt-get install -y \ 4 | python3.6 \ 5 | python3-pip \ 6 | curl \ 7 | git 8 | 9 | RUN pip3 install numpy && \ 10 | pip3 install https://download.pytorch.org/whl/cu100/torch-1.1.0-cp36-cp36m-linux_x86_64.whl 11 | 12 | RUN git clone https://github.com/MIC-DKFZ/nnUNet.git 13 | RUN cd nnUNet && pip3 install -r requirements.txt && pip3 install -e . 14 | 15 | COPY predict.sh / 16 | 17 | ENV INPUTDIR "/opt/input/" 18 | ENV OUTPUTDIR "/opt/output/" 19 | ENV RESULTS_FOLDER "/opt/results/" 20 | 21 | RUN mkdir ${RESULTS_FOLDER} 22 | 23 | ENTRYPOINT [ "/predict.sh" ] 24 | -------------------------------------------------------------------------------- /nnunet/training/network_training/nnUNet_variants/nnUNetTrainerCE.py: -------------------------------------------------------------------------------- 1 | from nnunet.training.loss_functions.ND_Crossentropy import CrossentropyND 2 | from nnunet.training.network_training.nnUNetTrainer import nnUNetTrainer 3 | 4 | 5 | class nnUNetTrainerCE(nnUNetTrainer): 6 | def __init__(self, plans_file, fold, output_folder=None, dataset_directory=None, batch_dice=True, stage=None, 7 | unpack_data=True, deterministic=True, fp16=False): 8 | super(nnUNetTrainerCE, self).__init__(plans_file, fold, output_folder, dataset_directory, batch_dice, stage, 9 | unpack_data, deterministic, fp16) 10 | self.loss = CrossentropyND() 11 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2732090.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "GE", 4 | "ManufacturersModelName": "Revolution_GSI", 5 | "InstitutionName": "Shanghai_Renji_Hospital", 6 | "StationName": "HD02", 7 | "PatientPosition": "FFS", 8 | "ProcedureStepDescription": "ABD", 9 | "SoftwareVersions": "sles_hde.132", 10 | "SeriesDescription": "Recon_2:", 11 | "ProtocolName": "Recon_2:", 12 | "ScanOptions": "HELICAL_MODE", 13 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL"], 14 | "SeriesNumber": 1001, 15 | "AcquisitionTime": "09:55:39.591060", 16 | "AcquisitionNumber": 2, 17 | "XRayExposure": 13, 18 | "ReconMatrixPE": 512, 19 | "ImageOrientationPatientDICOM": [ 20 | 1, 21 | 0, 22 | 0, 23 | 0, 24 | 1, 25 | 0 ], 26 | "ConversionSoftware": "dcm2niix", 27 | "ConversionSoftwareVersion": "v1.0.20190902" 28 | } 29 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2724908.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "UIH", 4 | "ManufacturersModelName": "uCT_760", 5 | "InstitutionName": "renji_hospital", 6 | "InstitutionAddress": "a_a_a_a_a_a_a_a_a_e_1678a", 7 | "DeviceSerialNumber": "600095", 8 | "BodyPartExamined": "ABDOMEN", 9 | "PatientPosition": "FFS", 10 | "SoftwareVersions": "R001", 11 | "SeriesDescription": "1.25_x_1.25", 12 | "ProtocolName": "Aorta_Delay", 13 | "ScanOptions": "HELICAL", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "HELICAL"], 15 | "SeriesNumber": 702, 16 | "AcquisitionTime": "03:04:51.909000", 17 | "AcquisitionNumber": 7, 18 | "XRayExposure": 121, 19 | "ReconMatrixPE": 512, 20 | "ImageOrientationPatientDICOM": [ 21 | 1, 22 | -0, 23 | -0, 24 | 0, 25 | 1, 26 | 0 ], 27 | "ConversionSoftware": "dcm2niix", 28 | "ConversionSoftwareVersion": "v1.0.20190902" 29 | } 30 | -------------------------------------------------------------------------------- /nnunet/utilities/nd_softmax.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import torch 16 | 17 | 18 | def softmax_helper(x): 19 | rpt = [1 for _ in range(len(x.size()))] 20 | rpt[1] = x.size(1) 21 | x_max = x.max(1, keepdim=True)[0].repeat(*rpt) 22 | e_x = torch.exp(x - x_max) 23 | return e_x / e_x.sum(1, keepdim=True).repeat(*rpt) 24 | -------------------------------------------------------------------------------- /nnunet/utilities/one_hot_encoding.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | 17 | 18 | def to_one_hot(seg, all_seg_labels=None): 19 | if all_seg_labels is None: 20 | all_seg_labels = np.unique(seg) 21 | result = np.zeros((len(all_seg_labels), *seg.shape), dtype=seg.dtype) 22 | for i, l in enumerate(all_seg_labels): 23 | result[i][seg == l] = 1 24 | return result 25 | -------------------------------------------------------------------------------- /nnunet/utilities/to_torch.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import torch 16 | 17 | 18 | def maybe_to_torch(d): 19 | if isinstance(d, list): 20 | d = [maybe_to_torch(i) if not isinstance(i, torch.Tensor) else i for i in d] 21 | elif not isinstance(d, torch.Tensor): 22 | d = torch.from_numpy(d).float() 23 | return d 24 | 25 | 26 | def to_cuda(data, non_blocking=True, gpu_id=0): 27 | if isinstance(data, list): 28 | data = [i.cuda(gpu_id, non_blocking=non_blocking) for i in data] 29 | else: 30 | data = data.cuda(gpu_id, non_blocking=True) 31 | return data 32 | -------------------------------------------------------------------------------- /nnunet/dataset_conversion/BeyondCranialVaultAbdominalOrganSegmentation.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from batchgenerators.utilities.file_and_folder_operations import * 16 | import shutil 17 | 18 | 19 | if __name__ == "__main__": 20 | indir = "/home/fabian/drives/datasets/results/nnUNetOutput_final/predicted_test_sets/Task17_AbdominalOrganSegmentation/ensemble_3d_fullres_cascade_and_3d_fullres" 21 | outdir = "/home/fabian/drives/datasets/results/nnUNetOutput_final/predicted_test_sets/Task17_AbdominalOrganSegmentation/submit" 22 | files = subfiles(indir, suffix='nii.gz', prefix="img", join=False) 23 | maybe_mkdir_p(outdir) 24 | for f in files: 25 | outname = "label" + f[3:] 26 | shutil.copy(join(indir, f), join(outdir, outname)) 27 | -------------------------------------------------------------------------------- /nnunet/training/loss_functions/ND_Crossentropy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import torch.nn 16 | 17 | 18 | class CrossentropyND(torch.nn.CrossEntropyLoss): 19 | """ 20 | Network has to have NO NONLINEARITY! 21 | """ 22 | def forward(self, inp, target): 23 | target = target.long() 24 | num_classes = inp.size()[1] 25 | 26 | i0 = 1 27 | i1 = 2 28 | 29 | while i1 < len(inp.shape): # this is ugly but torch only allows to transpose two axes at once 30 | inp = inp.transpose(i0, i1) 31 | i0 += 1 32 | i1 += 1 33 | 34 | inp = inp.contiguous() 35 | inp = inp.view(-1, num_classes) 36 | 37 | target = target.view(-1,) 38 | 39 | return super(CrossentropyND, self).forward(inp, target) 40 | -------------------------------------------------------------------------------- /nnunet/training/loss_functions/TopK_loss.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import torch 17 | from nnunet.training.loss_functions.ND_Crossentropy import CrossentropyND 18 | 19 | 20 | class TopKLoss(CrossentropyND): 21 | """ 22 | Network has to have NO LINEARITY! 23 | """ 24 | def __init__(self, weight=None, ignore_index=-100, k=10): 25 | self.k = k 26 | super(TopKLoss, self).__init__(weight, False, ignore_index, reduce=False) 27 | 28 | def forward(self, inp, target): 29 | target = target[:, 0].long() 30 | res = super(TopKLoss, self).forward(inp, target) 31 | num_voxels = np.prod(res.shape, dtype=np.int64) 32 | res, _ = torch.topk(res.view((-1, )), int(num_voxels * self.k / 100), sorted=False) 33 | return res.mean() 34 | -------------------------------------------------------------------------------- /nnunet/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: 当前文件", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal", 13 | "args": [ 14 | "-t", "Task11_LiverTumor", 15 | //"-pl", "1", 16 | //"-s", "0", 17 | //"-i", "/nnUNetLearnOnWindows/nnunet/nnUNet_base/nnUNet_raw_splitted/Task02_Heart/imagesTs", 18 | //"-o", "/nnUNetLearnOnWindows/nnUNet/nnUNet_result/", 19 | //"-t", "Task11_LiverTumor", 20 | //"-tr", "nnUNetTrainer", 21 | //"-m", "3d_fullres", 22 | //"-f", "2" 23 | //"3d_fullres", "nnUNetTrainer", "Task02_Heart", "2", "--ndet" 24 | ], 25 | "env": { 26 | "CUDA_VISIBLE_DEVICES": "0", 27 | "nnUNet_base": "/nnUNetLearnOnWindows/nnunet/nnUNet_base/", 28 | "nnUNet_preprocessed": "/nnUNetLearnOnWindows/nnunet/nnUNet_preprocessed/", 29 | "RESULTS_FOLDER": "/nnUNetLearnOnWindows/nnUNet/nnUNet_result/", 30 | "OMP_NUM_THREADS": "1" 31 | }, 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /nnunet/experiment_planning/configuration.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # experiment planning 16 | 17 | # a class in a patient will be set to background if it has less than X times the volume of the minimum volume of 18 | # that class in the training data 19 | MIN_SIZE_PER_CLASS_FACTOR = 0.5 20 | TARGET_SPACING_PERCENTILE = 50 21 | 22 | FEATUREMAP_MIN_EDGE_LENGTH_BOTTLENECK = 4 23 | FEATUREMAP_MIN_EDGE_LENGTH_BOTTLENECK2 = 6 24 | RESAMPLING_SEPARATE_Z_ANISOTROPY_THRESHOLD = 3 # z is defined as the axis with the highest spacing, also used to 25 | # determine whether we use 2d or 3d data augmentation 26 | 27 | HOW_MUCH_OF_A_PATIENT_MUST_THE_NETWORK_SEE_AT_STAGE0 = 4 # 1/4 of a patient 28 | 29 | batch_size_covers_max_percent_of_dataset = 0.05 # all samples in the batch together cannot cover more than 5% of the entire dataset 30 | dataset_min_batch_size_cap = 2 # if the dataset size dictates a very small batch size, do not make that smaller than 3 (if architecture dictates smaller batch size then use the smaller one of these two) 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /nnunet/common.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pickle 3 | import json 4 | 5 | def subdirs(folder, join=True, prefix=None, suffix=None, sort=True): 6 | if join: 7 | l = os.path.join 8 | else: 9 | l = lambda x, y: y 10 | res = [l(folder, i) for i in os.listdir(folder) if os.path.isdir(os.path.join(folder, i)) 11 | and (prefix is None or i.startswith(prefix)) 12 | and (suffix is None or i.endswith(suffix))] 13 | if sort: 14 | res.sort() 15 | return res 16 | 17 | 18 | def subfiles(folder, join=True, prefix=None, suffix=None, sort=True): 19 | if join: 20 | l = os.path.join 21 | else: 22 | l = lambda x, y: y 23 | res = [l(folder, i) for i in os.listdir(folder) if os.path.isfile(os.path.join(folder, i)) 24 | and (prefix is None or i.startswith(prefix)) 25 | and (suffix is None or i.endswith(suffix))] 26 | if sort: 27 | res.sort() 28 | return res 29 | 30 | subfolders = subdirs # I am tired of confusing those 31 | 32 | def maybe_mkdir_p(directory): 33 | splits = directory.split("/")[1:] 34 | for i in range(0, len(splits)): 35 | if not os.path.isdir(os.path.join("/", *splits[:i+1])): 36 | try: 37 | os.mkdir(os.path.join("/", *splits[:i+1])) 38 | except FileExistsError: 39 | # this can sometimes happen when two jobs try to create the same directory at the same time, 40 | # especially on network drives. 41 | print("WARNING: Folder %s already existed and does not need to be created" % directory) 42 | 43 | def join(path_base, target_path): 44 | return path_base + "/" +target_path 45 | 46 | isdir = os.path.isdir 47 | isfile = os.path.isfile 48 | listdir = os.listdir -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='nnunet', 4 | packages=["nnunet", 5 | "nnunet.dataset_conversion", 6 | "nnunet.evaluation", 7 | "nnunet.evaluation.model_selection", 8 | "nnunet.experiment_planning", 9 | "nnunet.inference", 10 | "nnunet.network_architecture", 11 | "nnunet.preprocessing", 12 | "nnunet.run", 13 | "nnunet.training", 14 | "nnunet.training.cascade_stuff", 15 | "nnunet.training.data_augmentation", 16 | "nnunet.training.dataloading", 17 | "nnunet.training.loss_functions", 18 | "nnunet.training.network_training", 19 | "nnunet.training.network_training.nnUNet_variants", 20 | "nnunet.utilities", 21 | ], 22 | version='0.6', 23 | description='no new-net. Framework for out-of-the box medical image segmentation.', 24 | url='https://github.com/MIC-DKFZ/nnUNet', 25 | author='Division of Medical Image Computing, German Cancer Research Center', 26 | author_email='f.isensee@dkfz-heidelberg.de', 27 | license='Apache License Version 2.0, January 2004', 28 | install_requires=[ 29 | "torch", 30 | "tqdm", 31 | "dicom2nifti", 32 | "scikit-image>=0.14", 33 | "medpy", 34 | "scipy", 35 | "batchgenerators>=0.19.4", 36 | "numpy", 37 | "sklearn", 38 | "SimpleITK", 39 | "pandas", 40 | ], 41 | keywords=['deep learning', 'image segmentation', 'medical image analysis', 42 | 'medical image segmentation', 'nnU-Net', 'nnunet'] 43 | ) 44 | -------------------------------------------------------------------------------- /nnunet/network_architecture/initialization.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from torch import nn 16 | 17 | 18 | class InitWeights_He(object): 19 | def __init__(self, neg_slope=1e-2): 20 | self.neg_slope = neg_slope 21 | 22 | def __call__(self, module): 23 | if isinstance(module, nn.Conv3d) or isinstance(module, nn.Conv2d) or isinstance(module, nn.ConvTranspose2d) or isinstance(module, nn.ConvTranspose3d): 24 | module.weight = nn.init.kaiming_normal_(module.weight, a=1e-2) 25 | if module.bias is not None: 26 | module.bias = nn.init.constant_(module.bias, 0) 27 | 28 | 29 | class InitWeights_XavierUniform(object): 30 | def __init__(self, gain=1): 31 | self.gain = gain 32 | 33 | def __call__(self, module): 34 | if isinstance(module, nn.Conv3d) or isinstance(module, nn.Conv2d) or isinstance(module, nn.ConvTranspose2d) or isinstance(module, nn.ConvTranspose3d): 35 | module.weight = nn.init.xavier_uniform_(module.weight, self.gain) 36 | if module.bias is not None: 37 | module.bias = nn.init.constant_(module.bias, 0) 38 | -------------------------------------------------------------------------------- /nnunet/utilities/tensor_utilities.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import torch 17 | 18 | 19 | def sum_tensor(inp, axes, keepdim=False): 20 | axes = np.unique(axes).astype(int) 21 | if keepdim: 22 | for ax in axes: 23 | inp = inp.sum(int(ax), keepdim=True) 24 | else: 25 | for ax in sorted(axes, reverse=True): 26 | inp = inp.sum(int(ax)) 27 | return inp 28 | 29 | 30 | def mean_tensor(inp, axes, keepdim=False): 31 | axes = np.unique(axes).astype(int) 32 | if keepdim: 33 | for ax in axes: 34 | inp = inp.mean(int(ax), keepdim=True) 35 | else: 36 | for ax in sorted(axes, reverse=True): 37 | inp = inp.mean(int(ax)) 38 | return inp 39 | 40 | 41 | def flip(x, dim): 42 | """ 43 | flips the tensor at dimension dim (mirroring!) 44 | :param x: 45 | :param dim: 46 | :return: 47 | """ 48 | indices = [slice(None)] * x.dim() 49 | indices[dim] = torch.arange(x.size(dim) - 1, -1, -1, 50 | dtype=torch.long, device=x.device) 51 | return x[tuple(indices)] 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 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 | -------------------------------------------------------------------------------- /docker/readme.md: -------------------------------------------------------------------------------- 1 | ## nnUNet Docker Container (Inference) 2 | 3 | This container takes the [MIC-DKFZ/nnUNet](https://github.com/MIC-DKFZ/nnUNet) code and inherits from a cuda-10.0-runtime Docker image. Thus, the execution needs the docker nvidia tools to be runnable. 4 | 5 | This container is just used for inference and not for training. 6 | 7 | ### Input files naming 8 | Please rename your input files beforehand by encoding the modality into the file name. This encoding must correspond with the one given by the model configuration file (JSON). Here is an example with two modalities: 9 | 10 | ``` 11 | "modality": { 12 | "0": "CT", 13 | "1": "PET" 14 | }, 15 | ``` 16 | 17 | This example shows a dataset containing CT scan and a PET scan for each individual with correct naming. 18 | 19 | > - imageA_0000.nii.gz | CT 20 | > - imageA_0001.nii.gz | PET 21 | > - imageB_0000.nii.gz | CT 22 | > - imageB_0001.nii.gz | PET 23 | 24 | ### Environment-Parameters 25 | > - `INPUTDIR`: dir path to the input folder containing the compressed nifti files (nii.gz): 26 | > - `OUTPUTDIR`: dir path to the output folder containing all segmentations which were calculated during the container process 27 | > - `RESULTS_FOLDER`: this is very important to make right, because here you have to mount a directory containing your model dir tree starting from the 'nnUNet'-folder 28 | > Example: `\opt\path\data\nnUNet` is the correct path when your model lives here: `\opt\path\data\nnUNet\3d_fullres\Task17_AbdominalOrganSegmentation\nnUNetTrainer__nnUNetPlans`, 29 | 30 | > - `TASK_NAME`: this name should match the task name you used for training the model. The task name appears also within the `RESULTS_FOLDER` variable 31 | 32 | ___ 33 | You may want to change data loading or output structuring. This shows an example use which can be adjusted to a specific need. 34 | -------------------------------------------------------------------------------- /nnunet/training/network_training/nnUNet_variants/nnUNetTrainerNoMirroring.py: -------------------------------------------------------------------------------- 1 | from nnunet.training.network_training.nnUNetTrainer import nnUNetTrainer 2 | 3 | 4 | class nnUNetTrainerNoMirroring(nnUNetTrainer): 5 | def validate(self, do_mirroring=True, use_train_mode=False, tiled=True, step=2, save_softmax=True, 6 | use_gaussian=True, compute_global_dice=True, overwrite=True, validation_folder_name='validation_raw'): 7 | if do_mirroring: 8 | print("WARNING! do_mirroring was True but we cannot do that because we trained without mirroring. " 9 | "do_mirroring was set to False") 10 | do_mirroring = False 11 | return super().validate(do_mirroring, use_train_mode, tiled, step, save_softmax, use_gaussian, 12 | compute_global_dice, overwrite, validation_folder_name) 13 | 14 | def setup_DA_params(self): 15 | super().setup_DA_params() 16 | self.data_aug_params["do_mirror"] = False 17 | # you can also use self.data_aug_params["mirror_axes"] to set axes for mirroring. 18 | # Default is self.data_aug_params["mirror_axes"] = (0, 1, 2) 19 | # 0, 1, 2 are the first, second and thirs spatial axes. 20 | 21 | def predict_preprocessed_data_return_softmax(self, data, do_mirroring, num_repeats, use_train_mode, batch_size, 22 | mirror_axes, tiled, tile_in_z, step, min_size, use_gaussian, 23 | all_in_gpu=False): 24 | 25 | if do_mirroring: 26 | print("WARNING! do_mirroring was True but we cannot do that because we trained without mirroring. " 27 | "do_mirroring was set to False") 28 | return super().predict_preprocessed_data_return_softmax(data, do_mirroring, num_repeats, use_train_mode, 29 | batch_size, mirror_axes, tiled, tile_in_z, step, 30 | min_size, use_gaussian) 31 | -------------------------------------------------------------------------------- /nnunet/nnUNet_preprocessed/Task02_Heart/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LeftAtrium", 3 | "description": "Left atrium segmentation", 4 | "tensorImageSize": "3D", 5 | "reference": "King’s College London", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 04/05/2018", 8 | "modality": { 9 | "0": "MRI" 10 | }, 11 | "labels": { 12 | "0": "background", 13 | "1": "left atrium" 14 | }, 15 | "numTraining": 20, 16 | "numTest": 10, 17 | "training":[{"image":"./imagesTr/la_007.nii.gz","label":"./labelsTr/la_007.nii.gz"},{"image":"./imagesTr/la_019.nii.gz","label":"./labelsTr/la_019.nii.gz"},{"image":"./imagesTr/la_023.nii.gz","label":"./labelsTr/la_023.nii.gz"},{"image":"./imagesTr/la_005.nii.gz","label":"./labelsTr/la_005.nii.gz"},{"image":"./imagesTr/la_009.nii.gz","label":"./labelsTr/la_009.nii.gz"},{"image":"./imagesTr/la_017.nii.gz","label":"./labelsTr/la_017.nii.gz"},{"image":"./imagesTr/la_021.nii.gz","label":"./labelsTr/la_021.nii.gz"},{"image":"./imagesTr/la_003.nii.gz","label":"./labelsTr/la_003.nii.gz"},{"image":"./imagesTr/la_011.nii.gz","label":"./labelsTr/la_011.nii.gz"},{"image":"./imagesTr/la_030.nii.gz","label":"./labelsTr/la_030.nii.gz"},{"image":"./imagesTr/la_022.nii.gz","label":"./labelsTr/la_022.nii.gz"},{"image":"./imagesTr/la_018.nii.gz","label":"./labelsTr/la_018.nii.gz"},{"image":"./imagesTr/la_020.nii.gz","label":"./labelsTr/la_020.nii.gz"},{"image":"./imagesTr/la_004.nii.gz","label":"./labelsTr/la_004.nii.gz"},{"image":"./imagesTr/la_016.nii.gz","label":"./labelsTr/la_016.nii.gz"},{"image":"./imagesTr/la_024.nii.gz","label":"./labelsTr/la_024.nii.gz"},{"image":"./imagesTr/la_010.nii.gz","label":"./labelsTr/la_010.nii.gz"},{"image":"./imagesTr/la_029.nii.gz","label":"./labelsTr/la_029.nii.gz"},{"image":"./imagesTr/la_026.nii.gz","label":"./labelsTr/la_026.nii.gz"}], 18 | "test":["./imagesTs/la_015.nii.gz","./imagesTs/la_025.nii.gz","./imagesTs/la_013.nii.gz","./imagesTs/la_001.nii.gz","./imagesTs/la_027.nii.gz","./imagesTs/la_006.nii.gz","./imagesTs/la_008.nii.gz","./imagesTs/la_012.nii.gz","./imagesTs/la_028.nii.gz","./imagesTs/la_002.nii.gz"] 19 | } 20 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task02_Heart/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LeftAtrium", 3 | "description": "Left atrium segmentation", 4 | "tensorImageSize": "3D", 5 | "reference": "King’s College London", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 04/05/2018", 8 | "modality": { 9 | "0": "MRI" 10 | }, 11 | "labels": { 12 | "0": "background", 13 | "1": "left atrium" 14 | }, 15 | "numTraining": 20, 16 | "numTest": 10, 17 | "training":[{"image":"./imagesTr/la_007.nii.gz","label":"./labelsTr/la_007.nii.gz"},{"image":"./imagesTr/la_019.nii.gz","label":"./labelsTr/la_019.nii.gz"},{"image":"./imagesTr/la_023.nii.gz","label":"./labelsTr/la_023.nii.gz"},{"image":"./imagesTr/la_005.nii.gz","label":"./labelsTr/la_005.nii.gz"},{"image":"./imagesTr/la_009.nii.gz","label":"./labelsTr/la_009.nii.gz"},{"image":"./imagesTr/la_017.nii.gz","label":"./labelsTr/la_017.nii.gz"},{"image":"./imagesTr/la_021.nii.gz","label":"./labelsTr/la_021.nii.gz"},{"image":"./imagesTr/la_003.nii.gz","label":"./labelsTr/la_003.nii.gz"},{"image":"./imagesTr/la_011.nii.gz","label":"./labelsTr/la_011.nii.gz"},{"image":"./imagesTr/la_030.nii.gz","label":"./labelsTr/la_030.nii.gz"},{"image":"./imagesTr/la_022.nii.gz","label":"./labelsTr/la_022.nii.gz"},{"image":"./imagesTr/la_018.nii.gz","label":"./labelsTr/la_018.nii.gz"},{"image":"./imagesTr/la_020.nii.gz","label":"./labelsTr/la_020.nii.gz"},{"image":"./imagesTr/la_004.nii.gz","label":"./labelsTr/la_004.nii.gz"},{"image":"./imagesTr/la_016.nii.gz","label":"./labelsTr/la_016.nii.gz"},{"image":"./imagesTr/la_024.nii.gz","label":"./labelsTr/la_024.nii.gz"},{"image":"./imagesTr/la_010.nii.gz","label":"./labelsTr/la_010.nii.gz"},{"image":"./imagesTr/la_029.nii.gz","label":"./labelsTr/la_029.nii.gz"},{"image":"./imagesTr/la_026.nii.gz","label":"./labelsTr/la_026.nii.gz"}], 18 | "test":["./imagesTs/la_015.nii.gz","./imagesTs/la_025.nii.gz","./imagesTs/la_013.nii.gz","./imagesTs/la_001.nii.gz","./imagesTs/la_027.nii.gz","./imagesTs/la_006.nii.gz","./imagesTs/la_008.nii.gz","./imagesTs/la_012.nii.gz","./imagesTs/la_028.nii.gz","./imagesTs/la_002.nii.gz"] 19 | } 20 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw_cropped/Task02_Heart/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LeftAtrium", 3 | "description": "Left atrium segmentation", 4 | "tensorImageSize": "3D", 5 | "reference": "King’s College London", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 04/05/2018", 8 | "modality": { 9 | "0": "MRI" 10 | }, 11 | "labels": { 12 | "0": "background", 13 | "1": "left atrium" 14 | }, 15 | "numTraining": 20, 16 | "numTest": 10, 17 | "training":[{"image":"./imagesTr/la_007.nii.gz","label":"./labelsTr/la_007.nii.gz"},{"image":"./imagesTr/la_019.nii.gz","label":"./labelsTr/la_019.nii.gz"},{"image":"./imagesTr/la_023.nii.gz","label":"./labelsTr/la_023.nii.gz"},{"image":"./imagesTr/la_005.nii.gz","label":"./labelsTr/la_005.nii.gz"},{"image":"./imagesTr/la_009.nii.gz","label":"./labelsTr/la_009.nii.gz"},{"image":"./imagesTr/la_017.nii.gz","label":"./labelsTr/la_017.nii.gz"},{"image":"./imagesTr/la_021.nii.gz","label":"./labelsTr/la_021.nii.gz"},{"image":"./imagesTr/la_003.nii.gz","label":"./labelsTr/la_003.nii.gz"},{"image":"./imagesTr/la_011.nii.gz","label":"./labelsTr/la_011.nii.gz"},{"image":"./imagesTr/la_030.nii.gz","label":"./labelsTr/la_030.nii.gz"},{"image":"./imagesTr/la_022.nii.gz","label":"./labelsTr/la_022.nii.gz"},{"image":"./imagesTr/la_018.nii.gz","label":"./labelsTr/la_018.nii.gz"},{"image":"./imagesTr/la_020.nii.gz","label":"./labelsTr/la_020.nii.gz"},{"image":"./imagesTr/la_004.nii.gz","label":"./labelsTr/la_004.nii.gz"},{"image":"./imagesTr/la_016.nii.gz","label":"./labelsTr/la_016.nii.gz"},{"image":"./imagesTr/la_024.nii.gz","label":"./labelsTr/la_024.nii.gz"},{"image":"./imagesTr/la_010.nii.gz","label":"./labelsTr/la_010.nii.gz"},{"image":"./imagesTr/la_029.nii.gz","label":"./labelsTr/la_029.nii.gz"},{"image":"./imagesTr/la_026.nii.gz","label":"./labelsTr/la_026.nii.gz"}], 18 | "test":["./imagesTs/la_015.nii.gz","./imagesTs/la_025.nii.gz","./imagesTs/la_013.nii.gz","./imagesTs/la_001.nii.gz","./imagesTs/la_027.nii.gz","./imagesTs/la_006.nii.gz","./imagesTs/la_008.nii.gz","./imagesTs/la_012.nii.gz","./imagesTs/la_028.nii.gz","./imagesTs/la_002.nii.gz"] 19 | } 20 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw_splitted/Task02_Heart/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LeftAtrium", 3 | "description": "Left atrium segmentation", 4 | "tensorImageSize": "3D", 5 | "reference": "King’s College London", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 04/05/2018", 8 | "modality": { 9 | "0": "MRI" 10 | }, 11 | "labels": { 12 | "0": "background", 13 | "1": "left atrium" 14 | }, 15 | "numTraining": 20, 16 | "numTest": 10, 17 | "training":[{"image":"./imagesTr/la_007.nii.gz","label":"./labelsTr/la_007.nii.gz"},{"image":"./imagesTr/la_019.nii.gz","label":"./labelsTr/la_019.nii.gz"},{"image":"./imagesTr/la_023.nii.gz","label":"./labelsTr/la_023.nii.gz"},{"image":"./imagesTr/la_005.nii.gz","label":"./labelsTr/la_005.nii.gz"},{"image":"./imagesTr/la_009.nii.gz","label":"./labelsTr/la_009.nii.gz"},{"image":"./imagesTr/la_017.nii.gz","label":"./labelsTr/la_017.nii.gz"},{"image":"./imagesTr/la_021.nii.gz","label":"./labelsTr/la_021.nii.gz"},{"image":"./imagesTr/la_003.nii.gz","label":"./labelsTr/la_003.nii.gz"},{"image":"./imagesTr/la_011.nii.gz","label":"./labelsTr/la_011.nii.gz"},{"image":"./imagesTr/la_030.nii.gz","label":"./labelsTr/la_030.nii.gz"},{"image":"./imagesTr/la_022.nii.gz","label":"./labelsTr/la_022.nii.gz"},{"image":"./imagesTr/la_018.nii.gz","label":"./labelsTr/la_018.nii.gz"},{"image":"./imagesTr/la_020.nii.gz","label":"./labelsTr/la_020.nii.gz"},{"image":"./imagesTr/la_004.nii.gz","label":"./labelsTr/la_004.nii.gz"},{"image":"./imagesTr/la_016.nii.gz","label":"./labelsTr/la_016.nii.gz"},{"image":"./imagesTr/la_024.nii.gz","label":"./labelsTr/la_024.nii.gz"},{"image":"./imagesTr/la_010.nii.gz","label":"./labelsTr/la_010.nii.gz"},{"image":"./imagesTr/la_029.nii.gz","label":"./labelsTr/la_029.nii.gz"},{"image":"./imagesTr/la_026.nii.gz","label":"./labelsTr/la_026.nii.gz"}], 18 | "test":["./imagesTs/la_015.nii.gz","./imagesTs/la_025.nii.gz","./imagesTs/la_013.nii.gz","./imagesTs/la_001.nii.gz","./imagesTs/la_027.nii.gz","./imagesTs/la_006.nii.gz","./imagesTs/la_008.nii.gz","./imagesTs/la_012.nii.gz","./imagesTs/la_028.nii.gz","./imagesTs/la_002.nii.gz"] 19 | } 20 | -------------------------------------------------------------------------------- /nnunet/utilities/online_evaluation_metrics.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import torch 17 | 18 | 19 | def hard_dice(output, target): 20 | if isinstance(output, torch.Tensor): 21 | output = output.detach().cpu().numpy() 22 | if isinstance(target, torch.Tensor): 23 | target = target.detach().cpu().numpy() 24 | target = target[:, 0] 25 | # target is not one hot encoded, output is 26 | # target must be the CPU segemtnation, not tensor. output is pytorch tensor 27 | num_classes = output.shape[1] 28 | output = output.argmax(1) 29 | foreground_classes = np.arange(1, num_classes) 30 | all_tp = [] 31 | all_fp = [] 32 | all_fn = [] 33 | all_fg_dc = [] 34 | for s in range(target.shape[0]): 35 | tp = [] 36 | fp = [] 37 | fn = [] 38 | for c in foreground_classes: 39 | t_is_c = target[s] == c 40 | o_is_c = output[s] == c 41 | t_is_not_c = target[s] != c 42 | o_is_not_c = output[s] != c 43 | tp.append(np.sum(o_is_c & t_is_c)) 44 | fp.append(np.sum(o_is_c & t_is_not_c)) 45 | fn.append(np.sum(o_is_not_c & t_is_c)) 46 | foreground_dice = [2 * i / (2 * i + j + k + 1e-8) for i, j, k in zip(tp, fp, fn)] 47 | all_tp.append(tp) 48 | all_fp.append(fp) 49 | all_fn.append(fn) 50 | all_fg_dc.append(foreground_dice) 51 | return all_fg_dc, all_tp, all_fp, all_fn 52 | -------------------------------------------------------------------------------- /nnunet/evaluation/collect_results_files.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import shutil 17 | from batchgenerators.utilities.file_and_folder_operations import subdirs, subfiles 18 | 19 | 20 | def crawl_and_copy(current_folder, out_folder, prefix="fabian_", suffix="ummary.json"): 21 | """ 22 | This script will run recursively through all subfolders of current_folder and copy all files that end with 23 | suffix with some automatically generated prefix into out_folder 24 | :param current_folder: 25 | :param out_folder: 26 | :param prefix: 27 | :return: 28 | """ 29 | s = subdirs(current_folder, join=False) 30 | f = subfiles(current_folder, join=False) 31 | f = [i for i in f if i.endswith(suffix)] 32 | if current_folder.find("fold0") != -1: 33 | for fl in f: 34 | shutil.copy(os.path.join(current_folder, fl), os.path.join(out_folder, prefix+fl)) 35 | for su in s: 36 | if prefix == "": 37 | add = su 38 | else: 39 | add = "__" + su 40 | crawl_and_copy(os.path.join(current_folder, su), out_folder, prefix=prefix+add) 41 | 42 | 43 | if __name__ == "__main__": 44 | from nnunet.paths import network_training_output_dir 45 | output_folder = "/home/fabian/PhD/results/nnUNetV2/leaderboard" 46 | crawl_and_copy(network_training_output_dir, output_folder) 47 | from nnunet.evaluation.add_mean_dice_to_json import run_in_folder 48 | run_in_folder(output_folder) 49 | -------------------------------------------------------------------------------- /nnunet/evaluation/add_mean_dice_to_json.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import numpy as np 17 | from batchgenerators.utilities.file_and_folder_operations import subfiles 18 | from collections import OrderedDict 19 | 20 | 21 | def foreground_mean(filename): 22 | with open(filename, 'r') as f: 23 | res = json.load(f) 24 | class_ids = np.array([int(i) for i in res['results']['mean'].keys() if (i != 'mean')]) 25 | class_ids = class_ids[class_ids != 0] 26 | class_ids = class_ids[class_ids != -1] 27 | class_ids = class_ids[class_ids != 99] 28 | 29 | tmp = res['results']['mean'].get('99') 30 | if tmp is not None: 31 | _ = res['results']['mean'].pop('99') 32 | 33 | metrics = res['results']['mean']['1'].keys() 34 | res['results']['mean']["mean"] = OrderedDict() 35 | for m in metrics: 36 | foreground_values = [res['results']['mean'][str(i)][m] for i in class_ids] 37 | res['results']['mean']["mean"][m] = np.nanmean(foreground_values) 38 | with open(filename, 'w') as f: 39 | json.dump(res, f, indent=4, sort_keys=True) 40 | 41 | def run_in_folder(folder): 42 | json_files = subfiles(folder, True, None, ".json", True) 43 | json_files = [i for i in json_files if not i.split("/")[-1].startswith(".") and not i.endswith("_globalMean.json")] # stupid mac 44 | for j in json_files: 45 | foreground_mean(j) 46 | 47 | if __name__ == "__main__": 48 | folder = "/media/fabian/Results/nnUNetOutput_final/summary_jsons" 49 | run_in_folder(folder) -------------------------------------------------------------------------------- /nnunet/experiment_planning/find_classes_in_slice.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import pickle 17 | from collections import OrderedDict 18 | 19 | 20 | def add_classes_in_slice_info(args): 21 | """ 22 | We need this for 2D dataloader with oversampling. As of now it will detect slices that contain specific classes 23 | at run time, meaning it needs to iterate over an entire patient just to extract one slice. That is obviously bad, 24 | so we are doing this once beforehand and just give the dataloader the info it needs in the patients pkl file. 25 | 26 | """ 27 | npz_file, pkl_file, all_classes = args 28 | seg_map = np.load(npz_file)['data'][-1] 29 | with open(pkl_file, 'rb') as f: 30 | props = pickle.load(f) 31 | #if props.get('classes_in_slice_per_axis') is not None: 32 | print(pkl_file) 33 | # this will be a dict of dict where the first dict encodes the axis along which a slice is extracted in its keys. 34 | # The second dict (value of first dict) will have all classes as key and as values a list of all slice ids that 35 | # contain this class 36 | classes_in_slice = OrderedDict() 37 | for axis in range(3): 38 | other_axes = tuple([i for i in range(3) if i != axis]) 39 | classes_in_slice[axis] = OrderedDict() 40 | for c in all_classes: 41 | valid_slices = np.where(np.sum(seg_map == c, axis=other_axes) > 0)[0] 42 | classes_in_slice[axis][c] = valid_slices 43 | 44 | number_of_voxels_per_class = OrderedDict() 45 | for c in all_classes: 46 | number_of_voxels_per_class[c] = np.sum(seg_map == c) 47 | 48 | props['classes_in_slice_per_axis'] = classes_in_slice 49 | props['number_of_voxels_per_class'] = number_of_voxels_per_class 50 | 51 | with open(pkl_file, 'wb') as f: 52 | pickle.dump(props, f) 53 | -------------------------------------------------------------------------------- /nnunet/training/loss_functions/GDL.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from nnunet.training.loss_functions.dice_loss import get_tp_fp_fn 3 | from nnunet.utilities.tensor_utilities import sum_tensor 4 | from torch import nn 5 | 6 | 7 | class GDL(nn.Module): 8 | def __init__(self, apply_nonlin=None, batch_dice=False, do_bg=True, smooth=1., 9 | square=False, square_volumes=False): 10 | """ 11 | square_volumes will square the weight term. The paper recommends square_volumes=True; I don't (just an intuition) 12 | """ 13 | super(GDL, self).__init__() 14 | 15 | self.square_volumes = square_volumes 16 | self.square = square 17 | self.do_bg = do_bg 18 | self.batch_dice = batch_dice 19 | self.apply_nonlin = apply_nonlin 20 | self.smooth = smooth 21 | 22 | def forward(self, x, y, loss_mask=None): 23 | shp_x = x.shape 24 | shp_y = y.shape 25 | 26 | if self.batch_dice: 27 | axes = [0] + list(range(2, len(shp_x))) 28 | else: 29 | axes = list(range(2, len(shp_x))) 30 | 31 | if len(shp_x) != len(shp_y): 32 | y = y.view((shp_y[0], 1, *shp_y[1:])) 33 | 34 | if all([i == j for i, j in zip(x.shape, y.shape)]): 35 | # if this is the case then gt is probably already a one hot encoding 36 | y_onehot = y 37 | else: 38 | gt = y.long() 39 | y_onehot = torch.zeros(shp_x) 40 | if x.device.type == "cuda": 41 | y_onehot = y_onehot.cuda(x.device.index) 42 | y_onehot.scatter_(1, gt, 1) 43 | 44 | if self.apply_nonlin is not None: 45 | x = self.apply_nonlin(x) 46 | 47 | if not self.do_bg: 48 | x = x[:, 1:] 49 | y_onehot = y_onehot[:, 1:] 50 | 51 | tp, fp, fn = get_tp_fp_fn(x, y_onehot, axes, loss_mask, self.square) 52 | 53 | # GDL weight computation, we use 1/V 54 | volumes = sum_tensor(y_onehot, axes) 55 | 56 | if self.square_volumes: 57 | volumes = volumes ** 2 58 | 59 | # apply weights 60 | tp = tp / volumes 61 | fp = fp / volumes 62 | fn = fn / volumes 63 | 64 | # sum over classes 65 | if self.batch_dice: 66 | axis = 0 67 | else: 68 | axis = 1 69 | 70 | tp = tp.sum(axis, keepdim=False) 71 | fp = fp.sum(axis, keepdim=False) 72 | fn = fn.sum(axis, keepdim=False) 73 | 74 | # compute dice 75 | dc = (2 * tp + self.smooth) / (2 * tp + fp + fn + self.smooth) 76 | 77 | dc = dc.mean() 78 | 79 | return -dc 80 | -------------------------------------------------------------------------------- /nnunet/evaluation/surface_dice.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import numpy as np 17 | from medpy.metric.binary import __surface_distances 18 | 19 | 20 | def normalized_surface_dice(a: np.ndarray, b: np.ndarray, threshold: float, spacing: tuple = None, connectivity=1): 21 | """ 22 | This implementation differs from the official surface dice implementation! These two are not comparable!!!!! 23 | 24 | The normalized surface dice is symmetric, so it should not matter wheter a or b is the reference image 25 | 26 | This implementation natively supports 2D and 3D images. Whether other dimensions are supported depends in the 27 | __surface_distances implementation in medpy 28 | 29 | :param a: image 1, must have the same shape as b 30 | :param b: image 2, must have the same shape as a 31 | :param threshold: distances below this threshold will be counted as true positives. Threshold is in mm, not voxels! 32 | (if spacing = (1, 1(, 1)) then one voxel=1mm so the threshold is effectively in voxels) 33 | must be a tuple of len dimension(a) 34 | :param spacing: how many mm is one voxel in reality? Can be left at None, we then assume an isotropic spacing of 1mm 35 | :param connectivity: see scipy.ndimage.generate_binary_structure for more information. I suggest you leave that 36 | one alone 37 | :return: 38 | """ 39 | assert all([i == j for i, j in zip(a.shape, b.shape)]), "a and b must have the same shape. a.shape= %s, " \ 40 | "b.shape= %s" % (str(a.shape), str(b.shape)) 41 | if spacing is None: 42 | spacing = tuple([1 for _ in range(len(a.shape))]) 43 | a_to_b = __surface_distances(a, b, spacing, connectivity) 44 | b_to_a = __surface_distances(b, a, spacing, connectivity) 45 | 46 | tp_a = np.sum(a_to_b <= threshold) 47 | tp_b = np.sum(b_to_a <= threshold) 48 | 49 | fp = np.sum(a_to_b > threshold) 50 | fn = np.sum(b_to_a > threshold) 51 | 52 | dc = (tp_a + tp_b) / (tp_a + tp_b + fp + fn + 1e-8) # 1e-8 just so that we don't get div by 0 53 | return dc 54 | 55 | -------------------------------------------------------------------------------- /nnunet/dataset_conversion/readme.md: -------------------------------------------------------------------------------- 1 | # Dataset conversion instructions 2 | 3 | ## How to use decathlon datasets 4 | 5 | First make sure you have all the proper paths set in `paths.py`: base, preprocessing_output_dir and network_training_output_dir. 6 | Then copy the downloaded dataset into `base/nnUNet_raw`. For Task04_Hippocampus, for example, this should look like this: 7 | `base/nnUNet_raw/Task04_Hippocampus`. Hereby, Task04_Hippocampus has three subfolders 8 | (`imagesTr`, `labelsTr`, `imagesTs`) and a `dataset.json` file. 9 | 10 | You can run the preprocessing and experiment planning for this stask by executing 11 | 12 | `python experiment_planning/plan_and_preprocess_task.py -t Task04_Hippocampus -p 8` 13 | 14 | 15 | For historical reasons nnU-Net does not like 4D niftis, so the first preprocessing step done by nnU-Net will be splitting the 16 | 4D niftis into a series of 3D niftis. They will be stored in `base/nnUNet_raw_splitted` when you run the preprocessing. 17 | 18 | ## How to convert non-Decathlon datasets for nnU-Net 19 | In this file we provide a description on how you need to convert your dataset to make it compatible with nnU-Net. 20 | 21 | For the purpose of this 22 | manual, we refer to your dataset as `TaskXX_MY_DATASET`. Hereby, `XX` is is a dual-digit number and `MY_DATASET` can 23 | be anything you want. 24 | 25 | We follow the folder structure of the Medical Segmentation Decathlon (MSD). In the `splitted_4d_output_dir` (see `paths.py`) 26 | , create a subfolder 27 | called `TaskXX_MY_DATASET`. In that subfolder, create the following three directories: `imagesTr`, `labelsTr`(, `imagesTs`). 28 | These are for training images, training labels (and test images), respectively. 29 | 30 | Just like the MSD we use the nifti (.nii.gz) file format. There is a crucial difference though. 31 | While the MSD provides 4D niftis where the first axis is for the modality, we prefer to split each training case into 32 | separate 3D Niftis. So what was a File `patientID.nii.gz` containing an image of shape (4, 160, 190, 160) now is four files with shape 33 | (160, 190, 160) each. These four files should be named `patientID_0000.nii.gz`, `patientID_0001.nii.gz`, `patientID_0002.nii.gz`, 34 | `patientID_0003.nii.gz`, where the ending 4-digit represents the modality. `patientID` can be anything you want. Make 35 | sure that the same modality is assigned the same digit for all patients (for example if you have T1 and T2 then you 36 | need to make sure T1 is always 0000 and T2 always 0001). If you data has only one modality, just name your cases `patientID_0000.nii.gz`. 37 | 38 | Copy all training cases (just the data, not the labels) into the `imagesTr` subfolder. 39 | 40 | Copy training labels into the `labelsTr` subfolder. Labels are always 3D niftis and should be named `patientID.nii.gz` 41 | where `patientiID` is identical to the identifier used for the corresponding raw data. 42 | 43 | If you have test images, convert them to 3D Nifti like the training data and place them in the `imagesTs` folder. 44 | All data you wish to predict with nnU-Net must be in this format. 45 | 46 | Finally you need to create a `dataset.json` file that you place in the `TaskXX_MY_DATASET` root folder. This file was 47 | always provided in the MSD challenge and is therefore a requirement for nnU-Net. Have a look at (for example) 48 | `LiverTumorSegmentationChallenge.py` to see what it needs to look like. Important: The list stored in 'training' contains images and labels are 49 | both called `patientID.nii.gz` (dropping the 0000 ending we used here) for consistency! -------------------------------------------------------------------------------- /nnunet/evaluation/model_selection/collect_all_fold0_results_and_summarize_in_one_csv.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from nnunet.evaluation.model_selection.summarize_results_in_one_json import summarize 16 | from nnunet.paths import network_training_output_dir 17 | from batchgenerators.utilities.file_and_folder_operations import * 18 | 19 | if __name__ == "__main__": 20 | summary_output_folder = join(network_training_output_dir, "summary_jsons_fold0") 21 | maybe_mkdir_p(summary_output_folder) 22 | summarize(range(50), output_dir=summary_output_folder, folds=(0,)) 23 | 24 | results_csv = join(network_training_output_dir, "summary_fold0.csv") 25 | 26 | summary_files = subfiles(summary_output_folder, suffix='.json', join=False) 27 | 28 | with open(results_csv, 'w') as f: 29 | for s in summary_files: 30 | if s.find("ensemble") == -1: 31 | task, network, trainer, plans, validation_folder = s.split("__") 32 | else: 33 | n1, n2 = s.split("--") 34 | n1 = n1[n1.find("ensemble_") + len("ensemble_") :] 35 | task = s.split("__")[0] 36 | network = "ensemble" 37 | trainer = n1 38 | plans = n2 39 | validation_folder = "none" 40 | validation_folder = validation_folder[:-len('.json')] 41 | results = load_json(join(summary_output_folder, s))['results']['mean']['mean']['Dice'] 42 | f.write("%s,%s,%s,%s,%s,%02.4f\n" % (task, 43 | network, trainer, validation_folder, plans, results)) 44 | 45 | summary_output_folder = join(network_training_output_dir, "summary_jsons") 46 | maybe_mkdir_p(summary_output_folder) 47 | summarize(['all'], output_dir=summary_output_folder) 48 | 49 | results_csv = join(network_training_output_dir, "summary_allFolds.csv") 50 | 51 | summary_files = subfiles(summary_output_folder, suffix='.json', join=False) 52 | 53 | with open(results_csv, 'w') as f: 54 | for s in summary_files: 55 | if s.find("ensemble") == -1: 56 | task, network, trainer, plans, validation_folder = s.split("__") 57 | else: 58 | n1, n2 = s.split("--") 59 | n1 = n1[n1.find("ensemble_") + len("ensemble_") :] 60 | task = s.split("__")[0] 61 | network = "ensemble" 62 | trainer = n1 63 | plans = n2 64 | validation_folder = "none" 65 | validation_folder = validation_folder[:-len('.json')] 66 | results = load_json(join(summary_output_folder, s))['results']['mean']['mean']['Dice'] 67 | f.write("%s,%s,%s,%s,%s,%02.4f\n" % (task, 68 | network, trainer, validation_folder, plans, results)) 69 | 70 | -------------------------------------------------------------------------------- /nnunet/evaluation/add_dummy_task_with_mean_over_all_tasks.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import numpy as np 17 | from batchgenerators.utilities.file_and_folder_operations import subfiles 18 | import os 19 | from collections import OrderedDict 20 | 21 | folder = "/home/fabian/drives/E132-Projekte/Projects/2018_MedicalDecathlon/Leaderboard" 22 | task_descriptors = ['2D final 2', 23 | '2D final, less pool, dc and topK, fold0', 24 | '2D final pseudo3d 7, fold0', 25 | '2D final, less pool, dc and ce, fold0', 26 | '3D stage0 final 2, fold0', 27 | '3D fullres final 2, fold0'] 28 | task_ids_with_no_stage0 = ["Task01_BrainTumour", "Task04_Hippocampus", "Task05_Prostate"] 29 | 30 | mean_scores = OrderedDict() 31 | for t in task_descriptors: 32 | mean_scores[t] = OrderedDict() 33 | 34 | json_files = subfiles(folder, True, None, ".json", True) 35 | json_files = [i for i in json_files if not i.split("/")[-1].startswith(".")] # stupid mac 36 | for j in json_files: 37 | with open(j, 'r') as f: 38 | res = json.load(f) 39 | task = res['task'] 40 | if task != "Task99_ALL": 41 | name = res['name'] 42 | if name in task_descriptors: 43 | if task not in list(mean_scores[name].keys()): 44 | mean_scores[name][task] = res['results']['mean']['mean'] 45 | else: 46 | raise RuntimeError("duplicate task %s for description %s" % (task, name)) 47 | 48 | for t in task_ids_with_no_stage0: 49 | mean_scores["3D stage0 final 2, fold0"][t] = mean_scores["3D fullres final 2, fold0"][t] 50 | 51 | a = set() 52 | for i in mean_scores.keys(): 53 | a = a.union(list(mean_scores[i].keys())) 54 | 55 | for i in mean_scores.keys(): 56 | try: 57 | for t in list(a): 58 | assert t in mean_scores[i].keys(), "did not find task %s for experiment %s" % (t, i) 59 | new_res = OrderedDict() 60 | new_res['name'] = i 61 | new_res['author'] = "Fabian" 62 | new_res['task'] = "Task99_ALL" 63 | new_res['results'] = OrderedDict() 64 | new_res['results']['mean'] = OrderedDict() 65 | new_res['results']['mean']['mean'] = OrderedDict() 66 | tasks = list(mean_scores[i].keys()) 67 | metrics = mean_scores[i][tasks[0]].keys() 68 | for m in metrics: 69 | foreground_values = [mean_scores[i][n][m] for n in tasks] 70 | new_res['results']['mean']["mean"][m] = np.nanmean(foreground_values) 71 | output_fname = i.replace(" ", "_") + "_globalMean.json" 72 | with open(os.path.join(folder, output_fname), 'w') as f: 73 | json.dump(new_res, f) 74 | except AssertionError: 75 | print("could not process experiment %s" % i) 76 | print("did not find task %s for experiment %s" % (t, i)) 77 | 78 | -------------------------------------------------------------------------------- /nnunet/dataset_conversion/Promise2012.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from collections import OrderedDict 16 | import SimpleITK as sitk 17 | from batchgenerators.utilities.file_and_folder_operations import * 18 | 19 | 20 | def export_for_submission(source_dir, target_dir): 21 | """ 22 | promise wants mhd :-/ 23 | :param source_dir: 24 | :param target_dir: 25 | :return: 26 | """ 27 | files = subfiles(source_dir, suffix=".nii.gz", join=False) 28 | target_files = [join(target_dir, i[:-7] + ".mhd") for i in files] 29 | maybe_mkdir_p(target_dir) 30 | for f, t in zip(files, target_files): 31 | img = sitk.ReadImage(join(source_dir, f)) 32 | sitk.WriteImage(img, t) 33 | 34 | 35 | if __name__ == "__main__": 36 | folder = "/media/fabian/My Book/datasets/promise2012" 37 | out_folder = "/media/fabian/My Book/MedicalDecathlon/MedicalDecathlon_raw_splitted/Task24_Promise" 38 | 39 | maybe_mkdir_p(join(out_folder, "imagesTr")) 40 | maybe_mkdir_p(join(out_folder, "imagesTs")) 41 | maybe_mkdir_p(join(out_folder, "labelsTr")) 42 | # train 43 | current_dir = join(folder, "train") 44 | segmentations = subfiles(current_dir, suffix="segmentation.mhd") 45 | raw_data = [i for i in subfiles(current_dir, suffix="mhd") if not i.endswith("segmentation.mhd")] 46 | for i in raw_data: 47 | out_fname = join(out_folder, "imagesTr", i.split("/")[-1][:-4] + "_0000.nii.gz") 48 | sitk.WriteImage(sitk.ReadImage(i), out_fname) 49 | for i in segmentations: 50 | out_fname = join(out_folder, "labelsTr", i.split("/")[-1][:-17] + ".nii.gz") 51 | sitk.WriteImage(sitk.ReadImage(i), out_fname) 52 | 53 | # test 54 | current_dir = join(folder, "test") 55 | test_data = subfiles(current_dir, suffix="mhd") 56 | for i in test_data: 57 | out_fname = join(out_folder, "imagesTs", i.split("/")[-1][:-4] + "_0000.nii.gz") 58 | sitk.WriteImage(sitk.ReadImage(i), out_fname) 59 | 60 | 61 | json_dict = OrderedDict() 62 | json_dict['name'] = "PROMISE12" 63 | json_dict['description'] = "prostate" 64 | json_dict['tensorImageSize'] = "4D" 65 | json_dict['reference'] = "see challenge website" 66 | json_dict['licence'] = "see challenge website" 67 | json_dict['release'] = "0.0" 68 | json_dict['modality'] = { 69 | "0": "MRI", 70 | } 71 | json_dict['labels'] = { 72 | "0": "background", 73 | "1": "prostate" 74 | } 75 | json_dict['numTraining'] = len(raw_data) 76 | json_dict['numTest'] = len(test_data) 77 | json_dict['training'] = [{'image': "./imagesTr/%s.nii.gz" % i.split("/")[-1][:-4], "label": "./labelsTr/%s.nii.gz" % i.split("/")[-1][:-4]} for i in 78 | raw_data] 79 | json_dict['test'] = ["./imagesTs/%s.nii.gz" % i.split("/")[-1][:-4] for i in test_data] 80 | 81 | save_json(json_dict, os.path.join(out_folder, "dataset.json")) 82 | 83 | -------------------------------------------------------------------------------- /nnunet/dataset_conversion/BraTS_2019.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from collections import OrderedDict 3 | 4 | from batchgenerators.utilities.file_and_folder_operations import * 5 | from nnunet.paths import splitted_4d_output_dir 6 | import SimpleITK as sitk 7 | import shutil 8 | 9 | 10 | def copy_nifti_modify_labels(in_file, out_file): 11 | # use this for segmentation only!!! 12 | # nnUNet wants the labels to be continuous. BraTS is 0, 1, 2, 4 -> we make that into 0, 1, 2, 3 13 | # no sanity checks -> #Yolo 14 | img = sitk.ReadImage(in_file) 15 | img_npy = sitk.GetArrayFromImage(img) 16 | seg_new = np.zeros_like(img_npy) 17 | seg_new[img_npy == 4] = 3 18 | seg_new[img_npy == 2] = 1 19 | seg_new[img_npy == 1] = 2 20 | img_corr = sitk.GetImageFromArray(seg_new) 21 | img_corr.CopyInformation(img) 22 | sitk.WriteImage(img_corr, out_file) 23 | 24 | 25 | if __name__ == "__main__": 26 | """ 27 | REMEMBER TO CONVERT LABELS BACK TO BRATS CONVENTION AFTER PREDICTION! 28 | """ 29 | 30 | task_name = "Task43_BraTS2019" 31 | downloaded_data_dir = "/home/fabian/drives/E132-Projekte/Move_to_E132-Rohdaten/BraTS_2019/MICCAI_BraTS_2019_Data_Training" 32 | 33 | target_base = join(splitted_4d_output_dir, task_name) 34 | target_imagesTr = join(target_base, "imagesTr") 35 | target_labelsTr = join(target_base, "labelsTr") 36 | 37 | maybe_mkdir_p(target_imagesTr) 38 | maybe_mkdir_p(target_labelsTr) 39 | 40 | patient_names = [] 41 | for tpe in ["HGG", "LGG"]: 42 | cur = join(downloaded_data_dir, tpe) 43 | for p in subdirs(cur, join=False): 44 | patdir = join(cur, p) 45 | patient_name = tpe + "__" + p 46 | patient_names.append(patient_name) 47 | t1 = join(patdir, p + "_t1.nii.gz") 48 | t1c = join(patdir, p + "_t1ce.nii.gz") 49 | t2 = join(patdir, p + "_t2.nii.gz") 50 | flair = join(patdir, p + "_flair.nii.gz") 51 | seg = join(patdir, p + "_seg.nii.gz") 52 | 53 | assert all([ 54 | isfile(t1), 55 | isfile(t1c), 56 | isfile(t2), 57 | isfile(flair), 58 | isfile(seg) 59 | ]), "%s" % patient_name 60 | 61 | shutil.copy(t1, join(target_imagesTr, patient_name + "_0000.nii.gz")) 62 | shutil.copy(t1c, join(target_imagesTr, patient_name + "_0001.nii.gz")) 63 | shutil.copy(t2, join(target_imagesTr, patient_name + "_0002.nii.gz")) 64 | shutil.copy(flair, join(target_imagesTr, patient_name + "_0003.nii.gz")) 65 | 66 | copy_nifti_modify_labels(seg, join(target_labelsTr, patient_name + ".nii.gz")) 67 | 68 | 69 | json_dict = OrderedDict() 70 | json_dict['name'] = "BraTS2019" 71 | json_dict['description'] = "nothing" 72 | json_dict['tensorImageSize'] = "4D" 73 | json_dict['reference'] = "see BraTS2019" 74 | json_dict['licence'] = "see BraTS2019 license" 75 | json_dict['release'] = "0.0" 76 | json_dict['modality'] = { 77 | "0": "T1", 78 | "1": "T1ce", 79 | "2": "T2", 80 | "3": "FLAIR" 81 | } 82 | json_dict['labels'] = { 83 | "0": "background", 84 | "1": "edema", 85 | "2": "non-enhancing", 86 | "3": "enhancing", 87 | } 88 | json_dict['numTraining'] = len(patient_names) 89 | json_dict['numTest'] = 0 90 | json_dict['training'] = [{'image': "./imagesTr/%s.nii.gz" % i, "label": "./labelsTr/%s.nii.gz" % i} for i in 91 | patient_names] 92 | json_dict['test'] = [] 93 | 94 | save_json(json_dict, join(target_base, "dataset.json")) 95 | -------------------------------------------------------------------------------- /nnunet/paths.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Put your personal paths in here. This file will shortly be added to gitignore so that your personal paths will not be tracked 16 | """ 17 | import os 18 | from batchgenerators.utilities.file_and_folder_operations import * 19 | # You need to set the following folders: base, preprocessing_output_dir and network_training_output_dir. See below for details. 20 | 21 | 22 | # do not modify these unless you know what you are doing 23 | my_output_identifier = "nnUNet" 24 | default_plans_identifier = "nnUNetPlans" 25 | default_data_identifier = 'nnUNet' 26 | 27 | try: 28 | # base is the folder where the raw data is stored. You just need to set base only, the others will be created 29 | # automatically (they are subfolders of base). 30 | # Here I use environment variables to set the base folder. Environment variables allow me to use the same code on 31 | # different systems (and our compute cluster). You can replace this line with something like: 32 | # base = "/path/to/my/folder" 33 | base = os.environ['nnUNet_base'] 34 | raw_dataset_dir = join(base, "nnUNet_raw") 35 | splitted_4d_output_dir = join(base, "nnUNet_raw_splitted") 36 | cropped_output_dir = join(base, "nnUNet_raw_cropped") 37 | maybe_mkdir_p(splitted_4d_output_dir) 38 | maybe_mkdir_p(raw_dataset_dir) 39 | maybe_mkdir_p(cropped_output_dir) 40 | except KeyError: 41 | cropped_output_dir = splitted_4d_output_dir = raw_dataset_dir = base = None 42 | 43 | # preprocessing_output_dir is where the preprocessed data is stored. If you run a training I very strongly recommend 44 | # this is a SSD! 45 | try: 46 | # Here I use environment variables to set the folder. Environment variables allow me to use the same code on 47 | # different systems (and our compute cluster). You can replace this line with something like: 48 | # preprocessing_output_dir = "/path/to/my/folder_with_preprocessed_data" 49 | preprocessing_output_dir = os.environ['nnUNet_preprocessed'] 50 | except KeyError: 51 | preprocessing_output_dir = None 52 | 53 | # This is where the trained model parameters are stored 54 | try: 55 | # Here I use environment variables to set the folder. Environment variables allow me to use the same code on 56 | # different systems (and our compute cluster). You can replace this line with something like: 57 | # network_training_output_dir = "/path/to/my/folder_with_results" 58 | network_training_output_dir = join(os.environ['RESULTS_FOLDER'], my_output_identifier) 59 | maybe_mkdir_p(network_training_output_dir) 60 | except KeyError: 61 | network_training_output_dir = None 62 | print("RESULTS_FOLDER was not in your environment variables, network_training_output_dir could not be determined. " 63 | "Please go to nnunet/paths.py and manually set network_training_output_dir. You can ignore this warning if " 64 | "you are using nnunet only as a toolkit and don't intend to run network trainings") 65 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"LiverTumer", 3 | "description":"LiverTumer HCC", 4 | "tensorImageSize":"3D", 5 | "reference":"RenJi Hospital", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 23/12/2019", 8 | "modality":{ 9 | "0":"MRI" 10 | }, 11 | "labels":{ 12 | "0":"background", 13 | "1":"HCC" 14 | }, 15 | "numTraining":21, 16 | "numTest":10, 17 | "training":[ 18 | { 19 | "image":"./imagesTr/la_0905871.nii.gz", 20 | "label":"./labelsTr/la_0905871.nii.gz" 21 | }, 22 | { 23 | "image":"./imagesTr/la_2034583.nii.gz", 24 | "label":"./labelsTr/la_2034583.nii.gz" 25 | }, 26 | { 27 | "image":"./imagesTr/la_2215156.nii.gz", 28 | "label":"./labelsTr/la_2215156.nii.gz" 29 | }, 30 | { 31 | "image":"./imagesTr/la_2672555.nii.gz", 32 | "label":"./labelsTr/la_2672555.nii.gz" 33 | }, 34 | { 35 | "image":"./imagesTr/la_2757093.nii.gz", 36 | "label":"./labelsTr/la_2757093.nii.gz" 37 | }, 38 | { 39 | "image":"./imagesTr/la_2757209.nii.gz", 40 | "label":"./labelsTr/la_2757209.nii.gz" 41 | }, 42 | { 43 | "image":"./imagesTr/la_2757480.nii.gz", 44 | "label":"./labelsTr/la_2757480.nii.gz" 45 | }, 46 | { 47 | "image":"./imagesTr/la_2761736.nii.gz", 48 | "label":"./labelsTr/la_2761736.nii.gz" 49 | }, 50 | { 51 | "image":"./imagesTr/la_2762836.nii.gz", 52 | "label":"./labelsTr/la_2762836.nii.gz" 53 | }, 54 | { 55 | "image":"./imagesTr/la_2763772.nii.gz", 56 | "label":"./labelsTr/la_2763772.nii.gz" 57 | }, 58 | { 59 | "image":"./imagesTr/la_2765567.nii.gz", 60 | "label":"./labelsTr/la_2765567.nii.gz" 61 | }, 62 | { 63 | "image":"./imagesTr/la_2771260.nii.gz", 64 | "label":"./labelsTr/la_2771260.nii.gz" 65 | }, 66 | { 67 | "image":"./imagesTr/la_2776282.nii.gz", 68 | "label":"./labelsTr/la_2776282.nii.gz" 69 | }, 70 | { 71 | "image":"./imagesTr/la_2782777.nii.gz", 72 | "label":"./labelsTr/la_2782777.nii.gz" 73 | }, 74 | { 75 | "image":"./imagesTr/la_2783682.nii.gz", 76 | "label":"./labelsTr/la_2783682.nii.gz" 77 | }, 78 | { 79 | "image":"./imagesTr/la_2785574.nii.gz", 80 | "label":"./labelsTr/la_2785574.nii.gz" 81 | }, 82 | { 83 | "image":"./imagesTr/la_2788650.nii.gz", 84 | "label":"./labelsTr/la_2788650.nii.gz" 85 | }, 86 | { 87 | "image":"./imagesTr/la_2789343.nii.gz", 88 | "label":"./labelsTr/la_2789343.nii.gz" 89 | }, 90 | { 91 | "image":"./imagesTr/la_RJN1066702.nii.gz", 92 | "label":"./labelsTr/la_RJN1066702.nii.gz" 93 | }, 94 | { 95 | "image":"./imagesTr/la_RJN1142167.nii.gz", 96 | "label":"./labelsTr/la_RJN1142167.nii.gz" 97 | }, 98 | { 99 | "image":"./imagesTr/la_RJN1167132.nii.gz", 100 | "label":"./labelsTr/la_RJN1167132.nii.gz" 101 | } 102 | ], 103 | "test":[ 104 | "./imagesTs/la_2461545.nii.gz", 105 | "./imagesTs/la_2714255.nii.gz", 106 | "./imagesTs/la_2723303.nii.gz", 107 | "./imagesTs/la_2724908.nii.gz", 108 | "./imagesTs/la_2725093.nii.gz", 109 | "./imagesTs/la_2726620.nii.gz", 110 | "./imagesTs/la_2727814.nii.gz", 111 | "./imagesTs/la_2732090.nii.gz", 112 | "./imagesTs/la_RJN547551.nii.gz", 113 | "./imagesTs/la_RJN1127275.nii.gz" 114 | ] 115 | } -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw_cropped/Task11_LiverTumor/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"LiverTumer", 3 | "description":"LiverTumer HCC", 4 | "tensorImageSize":"3D", 5 | "reference":"RenJi Hospital", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 23/12/2019", 8 | "modality":{ 9 | "0":"MRI" 10 | }, 11 | "labels":{ 12 | "0":"background", 13 | "1":"HCC" 14 | }, 15 | "numTraining":21, 16 | "numTest":10, 17 | "training":[ 18 | { 19 | "image":"./imagesTr/la_0905871.nii.gz", 20 | "label":"./labelsTr/la_0905871.nii.gz" 21 | }, 22 | { 23 | "image":"./imagesTr/la_2034583.nii.gz", 24 | "label":"./labelsTr/la_2034583.nii.gz" 25 | }, 26 | { 27 | "image":"./imagesTr/la_2215156.nii.gz", 28 | "label":"./labelsTr/la_2215156.nii.gz" 29 | }, 30 | { 31 | "image":"./imagesTr/la_2672555.nii.gz", 32 | "label":"./labelsTr/la_2672555.nii.gz" 33 | }, 34 | { 35 | "image":"./imagesTr/la_2757093.nii.gz", 36 | "label":"./labelsTr/la_2757093.nii.gz" 37 | }, 38 | { 39 | "image":"./imagesTr/la_2757209.nii.gz", 40 | "label":"./labelsTr/la_2757209.nii.gz" 41 | }, 42 | { 43 | "image":"./imagesTr/la_2757480.nii.gz", 44 | "label":"./labelsTr/la_2757480.nii.gz" 45 | }, 46 | { 47 | "image":"./imagesTr/la_2761736.nii.gz", 48 | "label":"./labelsTr/la_2761736.nii.gz" 49 | }, 50 | { 51 | "image":"./imagesTr/la_2762836.nii.gz", 52 | "label":"./labelsTr/la_2762836.nii.gz" 53 | }, 54 | { 55 | "image":"./imagesTr/la_2763772.nii.gz", 56 | "label":"./labelsTr/la_2763772.nii.gz" 57 | }, 58 | { 59 | "image":"./imagesTr/la_2765567.nii.gz", 60 | "label":"./labelsTr/la_2765567.nii.gz" 61 | }, 62 | { 63 | "image":"./imagesTr/la_2771260.nii.gz", 64 | "label":"./labelsTr/la_2771260.nii.gz" 65 | }, 66 | { 67 | "image":"./imagesTr/la_2776282.nii.gz", 68 | "label":"./labelsTr/la_2776282.nii.gz" 69 | }, 70 | { 71 | "image":"./imagesTr/la_2782777.nii.gz", 72 | "label":"./labelsTr/la_2782777.nii.gz" 73 | }, 74 | { 75 | "image":"./imagesTr/la_2783682.nii.gz", 76 | "label":"./labelsTr/la_2783682.nii.gz" 77 | }, 78 | { 79 | "image":"./imagesTr/la_2785574.nii.gz", 80 | "label":"./labelsTr/la_2785574.nii.gz" 81 | }, 82 | { 83 | "image":"./imagesTr/la_2788650.nii.gz", 84 | "label":"./labelsTr/la_2788650.nii.gz" 85 | }, 86 | { 87 | "image":"./imagesTr/la_2789343.nii.gz", 88 | "label":"./labelsTr/la_2789343.nii.gz" 89 | }, 90 | { 91 | "image":"./imagesTr/la_RJN1066702.nii.gz", 92 | "label":"./labelsTr/la_RJN1066702.nii.gz" 93 | }, 94 | { 95 | "image":"./imagesTr/la_RJN1142167.nii.gz", 96 | "label":"./labelsTr/la_RJN1142167.nii.gz" 97 | }, 98 | { 99 | "image":"./imagesTr/la_RJN1167132.nii.gz", 100 | "label":"./labelsTr/la_RJN1167132.nii.gz" 101 | } 102 | ], 103 | "test":[ 104 | "./imagesTs/la_2461545.nii.gz", 105 | "./imagesTs/la_2714255.nii.gz", 106 | "./imagesTs/la_2723303.nii.gz", 107 | "./imagesTs/la_2724908.nii.gz", 108 | "./imagesTs/la_2725093.nii.gz", 109 | "./imagesTs/la_2726620.nii.gz", 110 | "./imagesTs/la_2727814.nii.gz", 111 | "./imagesTs/la_2732090.nii.gz", 112 | "./imagesTs/la_RJN547551.nii.gz", 113 | "./imagesTs/la_RJN1127275.nii.gz" 114 | ] 115 | } -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw_splitted/Task11_LiverTumor/dataset.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"LiverTumer", 3 | "description":"LiverTumer HCC", 4 | "tensorImageSize":"3D", 5 | "reference":"RenJi Hospital", 6 | "licence":"CC-BY-SA 4.0", 7 | "relase":"1.0 23/12/2019", 8 | "modality":{ 9 | "0":"MRI" 10 | }, 11 | "labels":{ 12 | "0":"background", 13 | "1":"HCC" 14 | }, 15 | "numTraining":21, 16 | "numTest":10, 17 | "training":[ 18 | { 19 | "image":"./imagesTr/la_0905871.nii.gz", 20 | "label":"./labelsTr/la_0905871.nii.gz" 21 | }, 22 | { 23 | "image":"./imagesTr/la_2034583.nii.gz", 24 | "label":"./labelsTr/la_2034583.nii.gz" 25 | }, 26 | { 27 | "image":"./imagesTr/la_2215156.nii.gz", 28 | "label":"./labelsTr/la_2215156.nii.gz" 29 | }, 30 | { 31 | "image":"./imagesTr/la_2672555.nii.gz", 32 | "label":"./labelsTr/la_2672555.nii.gz" 33 | }, 34 | { 35 | "image":"./imagesTr/la_2757093.nii.gz", 36 | "label":"./labelsTr/la_2757093.nii.gz" 37 | }, 38 | { 39 | "image":"./imagesTr/la_2757209.nii.gz", 40 | "label":"./labelsTr/la_2757209.nii.gz" 41 | }, 42 | { 43 | "image":"./imagesTr/la_2757480.nii.gz", 44 | "label":"./labelsTr/la_2757480.nii.gz" 45 | }, 46 | { 47 | "image":"./imagesTr/la_2761736.nii.gz", 48 | "label":"./labelsTr/la_2761736.nii.gz" 49 | }, 50 | { 51 | "image":"./imagesTr/la_2762836.nii.gz", 52 | "label":"./labelsTr/la_2762836.nii.gz" 53 | }, 54 | { 55 | "image":"./imagesTr/la_2763772.nii.gz", 56 | "label":"./labelsTr/la_2763772.nii.gz" 57 | }, 58 | { 59 | "image":"./imagesTr/la_2765567.nii.gz", 60 | "label":"./labelsTr/la_2765567.nii.gz" 61 | }, 62 | { 63 | "image":"./imagesTr/la_2771260.nii.gz", 64 | "label":"./labelsTr/la_2771260.nii.gz" 65 | }, 66 | { 67 | "image":"./imagesTr/la_2776282.nii.gz", 68 | "label":"./labelsTr/la_2776282.nii.gz" 69 | }, 70 | { 71 | "image":"./imagesTr/la_2782777.nii.gz", 72 | "label":"./labelsTr/la_2782777.nii.gz" 73 | }, 74 | { 75 | "image":"./imagesTr/la_2783682.nii.gz", 76 | "label":"./labelsTr/la_2783682.nii.gz" 77 | }, 78 | { 79 | "image":"./imagesTr/la_2785574.nii.gz", 80 | "label":"./labelsTr/la_2785574.nii.gz" 81 | }, 82 | { 83 | "image":"./imagesTr/la_2788650.nii.gz", 84 | "label":"./labelsTr/la_2788650.nii.gz" 85 | }, 86 | { 87 | "image":"./imagesTr/la_2789343.nii.gz", 88 | "label":"./labelsTr/la_2789343.nii.gz" 89 | }, 90 | { 91 | "image":"./imagesTr/la_RJN1066702.nii.gz", 92 | "label":"./labelsTr/la_RJN1066702.nii.gz" 93 | }, 94 | { 95 | "image":"./imagesTr/la_RJN1142167.nii.gz", 96 | "label":"./labelsTr/la_RJN1142167.nii.gz" 97 | }, 98 | { 99 | "image":"./imagesTr/la_RJN1167132.nii.gz", 100 | "label":"./labelsTr/la_RJN1167132.nii.gz" 101 | } 102 | ], 103 | "test":[ 104 | "./imagesTs/la_2461545.nii.gz", 105 | "./imagesTs/la_2714255.nii.gz", 106 | "./imagesTs/la_2723303.nii.gz", 107 | "./imagesTs/la_2724908.nii.gz", 108 | "./imagesTs/la_2725093.nii.gz", 109 | "./imagesTs/la_2726620.nii.gz", 110 | "./imagesTs/la_2727814.nii.gz", 111 | "./imagesTs/la_2732090.nii.gz", 112 | "./imagesTs/la_RJN547551.nii.gz", 113 | "./imagesTs/la_RJN1127275.nii.gz" 114 | ] 115 | } -------------------------------------------------------------------------------- /nnunet/run/default_configuration.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import nnunet 16 | from nnunet.paths import network_training_output_dir, preprocessing_output_dir, default_plans_identifier 17 | from batchgenerators.utilities.file_and_folder_operations import * 18 | from nnunet.experiment_planning.summarize_plans import summarize_plans 19 | from nnunet.training.model_restore import recursive_find_trainer 20 | 21 | 22 | def get_configuration_from_output_folder(folder): 23 | # split off network_training_output_dir 24 | folder = folder[len(network_training_output_dir):] 25 | if folder.startswith("/"): 26 | folder = folder[1:] 27 | 28 | configuration, task, trainer_and_plans_identifier = folder.split("/") 29 | trainer, plans_identifier = trainer_and_plans_identifier.split("__") 30 | return configuration, task, trainer, plans_identifier 31 | 32 | 33 | def get_output_folder(configuration, task, trainer, plans_identifier): 34 | return join(network_training_output_dir, configuration, task, trainer + "__" + plans_identifier) 35 | 36 | 37 | def get_default_configuration(network, task, network_trainer, plans_identifier=default_plans_identifier, 38 | search_in=(nnunet.__path__[0], "training", "network_training"), 39 | base_module='nnunet.training.network_training'): 40 | assert network in ['2d', '3d_lowres', '3d_fullres', '3d_cascade_fullres'], \ 41 | "network can only be one of the following: \'3d\', \'3d_lowres\', \'3d_fullres\', \'3d_cascade_fullres\'" 42 | 43 | dataset_directory = join(preprocessing_output_dir, task) 44 | 45 | if network == '2d': 46 | plans_file = join(preprocessing_output_dir, task, plans_identifier + "_plans_2D.pkl") 47 | else: 48 | plans_file = join(preprocessing_output_dir, task, plans_identifier + "_plans_3D.pkl") 49 | 50 | plans = load_pickle(plans_file) 51 | possible_stages = list(plans['plans_per_stage'].keys()) 52 | 53 | if (network == '3d_cascade_fullres' or network == "3d_lowres") and len(possible_stages) == 1: 54 | raise RuntimeError("3d_lowres/3d_cascade_fullres only applies if there is more than one stage. This task does " 55 | "not require the cascade. Run 3d_fullres instead") 56 | 57 | if network == '2d' or network == "3d_lowres": 58 | stage = 0 59 | else: 60 | stage = possible_stages[-1] 61 | 62 | trainer_class = recursive_find_trainer([join(*search_in)], network_trainer, 63 | current_module=base_module) 64 | 65 | output_folder_name = join(network_training_output_dir, network, task, network_trainer + "__" + plans_identifier) 66 | 67 | print("###############################################") 68 | print("I am running the following nnUNet: %s" % network) 69 | print("My trainer class is: ", trainer_class) 70 | print("For that I will be using the following configuration:") 71 | summarize_plans(plans_file) 72 | print("I am using stage %d from these plans" % stage) 73 | 74 | if (network == '2d' or len(possible_stages) > 1) and not network == '3d_lowres': 75 | batch_dice = True 76 | print("I am using batch dice + CE loss") 77 | else: 78 | batch_dice = False 79 | print("I am using sample dice + CE loss") 80 | 81 | print("\nI am using data from this folder: ", join(dataset_directory, plans['data_identifier'])) 82 | print("###############################################") 83 | return plans_file, output_folder_name, dataset_directory, batch_dice, stage, trainer_class 84 | -------------------------------------------------------------------------------- /nnunet/training/network_training/nnUNet_variants/nnUNetTrainerNoDA.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | from batchgenerators.utilities.file_and_folder_operations import maybe_mkdir_p, join 3 | from nnunet.network_architecture.neural_network import SegmentationNetwork 4 | from nnunet.training.data_augmentation.default_data_augmentation import get_no_augmentation 5 | from nnunet.training.dataloading.dataset_loading import unpack_dataset, DataLoader3D, DataLoader2D 6 | from nnunet.training.network_training.nnUNetTrainer import nnUNetTrainer 7 | from torch import nn 8 | 9 | matplotlib.use("agg") 10 | 11 | 12 | class nnUNetTrainerNoDA(nnUNetTrainer): 13 | def get_basic_generators(self): 14 | self.load_dataset() 15 | self.do_split() 16 | 17 | if self.threeD: 18 | dl_tr = DataLoader3D(self.dataset_tr, self.patch_size, self.patch_size, self.batch_size, 19 | False, oversample_foreground_percent=self.oversample_foreground_percent 20 | , pad_mode="constant", pad_sides=self.pad_all_sides) 21 | dl_val = DataLoader3D(self.dataset_val, self.patch_size, self.patch_size, self.batch_size, False, 22 | oversample_foreground_percent=self.oversample_foreground_percent, 23 | pad_mode="constant", pad_sides=self.pad_all_sides) 24 | else: 25 | dl_tr = DataLoader2D(self.dataset_tr, self.patch_size, self.patch_size, self.batch_size, 26 | transpose=self.plans.get('transpose_forward'), 27 | oversample_foreground_percent=self.oversample_foreground_percent 28 | , pad_mode="constant", pad_sides=self.pad_all_sides) 29 | dl_val = DataLoader2D(self.dataset_val, self.patch_size, self.patch_size, self.batch_size, 30 | transpose=self.plans.get('transpose_forward'), 31 | oversample_foreground_percent=self.oversample_foreground_percent, 32 | pad_mode="constant", pad_sides=self.pad_all_sides) 33 | return dl_tr, dl_val 34 | 35 | def initialize(self, training=True, force_load_plans=False): 36 | """ 37 | For prediction of test cases just set training=False, this will prevent loading of training data and 38 | training batchgenerator initialization 39 | :param training: 40 | :return: 41 | """ 42 | 43 | maybe_mkdir_p(self.output_folder) 44 | 45 | if force_load_plans or (self.plans is None): 46 | self.load_plans_file() 47 | 48 | self.process_plans(self.plans) 49 | 50 | self.setup_DA_params() 51 | 52 | self.folder_with_preprocessed_data = join(self.dataset_directory, self.plans['data_identifier'] + 53 | "_stage%d" % self.stage) 54 | if training: 55 | self.dl_tr, self.dl_val = self.get_basic_generators() 56 | if self.unpack_data: 57 | print("unpacking dataset") 58 | unpack_dataset(self.folder_with_preprocessed_data) 59 | print("done") 60 | else: 61 | print("INFO: Not unpacking data! Training may be slow due to that. Pray you are not using 2d or you " 62 | "will wait all winter for your model to finish!") 63 | self.tr_gen, self.val_gen = get_no_augmentation(self.dl_tr, self.dl_val, 64 | self.data_aug_params[ 65 | 'patch_size_for_spatialtransform'], 66 | self.data_aug_params) 67 | self.print_to_log_file("TRAINING KEYS:\n %s" % (str(self.dataset_tr.keys())), 68 | also_print_to_console=False) 69 | self.print_to_log_file("VALIDATION KEYS:\n %s" % (str(self.dataset_val.keys())), 70 | also_print_to_console=False) 71 | else: 72 | pass 73 | self.initialize_network_optimizer_and_scheduler() 74 | assert isinstance(self.network, (SegmentationNetwork, nn.DataParallel)) 75 | self.was_initialized = True 76 | self.data_aug_params['mirror_axes'] = () 77 | -------------------------------------------------------------------------------- /nnunet/experiment_planning/summarize_plans.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from batchgenerators.utilities.file_and_folder_operations import * 16 | from nnunet.paths import preprocessing_output_dir 17 | 18 | 19 | # This file is intended to double check nnUNets design choices. It is intended to be used for developent purposes only 20 | def summarize_plans(file): 21 | plans = load_pickle(file) 22 | print("num_classes: ", plans['num_classes']) 23 | print("modalities: ", plans['modalities']) 24 | print("use_mask_for_norm", plans['use_mask_for_norm']) 25 | print("keep_only_largest_region", plans['keep_only_largest_region']) 26 | print("min_region_size_per_class", plans['min_region_size_per_class']) 27 | print("min_size_per_class", plans['min_size_per_class']) 28 | print("normalization_schemes", plans['normalization_schemes']) 29 | print("stages...\n") 30 | 31 | for i in range(len(plans['plans_per_stage'])): 32 | print("stage: ", i) 33 | print(plans['plans_per_stage'][i]) 34 | print("") 35 | 36 | 37 | def write_plans_to_file(f, plans_file): 38 | a = load_pickle(plans_file) 39 | stages = list(a['plans_per_stage'].keys()) 40 | stages.sort() 41 | for stage in stages: 42 | patch_size_in_mm = [i * j for i, j in zip(a['plans_per_stage'][stages[stage]]['patch_size'], 43 | a['plans_per_stage'][stages[stage]]['current_spacing'])] 44 | median_patient_size_in_mm = [i * j for i, j in zip(a['plans_per_stage'][stages[stage]]['median_patient_size_in_voxels'], 45 | a['plans_per_stage'][stages[stage]]['current_spacing'])] 46 | f.write(plans_file.split("/")[-2]) 47 | f.write(";%s" % plans_file.split("/")[-1]) 48 | f.write(";%d" % stage) 49 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['batch_size'])) 50 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['num_pool_per_axis'])) 51 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['patch_size'])) 52 | f.write(";%s" % str([str("%03.2f" % i) for i in patch_size_in_mm])) 53 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['median_patient_size_in_voxels'])) 54 | f.write(";%s" % str([str("%03.2f" % i) for i in median_patient_size_in_mm])) 55 | f.write(";%s" % str([str("%03.2f" % i) for i in a['plans_per_stage'][stages[stage]]['current_spacing']])) 56 | f.write(";%s" % str([str("%03.2f" % i) for i in a['plans_per_stage'][stages[stage]]['original_spacing']])) 57 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['pool_op_kernel_sizes'])) 58 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['conv_kernel_sizes'])) 59 | f.write(";%s" % str(a['data_identifier'])) 60 | f.write("\n") 61 | 62 | 63 | if __name__ == "__main__": 64 | base_dir = preprocessing_output_dir 65 | task_dirs = [i for i in subdirs(preprocessing_output_dir, join=False, prefix="Task") if i.find("BrainTumor") == -1 and i.find("MSSeg") == -1] 66 | print("found %d tasks" % len(task_dirs)) 67 | """for t in task_dirs: 68 | print(t, "\n") 69 | tmp = join(preprocessing_output_dir, t) 70 | print("##### 2D #####") 71 | summarize_plans(join(tmp, my_plans_identifier + "_plans_2D.pkl")) 72 | print("##### 3D #####") 73 | summarize_plans(join(tmp, my_plans_identifier + "_plans_3D.pkl")) 74 | print("-------------------------------------------------------\n")""" 75 | 76 | with open("2019_02_06_plans_summary.csv", 'w') as f: 77 | f.write("task;plans_file;stage;batch_size;num_pool_per_axis;patch_size;patch_size(mm);median_patient_size_in_voxels;median_patient_size_in_mm;current_spacing;original_spacing;pool_op_kernel_sizes;conv_kernel_sizes\n") 78 | for t in task_dirs: 79 | print(t) 80 | tmp = join(preprocessing_output_dir, t) 81 | plans_files = [i for i in subfiles(tmp, suffix=".pkl", join=False) if i.find("_plans_") != -1 and i.find("Dgx2") == -1] 82 | for p in plans_files: 83 | write_plans_to_file(f, join(tmp, p)) 84 | 85 | 86 | -------------------------------------------------------------------------------- /nnunet/dataset_conversion/LiverTumorSegmentationChallenge.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from collections import OrderedDict 16 | import SimpleITK as sitk 17 | from batchgenerators.utilities.file_and_folder_operations import * 18 | from multiprocessing import Pool 19 | import numpy as np 20 | from scipy.ndimage import label 21 | 22 | 23 | def export_segmentations(indir, outdir): 24 | niftis = subfiles(indir, suffix='nii.gz', join=False) 25 | for n in niftis: 26 | identifier = str(n.split("_")[-1][:-7]) 27 | outfname = join(outdir, "test-segmentation-%s.nii" % identifier) 28 | img = sitk.ReadImage(join(indir, n)) 29 | sitk.WriteImage(img, outfname) 30 | 31 | 32 | def export_segmentations_postprocess(indir, outdir): 33 | maybe_mkdir_p(outdir) 34 | niftis = subfiles(indir, suffix='nii.gz', join=False) 35 | for n in niftis: 36 | print("\n", n) 37 | identifier = str(n.split("_")[-1][:-7]) 38 | outfname = join(outdir, "test-segmentation-%s.nii" % identifier) 39 | img = sitk.ReadImage(join(indir, n)) 40 | img_npy = sitk.GetArrayFromImage(img) 41 | lmap, num_objects = label((img_npy > 0).astype(int)) 42 | sizes = [] 43 | for o in range(1, num_objects + 1): 44 | sizes.append((lmap == o).sum()) 45 | mx = np.argmax(sizes) + 1 46 | print(sizes) 47 | img_npy[lmap != mx] = 0 48 | img_new = sitk.GetImageFromArray(img_npy) 49 | img_new.CopyInformation(img) 50 | sitk.WriteImage(img_new, outfname) 51 | 52 | 53 | if __name__ == "__main__": 54 | train_dir = "/media/fabian/DeepLearningData/tmp/LITS-Challenge-Train-Data" 55 | test_dir = "/media/fabian/My Book/datasets/LiTS/test_data" 56 | 57 | 58 | output_folder = "/media/fabian/My Book/MedicalDecathlon/MedicalDecathlon_raw_splitted/Task29_LITS" 59 | img_dir = join(output_folder, "imagesTr") 60 | lab_dir = join(output_folder, "labelsTr") 61 | img_dir_te = join(output_folder, "imagesTs") 62 | maybe_mkdir_p(img_dir) 63 | maybe_mkdir_p(lab_dir) 64 | maybe_mkdir_p(img_dir_te) 65 | 66 | 67 | def load_save_train(args): 68 | data_file, seg_file = args 69 | pat_id = data_file.split("/")[-1] 70 | pat_id = "train_" + pat_id.split("-")[-1][:-4] 71 | 72 | img_itk = sitk.ReadImage(data_file) 73 | sitk.WriteImage(img_itk, join(img_dir, pat_id + "_0000.nii.gz")) 74 | 75 | img_itk = sitk.ReadImage(seg_file) 76 | sitk.WriteImage(img_itk, join(lab_dir, pat_id + ".nii.gz")) 77 | return pat_id 78 | 79 | def load_save_test(args): 80 | data_file = args 81 | pat_id = data_file.split("/")[-1] 82 | pat_id = "test_" + pat_id.split("-")[-1][:-4] 83 | 84 | img_itk = sitk.ReadImage(data_file) 85 | sitk.WriteImage(img_itk, join(img_dir_te, pat_id + "_0000.nii.gz")) 86 | return pat_id 87 | 88 | nii_files_tr_data = subfiles(train_dir, True, "volume", "nii", True) 89 | nii_files_tr_seg = subfiles(train_dir, True, "segmen", "nii", True) 90 | 91 | nii_files_ts = subfiles(test_dir, True, "test-volume", "nii", True) 92 | 93 | p = Pool(8) 94 | train_ids = p.map(load_save_train, zip(nii_files_tr_data, nii_files_tr_seg)) 95 | test_ids = p.map(load_save_test, nii_files_ts) 96 | p.close() 97 | p.join() 98 | 99 | json_dict = OrderedDict() 100 | json_dict['name'] = "LITS" 101 | json_dict['description'] = "LITS" 102 | json_dict['tensorImageSize'] = "4D" 103 | json_dict['reference'] = "see challenge website" 104 | json_dict['licence'] = "see challenge website" 105 | json_dict['release'] = "0.0" 106 | json_dict['modality'] = { 107 | "0": "CT" 108 | } 109 | 110 | json_dict['labels'] = { 111 | "0": "background", 112 | "1": "liver", 113 | "2": "tumor" 114 | } 115 | 116 | json_dict['numTraining'] = len(train_ids) 117 | json_dict['numTest'] = len(test_ids) 118 | json_dict['training'] = [{'image': "./imagesTr/%s.nii.gz" % i, "label": "./labelsTr/%s.nii.gz" % i} for i in train_ids] 119 | json_dict['test'] = ["./imagesTs/%s.nii.gz" % i for i in test_ids] 120 | 121 | with open(os.path.join(output_folder, "dataset.json"), 'w') as f: 122 | json.dump(json_dict, f, indent=4, sort_keys=True) -------------------------------------------------------------------------------- /nnunet/nnUNet_result/nnUNet/3d_fullres/Task02_Heart/nnUNetTrainer__nnUNetPlans/fold_2/debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "all_tr_losses": "[]", 3 | "all_val_eval_metrics": "[]", 4 | "all_val_losses": "[]", 5 | "all_val_losses_tr_mode": "[]", 6 | "also_val_in_tr_mode": "False", 7 | "base_num_features": "30", 8 | "basic_generator_patch_size": "[131 266 201]", 9 | "batch_dice": "False", 10 | "batch_size": "1", 11 | "best_MA_tr_loss_for_patience": "None", 12 | "best_epoch_based_on_MA_tr_loss": "None", 13 | "best_val_eval_criterion_MA": "None", 14 | "classes": "[1]", 15 | "data_aug_params": "{'selected_data_channels': None, 'selected_seg_channels': [0], 'do_elastic': True, 'elastic_deform_alpha': (0.0, 900.0), 'elastic_deform_sigma': (9.0, 13.0), 'do_scaling': True, 'scale_range': (0.85, 1.25), 'do_rotation': True, 'rotation_x': (-0.2617993877991494, 0.2617993877991494), 'rotation_y': (-0.2617993877991494, 0.2617993877991494), 'rotation_z': (-0.2617993877991494, 0.2617993877991494), 'random_crop': False, 'random_crop_dist_to_border': None, 'do_gamma': True, 'gamma_retain_stats': True, 'gamma_range': (0.7, 1.5), 'p_gamma': 0.3, 'num_threads': 12, 'num_cached_per_thread': 1, 'mirror': True, 'mirror_axes': (0, 1, 2), 'p_eldef': 0.2, 'p_scale': 0.2, 'p_rot': 0.2, 'dummy_2D': False, 'mask_was_used_for_normalization': OrderedDict([(0, True)]), 'all_segmentation_labels': None, 'move_last_seg_chanel_to_data': False, 'border_mode_data': 'constant', 'advanced_pyramid_augmentations': False, 'patch_size_for_spatialtransform': array([ 64, 192, 160])}", 16 | "dataset_directory": "/nnunet/nnunet/nnUNet_preprocessed/Task02_Heart", 17 | "deterministic": "False", 18 | "dl_tr": "", 19 | "dl_val": "", 20 | "do_dummy_2D_aug": "False", 21 | "epoch": "0", 22 | "experiment_name": "nnUNetTrainer", 23 | "fold": "2", 24 | "folder_with_preprocessed_data": "/nnunet/nnunet/nnUNet_preprocessed/Task02_Heart/nnUNet_stage0", 25 | "fp16": "False", 26 | "gt_niftis_folder": "/nnunet/nnunet/nnUNet_preprocessed/Task02_Heart/gt_segmentations", 27 | "inference_pad_border_mode": "constant", 28 | "inference_pad_kwargs": "{'constant_values': 0}", 29 | "init_args": "('/nnunet/nnunet/nnUNet_preprocessed/Task02_Heart/nnUNetPlans_plans_3D.pkl', 2, '/nnunet/nnUNet/nnUNet_result/nnUNet/3d_fullres/Task02_Heart/nnUNetTrainer__nnUNetPlans', '/nnunet/nnunet/nnUNet_preprocessed/Task02_Heart', False, 0, True, False, False)", 30 | "initial_lr": "0.0003", 31 | "log_file": "/nnunet/nnUNet/nnUNet_result/nnUNet/3d_fullres/Task02_Heart/nnUNetTrainer__nnUNetPlans/fold_2/training_log_2019_11_28_01_05_29.txt", 32 | "lr_scheduler": "", 33 | "lr_scheduler_eps": "0.001", 34 | "lr_scheduler_patience": "30", 35 | "lr_threshold": "1e-06", 36 | "max_num_epochs": "1000", 37 | "min_region_size_per_class": "OrderedDict([(1, 34402.048713488504)])", 38 | "min_size_per_class": "None", 39 | "net_conv_kernel_sizes": "[[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]]", 40 | "net_num_pool_op_kernel_sizes": "[[1, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]]", 41 | "net_pool_per_axis": "[4, 5, 5]", 42 | "normalization_schemes": "OrderedDict([(0, 'nonCT')])", 43 | "num_batches_per_epoch": "250", 44 | "num_classes": "2", 45 | "num_input_channels": "1", 46 | "num_val_batches_per_epoch": "50", 47 | "online_eval_fn": "[]", 48 | "online_eval_foreground_dc": "[]", 49 | "online_eval_fp": "[]", 50 | "online_eval_tp": "[]", 51 | "only_keep_largest_connected_component": "OrderedDict([((1,), True)])", 52 | "optimizer": "Adam (\nParameter Group 0\n amsgrad: True\n betas: (0.9, 0.999)\n eps: 1e-08\n lr: 0.0003\n weight_decay: 3e-05\n)", 53 | "output_folder": "/nnunet/nnUNet/nnUNet_result/nnUNet/3d_fullres/Task02_Heart/nnUNetTrainer__nnUNetPlans/fold_2", 54 | "output_folder_base": "/nnunet/nnUNet/nnUNet_result/nnUNet/3d_fullres/Task02_Heart/nnUNetTrainer__nnUNetPlans", 55 | "oversample_foreground_percent": "0.33", 56 | "pad_all_sides": "None", 57 | "patch_size": "[ 64 192 160]", 58 | "patience": "50", 59 | "plans_file": "/nnunet/nnunet/nnUNet_preprocessed/Task02_Heart/nnUNetPlans_plans_3D.pkl", 60 | "save_every": "50", 61 | "save_latest_only": "True", 62 | "stage": "0", 63 | "threeD": "True", 64 | "tr_gen": "", 65 | "train_loss_MA": "None", 66 | "train_loss_MA_alpha": "0.93", 67 | "train_loss_MA_eps": "0.0005", 68 | "transpose_backward": "[0, 1, 2]", 69 | "transpose_forward": "[0, 1, 2]", 70 | "unpack_data": "True", 71 | "use_mask_for_norm": "OrderedDict([(0, True)])", 72 | "val_eval_criterion_MA": "None", 73 | "val_eval_criterion_alpha": "0.9", 74 | "val_gen": "", 75 | "was_initialized": "True", 76 | "weight_decay": "3e-05" 77 | } -------------------------------------------------------------------------------- /nnunet/training/cascade_stuff/predict_next_stage.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | from batchgenerators.utilities.file_and_folder_operations import * 17 | import argparse 18 | from nnunet.preprocessing.preprocessing import resample_data_or_seg 19 | from batchgenerators.utilities.file_and_folder_operations import maybe_mkdir_p 20 | import nnunet 21 | from nnunet.run.default_configuration import get_default_configuration 22 | from multiprocessing import Pool 23 | 24 | from nnunet.training.model_restore import recursive_find_trainer 25 | from nnunet.training.network_training.nnUNetTrainer import nnUNetTrainer 26 | 27 | 28 | def resample_and_save(predicted, target_shape, output_file): 29 | predicted_new_shape = resample_data_or_seg(predicted, target_shape, False, order=1, do_separate_z=False, cval=0) 30 | seg_new_shape = predicted_new_shape.argmax(0) 31 | np.savez_compressed(output_file, data=seg_new_shape.astype(np.uint8)) 32 | 33 | 34 | def predict_next_stage(trainer, stage_to_be_predicted_folder): 35 | output_folder = join(pardir(trainer.output_folder), "pred_next_stage") 36 | maybe_mkdir_p(output_folder) 37 | 38 | process_manager = Pool(2) 39 | results = [] 40 | 41 | for pat in trainer.dataset_val.keys(): 42 | print(pat) 43 | data_file = trainer.dataset_val[pat]['data_file'] 44 | data_preprocessed = np.load(data_file)['data'][:-1] 45 | predicted = trainer.predict_preprocessed_data_return_softmax(data_preprocessed, True, 1, False, 1, 46 | trainer.data_aug_params['mirror_axes'], 47 | True, True, 2, trainer.patch_size, True) 48 | data_file_nofolder = data_file.split("/")[-1] 49 | data_file_nextstage = join(stage_to_be_predicted_folder, data_file_nofolder) 50 | data_nextstage = np.load(data_file_nextstage)['data'] 51 | target_shp = data_nextstage.shape[1:] 52 | output_file = join(output_folder, data_file_nextstage.split("/")[-1][:-4] + "_segFromPrevStage.npz") 53 | results.append(process_manager.starmap_async(resample_and_save, [(predicted, target_shp, output_file)])) 54 | 55 | _ = [i.get() for i in results] 56 | 57 | 58 | if __name__ == "__main__": 59 | """ 60 | RUNNING THIS SCRIPT MANUALLY IS USUALLY NOT NECESSARY. USE THE run_training.py FILE! 61 | 62 | This script is intended for predicting all the low resolution predictions of 3d_lowres for the next stage of the 63 | cascade. It needs to run once for each fold so that the segmentation is only generated for the validation set 64 | and not on the data the network was trained on. Run it with 65 | python predict_next_stage TRAINERCLASS TASK FOLD""" 66 | 67 | parser = argparse.ArgumentParser() 68 | parser.add_argument("network_trainer") 69 | parser.add_argument("task") 70 | parser.add_argument("fold", type=int) 71 | 72 | args = parser.parse_args() 73 | 74 | trainerclass = args.network_trainer 75 | task = args.task 76 | fold = args.fold 77 | 78 | plans_file, folder_with_preprocessed_data, output_folder_name, dataset_directory, batch_dice, stage = \ 79 | get_default_configuration("3d_lowres", task) 80 | 81 | trainer_class = recursive_find_trainer([join(nnunet.__path__[0], "training", "network_training")], trainerclass, 82 | "nnunet.training.network_training") 83 | 84 | if trainer_class is None: 85 | raise RuntimeError("Could not find trainer class in nnunet.training.network_training") 86 | else: 87 | assert issubclass(trainer_class, 88 | nnUNetTrainer), "network_trainer was found but is not derived from nnUNetTrainer" 89 | 90 | trainer = trainer_class(plans_file, fold, folder_with_preprocessed_data,output_folder=output_folder_name, 91 | dataset_directory=dataset_directory, batch_dice=batch_dice, stage=stage) 92 | 93 | trainer.initialize(False) 94 | trainer.load_dataset() 95 | trainer.do_split() 96 | trainer.load_best_checkpoint(train=False) 97 | 98 | stage_to_be_predicted_folder = join(dataset_directory, trainer.plans['data_identifier'] + "_stage%d" % 1) 99 | output_folder = join(pardir(trainer.output_folder), "pred_next_stage") 100 | maybe_mkdir_p(output_folder) 101 | 102 | predict_next_stage(trainer, stage_to_be_predicted_folder) 103 | 104 | -------------------------------------------------------------------------------- /nnunet/dataset_conversion/AutomaticCardiacDetectionChallenge.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from collections import OrderedDict 16 | from batchgenerators.utilities.file_and_folder_operations import * 17 | import shutil 18 | 19 | 20 | def convert_to_submission(source_dir, target_dir): 21 | niftis = subfiles(source_dir, join=False, suffix=".nii.gz") 22 | patientids = np.unique([i[:10] for i in niftis]) 23 | maybe_mkdir_p(target_dir) 24 | for p in patientids: 25 | files_of_that_patient = subfiles(source_dir, prefix=p, suffix=".nii.gz", join=False) 26 | assert len(files_of_that_patient) 27 | files_of_that_patient.sort() 28 | # first is ED, second is ES 29 | shutil.copy(join(source_dir, files_of_that_patient[0]), join(target_dir, p + "_ED.nii.gz")) 30 | shutil.copy(join(source_dir, files_of_that_patient[1]), join(target_dir, p + "_ES.nii.gz")) 31 | 32 | 33 | if __name__ == "__main__": 34 | folder = "/media/fabian/My Book/datasets/ACDC/training" 35 | folder_test = "/media/fabian/My Book/datasets/ACDC/testing/testing" 36 | out_folder = "/media/fabian/My Book/MedicalDecathlon/MedicalDecathlon_raw_splitted/Task27_ACDC" 37 | 38 | maybe_mkdir_p(join(out_folder, "imagesTr")) 39 | maybe_mkdir_p(join(out_folder, "imagesTs")) 40 | maybe_mkdir_p(join(out_folder, "labelsTr")) 41 | 42 | # train 43 | all_train_files = [] 44 | patient_dirs_train = subfolders(folder, prefix="patient") 45 | for p in patient_dirs_train: 46 | current_dir = p 47 | data_files_train = [i for i in subfiles(current_dir, suffix=".nii.gz") if i.find("_gt") == -1 and i.find("_4d") == -1] 48 | corresponding_seg_files = [i[:-7] + "_gt.nii.gz" for i in data_files_train] 49 | for d, s in zip(data_files_train, corresponding_seg_files): 50 | patient_identifier = d.split("/")[-1][:-7] 51 | all_train_files.append(patient_identifier + "_0000.nii.gz") 52 | shutil.copy(d, join(out_folder, "imagesTr", patient_identifier + "_0000.nii.gz")) 53 | shutil.copy(s, join(out_folder, "labelsTr", patient_identifier + ".nii.gz")) 54 | 55 | # test 56 | all_test_files = [] 57 | patient_dirs_test = subfolders(folder_test, prefix="patient") 58 | for p in patient_dirs_test: 59 | current_dir = p 60 | data_files_test = [i for i in subfiles(current_dir, suffix=".nii.gz") if i.find("_gt") == -1 and i.find("_4d") == -1] 61 | for d in data_files_test: 62 | patient_identifier = d.split("/")[-1][:-7] 63 | all_test_files.append(patient_identifier + "_0000.nii.gz") 64 | shutil.copy(d, join(out_folder, "imagesTs", patient_identifier + "_0000.nii.gz")) 65 | 66 | 67 | json_dict = OrderedDict() 68 | json_dict['name'] = "ACDC" 69 | json_dict['description'] = "cardias cine MRI segmentation" 70 | json_dict['tensorImageSize'] = "4D" 71 | json_dict['reference'] = "see ACDC challenge" 72 | json_dict['licence'] = "see ACDC challenge" 73 | json_dict['release'] = "0.0" 74 | json_dict['modality'] = { 75 | "0": "MRI", 76 | } 77 | json_dict['labels'] = { 78 | "0": "background", 79 | "1": "RV", 80 | "2": "MLV", 81 | "3": "LVC" 82 | } 83 | json_dict['numTraining'] = len(all_train_files) 84 | json_dict['numTest'] = len(all_test_files) 85 | json_dict['training'] = [{'image': "./imagesTr/%s.nii.gz" % i.split("/")[-1][:-12], "label": "./labelsTr/%s.nii.gz" % i.split("/")[-1][:-12]} for i in 86 | all_train_files] 87 | json_dict['test'] = ["./imagesTs/%s.nii.gz" % i.split("/")[-1][:-12] for i in all_test_files] 88 | 89 | save_json(json_dict, os.path.join(out_folder, "dataset.json")) 90 | 91 | # create a dummy split (patients need to be separated) 92 | import numpy as np 93 | from sklearn.model_selection import KFold 94 | 95 | splits = [] 96 | patients = np.unique([i[:10] for i in all_train_files]) 97 | patientids = [i[:-12] for i in all_train_files] 98 | 99 | kf = KFold(5, True, 12345) 100 | for tr, val in kf.split(patients): 101 | splits.append(OrderedDict()) 102 | tr_patients = patients[tr] 103 | splits[-1]['train'] = [i[:-12] for i in all_train_files if i[:10] in tr_patients] 104 | val_patients = patients[val] 105 | splits[-1]['val'] = [i[:-12] for i in all_train_files if i[:10] in val_patients] 106 | 107 | save_pickle(splits, "/media/fabian/nnunet/Task27_ACDC/splits_final.pkl") -------------------------------------------------------------------------------- /nnunet/inference/ensemble_predictions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from nnunet.inference.segmentation_export import save_segmentation_nifti_from_softmax 16 | from batchgenerators.utilities.file_and_folder_operations import * 17 | import numpy as np 18 | from multiprocessing import Pool 19 | 20 | 21 | def merge_files(args): 22 | files, properties_file, out_file, only_keep_largest_connected_component, min_region_size_per_class, override = args 23 | if override or not isfile(out_file): 24 | softmax = [np.load(f)['softmax'][None] for f in files] 25 | softmax = np.vstack(softmax) 26 | softmax = np.mean(softmax, 0) 27 | props = load_pickle(properties_file) 28 | save_segmentation_nifti_from_softmax(softmax, out_file, props, 1, None, None, None) 29 | 30 | 31 | def merge(folders, output_folder, threads, override=True): 32 | maybe_mkdir_p(output_folder) 33 | 34 | patient_ids = [subfiles(i, suffix=".npz", join=False) for i in folders] 35 | patient_ids = [i for j in patient_ids for i in j] 36 | patient_ids = [i[:-4] for i in patient_ids] 37 | patient_ids = np.unique(patient_ids) 38 | 39 | for f in folders: 40 | assert all([isfile(join(f, i + ".npz")) for i in patient_ids]), "Not all patient npz are available in " \ 41 | "all folders" 42 | assert all([isfile(join(f, i + ".pkl")) for i in patient_ids]), "Not all patient pkl are available in " \ 43 | "all folders" 44 | 45 | files = [] 46 | property_files = [] 47 | out_files = [] 48 | for p in patient_ids: 49 | files.append([join(f, p + ".npz") for f in folders]) 50 | property_files.append(join(folders[0], p + ".pkl")) 51 | out_files.append(join(output_folder, p + ".nii.gz")) 52 | 53 | plans = load_pickle(join(folders[0], "plans.pkl")) 54 | 55 | only_keep_largest_connected_component, min_region_size_per_class = plans['keep_only_largest_region'], \ 56 | plans['min_region_size_per_class'] 57 | p = Pool(threads) 58 | p.map(merge_files, zip(files, property_files, out_files, [only_keep_largest_connected_component] * len(out_files), 59 | [min_region_size_per_class] * len(out_files), [override] * len(out_files))) 60 | p.close() 61 | p.join() 62 | 63 | 64 | if __name__ == "__main__": 65 | import argparse 66 | parser = argparse.ArgumentParser(description="This requires that all folders to be merged use the same " 67 | "postprocessing function " 68 | "(nnunet.utilities.postprocessing.postprocess_segmentation). " 69 | "This will be the case if the corresponding " 70 | "models were trained with nnUNetTrainer or nnUNetTrainerCascadeFullRes" 71 | "but may not be the case if you added models of your own that use a " 72 | "different postprocessing. This script also requires a plans file to" 73 | "be present in all of the folders (if they are not present you can " 74 | "take them from the respective model training output folders. " 75 | "Parameters for the postprocessing " 76 | "will be taken from the plans file. If the folders were created by " 77 | "predict_folder.py then the plans file will have been copied " 78 | "automatically (if --save_npz is specified)") 79 | parser.add_argument('-f', '--folders', nargs='+', help="list of folders to merge. All folders must contain npz " 80 | "files", required=True) 81 | parser.add_argument('-o', '--output_folder', help="where to save the results", required=True, type=str) 82 | parser.add_argument('-t', '--threads', help="number of threads used to saving niftis", required=False, default=2, 83 | type=int) 84 | 85 | args = parser.parse_args() 86 | 87 | folders = args.folders 88 | threads = args.threads 89 | output_folder = args.output_folder 90 | 91 | merge(folders, output_folder, threads, override=True) 92 | -------------------------------------------------------------------------------- /nnunet/training/data_augmentation/custom_transforms.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | from batchgenerators.transforms import AbstractTransform 17 | 18 | 19 | class RemoveKeyTransform(AbstractTransform): 20 | def __init__(self, key_to_remove): 21 | self.key_to_remove = key_to_remove 22 | 23 | def __call__(self, **data_dict): 24 | _ = data_dict.pop(self.key_to_remove, None) 25 | return data_dict 26 | 27 | 28 | class MaskTransform(AbstractTransform): 29 | def __init__(self, dct_for_where_it_was_used, mask_idx_in_seg=1, set_outside_to=0, data_key="data", seg_key="seg"): 30 | """ 31 | data[mask < 0] = 0 32 | Sets everything outside the mask to 0. CAREFUL! outside is defined as < 0, not =0 (in the Mask)!!! 33 | 34 | :param dct_for_where_it_was_used: 35 | :param mask_idx_in_seg: 36 | :param set_outside_to: 37 | :param data_key: 38 | :param seg_key: 39 | """ 40 | self.dct_for_where_it_was_used = dct_for_where_it_was_used 41 | self.seg_key = seg_key 42 | self.data_key = data_key 43 | self.set_outside_to = set_outside_to 44 | self.mask_idx_in_seg = mask_idx_in_seg 45 | 46 | def __call__(self, **data_dict): 47 | seg = data_dict.get(self.seg_key) 48 | if seg is None or seg.shape[1] < self.mask_idx_in_seg: 49 | raise Warning("mask not found, seg may be missing or seg[:, mask_idx_in_seg] may not exist") 50 | data = data_dict.get(self.data_key) 51 | for b in range(data.shape[0]): 52 | mask = seg[b, self.mask_idx_in_seg] 53 | for c in range(data.shape[1]): 54 | if self.dct_for_where_it_was_used[c]: 55 | data[b, c][mask < 0] = self.set_outside_to 56 | data_dict[self.data_key] = data 57 | return data_dict 58 | 59 | 60 | def convert_3d_to_2d_generator(data_dict): 61 | shp = data_dict['data'].shape 62 | data_dict['data'] = data_dict['data'].reshape((shp[0], shp[1] * shp[2], shp[3], shp[4])) 63 | data_dict['orig_shape_data'] = shp 64 | shp = data_dict['seg'].shape 65 | data_dict['seg'] = data_dict['seg'].reshape((shp[0], shp[1] * shp[2], shp[3], shp[4])) 66 | data_dict['orig_shape_seg'] = shp 67 | return data_dict 68 | 69 | 70 | def convert_2d_to_3d_generator(data_dict): 71 | shp = data_dict['orig_shape_data'] 72 | current_shape = data_dict['data'].shape 73 | data_dict['data'] = data_dict['data'].reshape((shp[0], shp[1], shp[2], current_shape[-2], current_shape[-1])) 74 | shp = data_dict['orig_shape_seg'] 75 | current_shape_seg = data_dict['seg'].shape 76 | data_dict['seg'] = data_dict['seg'].reshape((shp[0], shp[1], shp[2], current_shape_seg[-2], current_shape_seg[-1])) 77 | return data_dict 78 | 79 | 80 | class Convert3DTo2DTransform(AbstractTransform): 81 | def __init__(self): 82 | pass 83 | 84 | def __call__(self, **data_dict): 85 | return convert_3d_to_2d_generator(data_dict) 86 | 87 | 88 | class Convert2DTo3DTransform(AbstractTransform): 89 | def __init__(self): 90 | pass 91 | 92 | def __call__(self, **data_dict): 93 | return convert_2d_to_3d_generator(data_dict) 94 | 95 | 96 | class ConvertSegmentationToRegionsTransform(AbstractTransform): 97 | def __init__(self, regions, seg_key="seg", output_key="seg", seg_channel=0): 98 | """ 99 | regions are tuple of tuples where each inner tuple holds the class indices that are merged into one region, example: 100 | regions= ((1, 2), (2, )) will result in 2 regions: one covering the region of labels 1&2 and the other just 2 101 | :param regions: 102 | :param seg_key: 103 | :param output_key: 104 | """ 105 | self.seg_channel = seg_channel 106 | self.output_key = output_key 107 | self.seg_key = seg_key 108 | self.regions = regions 109 | 110 | def __call__(self, **data_dict): 111 | seg = data_dict.get(self.seg_key) 112 | num_regions = len(self.regions) 113 | if seg is not None: 114 | seg_shp = seg.shape 115 | output_shape = list(seg_shp) 116 | output_shape[1] = num_regions 117 | region_output = np.zeros(output_shape, dtype=seg.dtype) 118 | for b in range(seg_shp[0]): 119 | for r in range(num_regions): 120 | for l in self.regions[r]: 121 | region_output[b, r][seg[b, self.seg_channel] == l] = 1 122 | data_dict[self.output_key] = region_output 123 | return data_dict 124 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2461545.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "Siemens", 4 | "ManufacturersModelName": "SOMATOM_Definition_AS+", 5 | "InstitutionName": "SH_RENJI_HOSPITAL", 6 | "InstitutionAddress": "Dongfang_Rd_5D7881_PuDong_new_Dist_CN", 7 | "DeviceSerialNumber": "64135", 8 | "StationName": "CTAWP64135", 9 | "BodyPartExamined": "ABDOMEN", 10 | "PatientPosition": "FFS", 11 | "SoftwareVersions": "syngo_CT_2012B", 12 | "SeriesDescription": "Arterial_Phase_1.0_B31f", 13 | "ProtocolName": "2_Abd2_Phase", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "CT", "SOM5", "SPI"], 15 | "SeriesNumber": 7, 16 | "AcquisitionTime": "09:59:52.837000", 17 | "AcquisitionNumber": 10, 18 | "XRayExposure": 300, 19 | "ReconMatrixPE": 512, 20 | "SliceTiming": [ 21 | 0, 22 | 0.00781, 23 | 0.01563, 24 | 0.03125, 25 | 0.04688, 26 | 0.05469, 27 | 0.07031, 28 | 0.08594, 29 | 0.09375, 30 | 0.10938, 31 | 0.125, 32 | 0.13281, 33 | 0.14844, 34 | 0.16406, 35 | 0.17188, 36 | 0.1875, 37 | 0.20313, 38 | 0.21094, 39 | 0.22656, 40 | 0.24219, 41 | 0.25781, 42 | 0.26563, 43 | 0.28125, 44 | 0.29688, 45 | 0.30469, 46 | 0.32031, 47 | 0.33594, 48 | 0.34375, 49 | 0.35938, 50 | 0.36719, 51 | 0.38281, 52 | 0.39844, 53 | 0.40625, 54 | 0.42188, 55 | 0.4375, 56 | 0.44531, 57 | 0.46094, 58 | 0.47656, 59 | 0.48438, 60 | 0.5, 61 | 0.51563, 62 | 0.52344, 63 | 0.53906, 64 | 0.55469, 65 | 0.5625, 66 | 0.57813, 67 | 0.59375, 68 | 0.60156, 69 | 0.61719, 70 | 0.63281, 71 | 0.64063, 72 | 0.65625, 73 | 0.67188, 74 | 0.67969, 75 | 0.69531, 76 | 0.71094, 77 | 0.71875, 78 | 0.73438, 79 | 0.75, 80 | 0.75781, 81 | 0.77344, 82 | 0.78906, 83 | 0.79688, 84 | 0.8125, 85 | 0.82813, 86 | 0.83594, 87 | 0.85156, 88 | 0.86719, 89 | 0.875, 90 | 0.89063, 91 | 0.90625, 92 | 0.91406, 93 | 0.92969, 94 | 0.94531, 95 | 0.95313, 96 | 0.96875, 97 | 0.98438, 98 | 0.99219, 99 | 1.00781, 100 | 1.02344, 101 | 1.03125, 102 | 1.04688, 103 | 1.0625, 104 | 1.07031, 105 | 1.08594, 106 | 1.10156, 107 | 1.10938, 108 | 1.125, 109 | 1.14063, 110 | 1.14844, 111 | 1.16406, 112 | 1.17969, 113 | 1.1875, 114 | 1.20313, 115 | 1.21875, 116 | 1.22656, 117 | 1.24219, 118 | 1.25781, 119 | 1.26563, 120 | 1.28125, 121 | 1.29688, 122 | 1.30469, 123 | 1.32031, 124 | 1.33594, 125 | 1.34375, 126 | 1.35938, 127 | 1.375, 128 | 1.39063, 129 | 1.39844, 130 | 1.41406, 131 | 1.42188, 132 | 1.4375, 133 | 1.45313, 134 | 1.46094, 135 | 1.47656, 136 | 1.49219, 137 | 1.50781, 138 | 1.51563, 139 | 1.53125, 140 | 1.54688, 141 | 1.55469, 142 | 1.57031, 143 | 1.58594, 144 | 1.59375, 145 | 1.60938, 146 | 1.61719, 147 | 1.63281, 148 | 1.64844, 149 | 1.65625, 150 | 1.67188, 151 | 1.6875, 152 | 1.69531, 153 | 1.71094, 154 | 1.72656, 155 | 1.73438, 156 | 1.75, 157 | 1.76563, 158 | 1.77344, 159 | 1.78906, 160 | 1.80469, 161 | 1.82031, 162 | 1.82813, 163 | 1.84375, 164 | 1.85938, 165 | 1.86719, 166 | 1.88281, 167 | 1.89844, 168 | 1.90625, 169 | 1.92188, 170 | 1.92969, 171 | 1.94531, 172 | 1.96094, 173 | 1.96875, 174 | 1.98438, 175 | 2, 176 | 2.00781, 177 | 2.02344, 178 | 2.03906, 179 | 2.04688, 180 | 2.0625, 181 | 2.07813, 182 | 2.08594, 183 | 2.10156, 184 | 2.11719, 185 | 2.125, 186 | 2.14063, 187 | 2.15625, 188 | 2.16406, 189 | 2.17969, 190 | 2.19531, 191 | 2.20313, 192 | 2.21875, 193 | 2.23438, 194 | 2.24219, 195 | 2.25781, 196 | 2.27344, 197 | 2.28125, 198 | 2.29688, 199 | 2.3125, 200 | 2.32031, 201 | 2.33594, 202 | 2.35156, 203 | 2.35938, 204 | 2.375, 205 | 2.39063, 206 | 2.39844, 207 | 2.41406, 208 | 2.42969, 209 | 2.4375, 210 | 2.45313, 211 | 2.46875, 212 | 2.47656, 213 | 2.49219, 214 | 2.50781, 215 | 2.51563, 216 | 2.53125, 217 | 2.54688, 218 | 2.55469, 219 | 2.57031, 220 | 2.58594, 221 | 2.59375, 222 | 2.60938, 223 | 2.625, 224 | 2.63281, 225 | 2.64844, 226 | 2.66406, 227 | 2.67188, 228 | 2.6875, 229 | 2.70313, 230 | 2.71094, 231 | 2.72656, 232 | 2.74219, 233 | 2.75, 234 | 2.76563, 235 | 2.78125, 236 | 2.78906, 237 | 2.80469, 238 | 2.82031, 239 | 2.82813, 240 | 2.84375, 241 | 2.85938, 242 | 2.86719, 243 | 2.88281, 244 | 2.89844, 245 | 2.90625, 246 | 2.92188, 247 | 2.9375, 248 | 2.94531, 249 | 2.96094, 250 | 2.97656, 251 | 2.98438, 252 | 3, 253 | 3.01563, 254 | 3.02344, 255 | 3.03906, 256 | 3.05469, 257 | 3.0625, 258 | 3.07813, 259 | 3.09375, 260 | 3.10938, 261 | 3.11719, 262 | 3.13281, 263 | 3.14844, 264 | 3.15625, 265 | 3.17188, 266 | 3.1875, 267 | 3.19531, 268 | 3.21094, 269 | 3.22656, 270 | 3.23438, 271 | 3.25, 272 | 3.26563, 273 | 3.27344, 274 | 3.28906, 275 | 3.30469, 276 | 3.3125, 277 | 3.32813, 278 | 3.34375, 279 | 3.35156, 280 | 3.36719, 281 | 3.38281, 282 | 3.39063, 283 | 3.40625, 284 | 3.42188, 285 | 3.42969, 286 | 3.44531, 287 | 3.46094, 288 | 3.46875, 289 | 3.48438, 290 | 3.49219, 291 | 3.50781, 292 | 3.52344, 293 | 3.53125, 294 | 3.54688, 295 | 3.5625, 296 | 3.57031, 297 | 3.58594 ], 298 | "ImageOrientationPatientDICOM": [ 299 | 1, 300 | 0, 301 | 0, 302 | 0, 303 | 1, 304 | 0 ], 305 | "ConversionSoftware": "dcm2niix", 306 | "ConversionSoftwareVersion": "v1.0.20190902" 307 | } 308 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2714255.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "Siemens", 4 | "ManufacturersModelName": "SOMATOM_Definition_AS+", 5 | "InstitutionName": "SH_RENJI_HOSPITAL", 6 | "InstitutionAddress": "Dongfang_Rd_5D7881_PuDong_new_Dist_CN", 7 | "DeviceSerialNumber": "64135", 8 | "StationName": "CTAWP64135", 9 | "BodyPartExamined": "ABDOMEN", 10 | "PatientPosition": "FFS", 11 | "SoftwareVersions": "syngo_CT_2012B", 12 | "SeriesDescription": "Delay_Phase_1.0_B31f", 13 | "ProtocolName": "4_Abd3_Phase", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "CT", "SOM5", "SPI"], 15 | "SeriesNumber": 12, 16 | "AcquisitionTime": "09:51:5.576000", 17 | "AcquisitionNumber": 17, 18 | "XRayExposure": 300, 19 | "ReconMatrixPE": 512, 20 | "SliceTiming": [ 21 | 3.64063, 22 | 3.625, 23 | 3.60938, 24 | 3.60156, 25 | 3.58594, 26 | 3.57031, 27 | 3.5625, 28 | 3.54688, 29 | 3.53125, 30 | 3.52344, 31 | 3.50781, 32 | 3.49219, 33 | 3.48438, 34 | 3.46875, 35 | 3.45313, 36 | 3.44531, 37 | 3.42969, 38 | 3.41406, 39 | 3.40625, 40 | 3.39063, 41 | 3.375, 42 | 3.36719, 43 | 3.35156, 44 | 3.33594, 45 | 3.32813, 46 | 3.3125, 47 | 3.29688, 48 | 3.28906, 49 | 3.27344, 50 | 3.25781, 51 | 3.25, 52 | 3.23438, 53 | 3.21875, 54 | 3.21094, 55 | 3.19531, 56 | 3.17969, 57 | 3.17188, 58 | 3.15625, 59 | 3.14063, 60 | 3.13281, 61 | 3.11719, 62 | 3.10156, 63 | 3.09375, 64 | 3.07813, 65 | 3.0625, 66 | 3.05469, 67 | 3.03906, 68 | 3.02344, 69 | 3.01563, 70 | 3, 71 | 2.98438, 72 | 2.97656, 73 | 2.96094, 74 | 2.94531, 75 | 2.9375, 76 | 2.92188, 77 | 2.90625, 78 | 2.89844, 79 | 2.88281, 80 | 2.86719, 81 | 2.85938, 82 | 2.84375, 83 | 2.82813, 84 | 2.82031, 85 | 2.80469, 86 | 2.78906, 87 | 2.78125, 88 | 2.76563, 89 | 2.75, 90 | 2.74219, 91 | 2.72656, 92 | 2.71094, 93 | 2.70313, 94 | 2.6875, 95 | 2.67188, 96 | 2.66406, 97 | 2.64844, 98 | 2.63281, 99 | 2.625, 100 | 2.60938, 101 | 2.59375, 102 | 2.58594, 103 | 2.57031, 104 | 2.55469, 105 | 2.54688, 106 | 2.53125, 107 | 2.51563, 108 | 2.50781, 109 | 2.49219, 110 | 2.47656, 111 | 2.46875, 112 | 2.45313, 113 | 2.4375, 114 | 2.42969, 115 | 2.41406, 116 | 2.39844, 117 | 2.39063, 118 | 2.375, 119 | 2.35938, 120 | 2.35156, 121 | 2.33594, 122 | 2.32031, 123 | 2.3125, 124 | 2.29688, 125 | 2.28125, 126 | 2.27344, 127 | 2.25781, 128 | 2.24219, 129 | 2.23438, 130 | 2.21875, 131 | 2.20313, 132 | 2.19531, 133 | 2.17969, 134 | 2.16406, 135 | 2.15625, 136 | 2.14063, 137 | 2.125, 138 | 2.11719, 139 | 2.10156, 140 | 2.08594, 141 | 2.07813, 142 | 2.0625, 143 | 2.04688, 144 | 2.03906, 145 | 2.02344, 146 | 2.00781, 147 | 2, 148 | 1.98438, 149 | 1.96875, 150 | 1.96094, 151 | 1.94531, 152 | 1.92969, 153 | 1.92188, 154 | 1.90625, 155 | 1.89063, 156 | 1.88281, 157 | 1.86719, 158 | 1.85156, 159 | 1.84375, 160 | 1.82813, 161 | 1.8125, 162 | 1.80469, 163 | 1.78906, 164 | 1.77344, 165 | 1.76563, 166 | 1.75, 167 | 1.73438, 168 | 1.72656, 169 | 1.71094, 170 | 1.69531, 171 | 1.6875, 172 | 1.67188, 173 | 1.65625, 174 | 1.64844, 175 | 1.63281, 176 | 1.61719, 177 | 1.60938, 178 | 1.59375, 179 | 1.57813, 180 | 1.57031, 181 | 1.55469, 182 | 1.53906, 183 | 1.53125, 184 | 1.51563, 185 | 1.5, 186 | 1.49219, 187 | 1.47656, 188 | 1.46094, 189 | 1.45313, 190 | 1.4375, 191 | 1.42188, 192 | 1.41406, 193 | 1.39844, 194 | 1.38281, 195 | 1.375, 196 | 1.35938, 197 | 1.34375, 198 | 1.33594, 199 | 1.32031, 200 | 1.30469, 201 | 1.29688, 202 | 1.28125, 203 | 1.26563, 204 | 1.25781, 205 | 1.24219, 206 | 1.22656, 207 | 1.21875, 208 | 1.20313, 209 | 1.1875, 210 | 1.17969, 211 | 1.16406, 212 | 1.14844, 213 | 1.14063, 214 | 1.125, 215 | 1.10938, 216 | 1.10156, 217 | 1.08594, 218 | 1.07031, 219 | 1.0625, 220 | 1.04688, 221 | 1.03125, 222 | 1.02344, 223 | 1.00781, 224 | 0.99219, 225 | 0.98438, 226 | 0.96875, 227 | 0.95313, 228 | 0.94531, 229 | 0.92969, 230 | 0.91406, 231 | 0.90625, 232 | 0.89063, 233 | 0.875, 234 | 0.86719, 235 | 0.85156, 236 | 0.83594, 237 | 0.82813, 238 | 0.8125, 239 | 0.79688, 240 | 0.78906, 241 | 0.77344, 242 | 0.75781, 243 | 0.75, 244 | 0.73438, 245 | 0.71875, 246 | 0.71094, 247 | 0.69531, 248 | 0.67969, 249 | 0.67188, 250 | 0.65625, 251 | 0.64063, 252 | 0.63281, 253 | 0.61719, 254 | 0.60156, 255 | 0.59375, 256 | 0.57813, 257 | 0.5625, 258 | 0.55469, 259 | 0.53906, 260 | 0.52344, 261 | 0.51563, 262 | 0.5, 263 | 0.48438, 264 | 0.47656, 265 | 0.46094, 266 | 0.44531, 267 | 0.4375, 268 | 0.42188, 269 | 0.40625, 270 | 0.39844, 271 | 0.38281, 272 | 0.36719, 273 | 0.35938, 274 | 0.34375, 275 | 0.32813, 276 | 0.32031, 277 | 0.30469, 278 | 0.28906, 279 | 0.28125, 280 | 0.26563, 281 | 0.25, 282 | 0.24219, 283 | 0.21875, 284 | 0.21094, 285 | 0.20313, 286 | 0.1875, 287 | 0.17188, 288 | 0.16406, 289 | 0.14844, 290 | 0.13281, 291 | 0.125, 292 | 0.10938, 293 | 0.09375, 294 | 0.08594, 295 | 0.07031, 296 | 0.05469, 297 | 0.04688, 298 | 0.03125, 299 | 0.01563, 300 | 0, 301 | 0 ], 302 | "ImageOrientationPatientDICOM": [ 303 | 1, 304 | 0, 305 | 0, 306 | 0, 307 | 1, 308 | 0 ], 309 | "ConversionSoftware": "dcm2niix", 310 | "ConversionSoftwareVersion": "v1.0.20190902" 311 | } 312 | -------------------------------------------------------------------------------- /nnunet/training/loss_functions/dice_loss.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import torch 15 | from nnunet.training.loss_functions.TopK_loss import TopKLoss 16 | from nnunet.utilities.nd_softmax import softmax_helper 17 | from nnunet.training.loss_functions.ND_Crossentropy import CrossentropyND 18 | from nnunet.utilities.tensor_utilities import sum_tensor 19 | from torch import nn 20 | 21 | 22 | def get_tp_fp_fn(net_output, gt, axes=None, mask=None, square=False): 23 | """ 24 | net_output must be (b, c, x, y(, z))) 25 | gt must be a label map (shape (b, 1, x, y(, z)) OR shape (b, x, y(, z))) or one hot encoding (b, c, x, y(, z)) 26 | if mask is provided it must have shape (b, 1, x, y(, z))) 27 | :param net_output: 28 | :param gt: 29 | :param axes: 30 | :param mask: mask must be 1 for valid pixels and 0 for invalid pixels 31 | :param square: if True then fp, tp and fn will be squared before summation 32 | :return: 33 | """ 34 | if axes is None: 35 | axes = tuple(range(2, len(net_output.size()))) 36 | 37 | shp_x = net_output.shape 38 | shp_y = gt.shape 39 | 40 | with torch.no_grad(): 41 | if len(shp_x) != len(shp_y): 42 | gt = gt.view((shp_y[0], 1, *shp_y[1:])) 43 | 44 | if all([i == j for i, j in zip(net_output.shape, gt.shape)]): 45 | # if this is the case then gt is probably already a one hot encoding 46 | y_onehot = gt 47 | else: 48 | gt = gt.long() 49 | y_onehot = torch.zeros(shp_x) 50 | if net_output.device.type == "cuda": 51 | y_onehot = y_onehot.cuda(net_output.device.index) 52 | y_onehot.scatter_(1, gt, 1) 53 | 54 | tp = net_output * y_onehot 55 | fp = net_output * (1 - y_onehot) 56 | fn = (1 - net_output) * y_onehot 57 | 58 | if mask is not None: 59 | tp = torch.stack(tuple(x_i * mask[:, 0] for x_i in torch.unbind(tp, dim=1)), dim=1) 60 | fp = torch.stack(tuple(x_i * mask[:, 0] for x_i in torch.unbind(fp, dim=1)), dim=1) 61 | fn = torch.stack(tuple(x_i * mask[:, 0] for x_i in torch.unbind(fn, dim=1)), dim=1) 62 | 63 | if square: 64 | tp = tp ** 2 65 | fp = fp ** 2 66 | fn = fn ** 2 67 | 68 | tp = sum_tensor(tp, axes, keepdim=False) 69 | fp = sum_tensor(fp, axes, keepdim=False) 70 | fn = sum_tensor(fn, axes, keepdim=False) 71 | 72 | return tp, fp, fn 73 | 74 | 75 | class SoftDiceLoss(nn.Module): 76 | def __init__(self, apply_nonlin=None, batch_dice=False, do_bg=True, smooth=1., 77 | square=False): 78 | """ 79 | 80 | """ 81 | super(SoftDiceLoss, self).__init__() 82 | 83 | self.square = square 84 | self.do_bg = do_bg 85 | self.batch_dice = batch_dice 86 | self.apply_nonlin = apply_nonlin 87 | self.smooth = smooth 88 | 89 | def forward(self, x, y, loss_mask=None): 90 | shp_x = x.shape 91 | 92 | if self.batch_dice: 93 | axes = [0] + list(range(2, len(shp_x))) 94 | else: 95 | axes = list(range(2, len(shp_x))) 96 | 97 | if self.apply_nonlin is not None: 98 | x = self.apply_nonlin(x) 99 | 100 | tp, fp, fn = get_tp_fp_fn(x, y, axes, loss_mask, self.square) 101 | 102 | dc = (2 * tp + self.smooth) / (2 * tp + fp + fn + self.smooth) 103 | 104 | if not self.do_bg: 105 | if self.batch_dice: 106 | dc = dc[1:] 107 | else: 108 | dc = dc[:, 1:] 109 | dc = dc.mean() 110 | 111 | return -dc 112 | 113 | 114 | class DC_and_CE_loss(nn.Module): 115 | def __init__(self, soft_dice_kwargs, ce_kwargs, aggregate="sum"): 116 | super(DC_and_CE_loss, self).__init__() 117 | self.aggregate = aggregate 118 | self.ce = CrossentropyND(**ce_kwargs) 119 | self.dc = SoftDiceLoss(apply_nonlin=softmax_helper, **soft_dice_kwargs) 120 | 121 | def forward(self, net_output, target): 122 | dc_loss = self.dc(net_output, target) 123 | ce_loss = self.ce(net_output, target) 124 | if self.aggregate == "sum": 125 | result = ce_loss + dc_loss 126 | else: 127 | raise NotImplementedError("nah son") # reserved for other stuff (later) 128 | return result 129 | 130 | 131 | class DC_and_topk_loss(nn.Module): 132 | def __init__(self, soft_dice_kwargs, ce_kwargs, aggregate="sum"): 133 | super(DC_and_topk_loss, self).__init__() 134 | self.aggregate = aggregate 135 | self.ce = TopKLoss(**ce_kwargs) 136 | self.dc = SoftDiceLoss(apply_nonlin=softmax_helper, **soft_dice_kwargs) 137 | 138 | def forward(self, net_output, target): 139 | dc_loss = self.dc(net_output, target) 140 | ce_loss = self.ce(net_output, target) 141 | if self.aggregate == "sum": 142 | result = ce_loss + dc_loss 143 | else: 144 | raise NotImplementedError("nah son") # reserved for other stuff (later?) 145 | return result 146 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2725093.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "Siemens", 4 | "ManufacturersModelName": "SOMATOM_Definition_AS+", 5 | "InstitutionName": "SH_RENJI_HOSPITAL", 6 | "InstitutionAddress": "Dongfang_Rd_5D7881_PuDong_new_Dist_CN", 7 | "DeviceSerialNumber": "64135", 8 | "StationName": "CTAWP64135", 9 | "BodyPartExamined": "ABDOMEN", 10 | "PatientPosition": "FFS", 11 | "SoftwareVersions": "syngo_CT_2012B", 12 | "SeriesDescription": "Venous_Phase_1.0_B31f", 13 | "ProtocolName": "5_Abd3_Bolus", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "CT", "SOM5", "SPI"], 15 | "SeriesNumber": 9, 16 | "AcquisitionTime": "11:20:53.611000", 17 | "AcquisitionNumber": 12, 18 | "XRayExposure": 300, 19 | "ReconMatrixPE": 512, 20 | "SliceTiming": [ 21 | 3.9375, 22 | 3.92969, 23 | 3.91406, 24 | 3.89844, 25 | 3.89063, 26 | 3.875, 27 | 3.85938, 28 | 3.85156, 29 | 3.83594, 30 | 3.82031, 31 | 3.8125, 32 | 3.79688, 33 | 3.78125, 34 | 3.77344, 35 | 3.75781, 36 | 3.74219, 37 | 3.73438, 38 | 3.71875, 39 | 3.70313, 40 | 3.69531, 41 | 3.67969, 42 | 3.66406, 43 | 3.65625, 44 | 3.64063, 45 | 3.625, 46 | 3.61719, 47 | 3.60156, 48 | 3.58594, 49 | 3.57813, 50 | 3.5625, 51 | 3.54688, 52 | 3.53906, 53 | 3.52344, 54 | 3.50781, 55 | 3.5, 56 | 3.48438, 57 | 3.46875, 58 | 3.46094, 59 | 3.44531, 60 | 3.42969, 61 | 3.42188, 62 | 3.40625, 63 | 3.39063, 64 | 3.38281, 65 | 3.36719, 66 | 3.35156, 67 | 3.34375, 68 | 3.32813, 69 | 3.3125, 70 | 3.30469, 71 | 3.28906, 72 | 3.27344, 73 | 3.26563, 74 | 3.25, 75 | 3.23438, 76 | 3.22656, 77 | 3.21094, 78 | 3.19531, 79 | 3.1875, 80 | 3.17188, 81 | 3.15625, 82 | 3.14844, 83 | 3.13281, 84 | 3.11719, 85 | 3.10938, 86 | 3.09375, 87 | 3.07813, 88 | 3.07031, 89 | 3.05469, 90 | 3.03906, 91 | 3.03125, 92 | 3.01563, 93 | 3, 94 | 2.99219, 95 | 2.97656, 96 | 2.96094, 97 | 2.95313, 98 | 2.9375, 99 | 2.92188, 100 | 2.91406, 101 | 2.89844, 102 | 2.88281, 103 | 2.875, 104 | 2.85938, 105 | 2.84375, 106 | 2.83594, 107 | 2.82031, 108 | 2.80469, 109 | 2.79688, 110 | 2.78125, 111 | 2.76563, 112 | 2.75781, 113 | 2.74219, 114 | 2.72656, 115 | 2.71875, 116 | 2.70313, 117 | 2.6875, 118 | 2.67969, 119 | 2.66406, 120 | 2.64844, 121 | 2.64063, 122 | 2.625, 123 | 2.60938, 124 | 2.60156, 125 | 2.58594, 126 | 2.57031, 127 | 2.5625, 128 | 2.54688, 129 | 2.53125, 130 | 2.52344, 131 | 2.50781, 132 | 2.49219, 133 | 2.48438, 134 | 2.46875, 135 | 2.45313, 136 | 2.44531, 137 | 2.42969, 138 | 2.41406, 139 | 2.40625, 140 | 2.39063, 141 | 2.375, 142 | 2.36719, 143 | 2.35156, 144 | 2.33594, 145 | 2.32813, 146 | 2.3125, 147 | 2.29688, 148 | 2.28906, 149 | 2.27344, 150 | 2.25781, 151 | 2.25, 152 | 2.23438, 153 | 2.21875, 154 | 2.21094, 155 | 2.19531, 156 | 2.17969, 157 | 2.17188, 158 | 2.15625, 159 | 2.14063, 160 | 2.13281, 161 | 2.11719, 162 | 2.10156, 163 | 2.09375, 164 | 2.07813, 165 | 2.0625, 166 | 2.05469, 167 | 2.03906, 168 | 2.02344, 169 | 2.01563, 170 | 2, 171 | 1.98438, 172 | 1.97656, 173 | 1.96094, 174 | 1.94531, 175 | 1.9375, 176 | 1.92188, 177 | 1.90625, 178 | 1.89844, 179 | 1.88281, 180 | 1.86719, 181 | 1.85938, 182 | 1.84375, 183 | 1.82813, 184 | 1.82031, 185 | 1.80469, 186 | 1.78906, 187 | 1.78125, 188 | 1.76563, 189 | 1.75, 190 | 1.74219, 191 | 1.72656, 192 | 1.71094, 193 | 1.70313, 194 | 1.6875, 195 | 1.67188, 196 | 1.66406, 197 | 1.64844, 198 | 1.63281, 199 | 1.625, 200 | 1.60938, 201 | 1.59375, 202 | 1.58594, 203 | 1.57031, 204 | 1.55469, 205 | 1.54688, 206 | 1.53125, 207 | 1.51563, 208 | 1.50781, 209 | 1.49219, 210 | 1.47656, 211 | 1.46875, 212 | 1.45313, 213 | 1.4375, 214 | 1.42969, 215 | 1.41406, 216 | 1.39844, 217 | 1.39063, 218 | 1.375, 219 | 1.35938, 220 | 1.35156, 221 | 1.33594, 222 | 1.32031, 223 | 1.3125, 224 | 1.29688, 225 | 1.28125, 226 | 1.27344, 227 | 1.25781, 228 | 1.24219, 229 | 1.23438, 230 | 1.21875, 231 | 1.20313, 232 | 1.19531, 233 | 1.17969, 234 | 1.16406, 235 | 1.15625, 236 | 1.14063, 237 | 1.125, 238 | 1.11719, 239 | 1.10156, 240 | 1.08594, 241 | 1.07813, 242 | 1.0625, 243 | 1.04688, 244 | 1.03906, 245 | 1.02344, 246 | 1.00781, 247 | 1, 248 | 0.98438, 249 | 0.96875, 250 | 0.96094, 251 | 0.94531, 252 | 0.92969, 253 | 0.92188, 254 | 0.90625, 255 | 0.89063, 256 | 0.88281, 257 | 0.86719, 258 | 0.85156, 259 | 0.84375, 260 | 0.82813, 261 | 0.8125, 262 | 0.80469, 263 | 0.78906, 264 | 0.77344, 265 | 0.76563, 266 | 0.75, 267 | 0.73438, 268 | 0.72656, 269 | 0.71094, 270 | 0.69531, 271 | 0.6875, 272 | 0.67188, 273 | 0.65625, 274 | 0.64844, 275 | 0.63281, 276 | 0.61719, 277 | 0.60938, 278 | 0.59375, 279 | 0.57813, 280 | 0.57031, 281 | 0.55469, 282 | 0.53906, 283 | 0.53125, 284 | 0.51563, 285 | 0.5, 286 | 0.49219, 287 | 0.47656, 288 | 0.46094, 289 | 0.45313, 290 | 0.4375, 291 | 0.42188, 292 | 0.41406, 293 | 0.39844, 294 | 0.38281, 295 | 0.375, 296 | 0.35938, 297 | 0.34375, 298 | 0.33594, 299 | 0.32031, 300 | 0.30469, 301 | 0.29688, 302 | 0.28125, 303 | 0.26563, 304 | 0.25781, 305 | 0.24219, 306 | 0.22656, 307 | 0.21875, 308 | 0.20313, 309 | 0.1875, 310 | 0.17969, 311 | 0.16406, 312 | 0.14844, 313 | 0.14063, 314 | 0.125, 315 | 0.10938, 316 | 0.10156, 317 | 0.08594, 318 | 0.07031, 319 | 0.0625, 320 | 0.04688, 321 | 0.03125, 322 | 0.02344, 323 | 0.00781, 324 | 0 ], 325 | "ImageOrientationPatientDICOM": [ 326 | 1, 327 | 0, 328 | 0, 329 | 0, 330 | 1, 331 | 0 ], 332 | "ConversionSoftware": "dcm2niix", 333 | "ConversionSoftwareVersion": "v1.0.20190902" 334 | } 335 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2727814.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "Siemens", 4 | "ManufacturersModelName": "SOMATOM_Definition_AS+", 5 | "InstitutionName": "SH_RENJI_HOSPITAL", 6 | "InstitutionAddress": "Dongfang_Rd_5D7881_PuDong_new_Dist_CN", 7 | "DeviceSerialNumber": "64135", 8 | "StationName": "CTAWP64135", 9 | "BodyPartExamined": "ABDOMEN", 10 | "PatientPosition": "FFS", 11 | "SoftwareVersions": "syngo_CT_2012B", 12 | "SeriesDescription": "Venous_Phase_1.0_B31f", 13 | "ProtocolName": "2_Abd2_Phase", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "CT", "SOM5", "SPI"], 15 | "SeriesNumber": 9, 16 | "AcquisitionTime": "11:23:16.059000", 17 | "AcquisitionNumber": 14, 18 | "XRayExposure": 300, 19 | "ReconMatrixPE": 512, 20 | "SliceTiming": [ 21 | 4, 22 | 3.98438, 23 | 3.96875, 24 | 3.96094, 25 | 3.94531, 26 | 3.92969, 27 | 3.91406, 28 | 3.90625, 29 | 3.89063, 30 | 3.875, 31 | 3.86719, 32 | 3.85156, 33 | 3.83594, 34 | 3.82813, 35 | 3.8125, 36 | 3.79688, 37 | 3.78906, 38 | 3.77344, 39 | 3.75781, 40 | 3.75, 41 | 3.73438, 42 | 3.71875, 43 | 3.71094, 44 | 3.69531, 45 | 3.67969, 46 | 3.67188, 47 | 3.65625, 48 | 3.64063, 49 | 3.625, 50 | 3.61719, 51 | 3.60938, 52 | 3.58594, 53 | 3.57813, 54 | 3.57031, 55 | 3.54688, 56 | 3.53906, 57 | 3.53125, 58 | 3.50781, 59 | 3.5, 60 | 3.48438, 61 | 3.46875, 62 | 3.46094, 63 | 3.44531, 64 | 3.42969, 65 | 3.42188, 66 | 3.40625, 67 | 3.39063, 68 | 3.38281, 69 | 3.36719, 70 | 3.35938, 71 | 3.34375, 72 | 3.32813, 73 | 3.32031, 74 | 3.30469, 75 | 3.28906, 76 | 3.28125, 77 | 3.26563, 78 | 3.25, 79 | 3.24219, 80 | 3.22656, 81 | 3.21094, 82 | 3.20313, 83 | 3.1875, 84 | 3.17188, 85 | 3.15625, 86 | 3.14844, 87 | 3.13281, 88 | 3.11719, 89 | 3.10938, 90 | 3.09375, 91 | 3.07813, 92 | 3.07031, 93 | 3.05469, 94 | 3.03906, 95 | 3.03125, 96 | 3.01563, 97 | 3, 98 | 2.99219, 99 | 2.98438, 100 | 2.96094, 101 | 2.95313, 102 | 2.94531, 103 | 2.92969, 104 | 2.91406, 105 | 2.90625, 106 | 2.89063, 107 | 2.875, 108 | 2.86719, 109 | 2.84375, 110 | 2.83594, 111 | 2.82813, 112 | 2.80469, 113 | 2.79688, 114 | 2.78906, 115 | 2.77344, 116 | 2.75781, 117 | 2.75, 118 | 2.73438, 119 | 2.71875, 120 | 2.71094, 121 | 2.69531, 122 | 2.67969, 123 | 2.67188, 124 | 2.65625, 125 | 2.64063, 126 | 2.63281, 127 | 2.61719, 128 | 2.60156, 129 | 2.59375, 130 | 2.57813, 131 | 2.5625, 132 | 2.55469, 133 | 2.53906, 134 | 2.52344, 135 | 2.51563, 136 | 2.5, 137 | 2.48438, 138 | 2.47656, 139 | 2.46094, 140 | 2.44531, 141 | 2.4375, 142 | 2.42188, 143 | 2.40625, 144 | 2.39063, 145 | 2.38281, 146 | 2.36719, 147 | 2.35938, 148 | 2.34375, 149 | 2.32813, 150 | 2.32031, 151 | 2.29688, 152 | 2.28906, 153 | 2.28125, 154 | 2.25781, 155 | 2.25, 156 | 2.24219, 157 | 2.21875, 158 | 2.21094, 159 | 2.20313, 160 | 2.17969, 161 | 2.17188, 162 | 2.16406, 163 | 2.14063, 164 | 2.13281, 165 | 2.11719, 166 | 2.10156, 167 | 2.09375, 168 | 2.07813, 169 | 2.0625, 170 | 2.05469, 171 | 2.03906, 172 | 2.02344, 173 | 2.01563, 174 | 2, 175 | 1.98438, 176 | 1.97656, 177 | 1.96094, 178 | 1.94531, 179 | 1.9375, 180 | 1.92188, 181 | 1.90625, 182 | 1.89844, 183 | 1.88281, 184 | 1.86719, 185 | 1.85938, 186 | 1.84375, 187 | 1.82813, 188 | 1.82031, 189 | 1.80469, 190 | 1.78906, 191 | 1.78125, 192 | 1.76563, 193 | 1.75, 194 | 1.74219, 195 | 1.72656, 196 | 1.71094, 197 | 1.70313, 198 | 1.6875, 199 | 1.67188, 200 | 1.66406, 201 | 1.64844, 202 | 1.63281, 203 | 1.625, 204 | 1.60938, 205 | 1.59375, 206 | 1.58594, 207 | 1.57813, 208 | 1.55469, 209 | 1.54688, 210 | 1.53906, 211 | 1.51563, 212 | 1.50781, 213 | 1.5, 214 | 1.48438, 215 | 1.46875, 216 | 1.46094, 217 | 1.44531, 218 | 1.42969, 219 | 1.42188, 220 | 1.40625, 221 | 1.39063, 222 | 1.38281, 223 | 1.36719, 224 | 1.35156, 225 | 1.34375, 226 | 1.32813, 227 | 1.3125, 228 | 1.30469, 229 | 1.28906, 230 | 1.27344, 231 | 1.26563, 232 | 1.25, 233 | 1.23438, 234 | 1.22656, 235 | 1.21094, 236 | 1.19531, 237 | 1.1875, 238 | 1.17188, 239 | 1.15625, 240 | 1.14844, 241 | 1.13281, 242 | 1.11719, 243 | 1.10938, 244 | 1.09375, 245 | 1.07813, 246 | 1.07031, 247 | 1.05469, 248 | 1.03906, 249 | 1.03125, 250 | 1.01563, 251 | 1, 252 | 0.99219, 253 | 0.97656, 254 | 0.96094, 255 | 0.95313, 256 | 0.9375, 257 | 0.92188, 258 | 0.91406, 259 | 0.89844, 260 | 0.88281, 261 | 0.875, 262 | 0.85938, 263 | 0.84375, 264 | 0.83594, 265 | 0.82031, 266 | 0.80469, 267 | 0.79688, 268 | 0.78125, 269 | 0.76563, 270 | 0.75781, 271 | 0.74219, 272 | 0.72656, 273 | 0.71094, 274 | 0.70313, 275 | 0.6875, 276 | 0.67188, 277 | 0.65625, 278 | 0.64844, 279 | 0.63281, 280 | 0.61719, 281 | 0.60938, 282 | 0.59375, 283 | 0.57813, 284 | 0.57031, 285 | 0.55469, 286 | 0.53906, 287 | 0.53125, 288 | 0.51563, 289 | 0.5, 290 | 0.49219, 291 | 0.47656, 292 | 0.46094, 293 | 0.45313, 294 | 0.4375, 295 | 0.42188, 296 | 0.41406, 297 | 0.39844, 298 | 0.38281, 299 | 0.375, 300 | 0.35938, 301 | 0.34375, 302 | 0.33594, 303 | 0.32031, 304 | 0.30469, 305 | 0.29688, 306 | 0.28125, 307 | 0.26563, 308 | 0.25781, 309 | 0.24219, 310 | 0.22656, 311 | 0.21875, 312 | 0.20313, 313 | 0.1875, 314 | 0.17969, 315 | 0.16406, 316 | 0.14844, 317 | 0.14063, 318 | 0.125, 319 | 0.10938, 320 | 0.10156, 321 | 0.08594, 322 | 0.07031, 323 | 0.0625, 324 | 0.04688, 325 | 0.03125, 326 | 0.02344, 327 | 0.00781, 328 | 0 ], 329 | "ImageOrientationPatientDICOM": [ 330 | 1, 331 | 0, 332 | 0, 333 | 0, 334 | 1, 335 | 0 ], 336 | "ConversionSoftware": "dcm2niix", 337 | "ConversionSoftwareVersion": "v1.0.20190902" 338 | } 339 | -------------------------------------------------------------------------------- /nnunet/evaluation/model_selection/figure_out_what_to_submit.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import nnunet 16 | from batchgenerators.utilities.file_and_folder_operations import * 17 | from nnunet.paths import network_training_output_dir 18 | import numpy as np 19 | from nnunet.evaluation.add_mean_dice_to_json import foreground_mean 20 | from subprocess import call 21 | import SimpleITK as sitk 22 | from nnunet.run.default_configuration import get_output_folder 23 | 24 | 25 | def copy_nifti_and_convert_to_uint8(args): 26 | source_file, target_file = args 27 | i = sitk.ReadImage(source_file) 28 | j = sitk.GetImageFromArray(sitk.GetArrayFromImage(i).astype(np.uint8)) 29 | j.CopyInformation(i) 30 | sitk.WriteImage(j, target_file) 31 | 32 | 33 | if __name__ == "__main__": 34 | # This script was hacked together at some point and is ugly af. TODO this needs to be redone properly 35 | import argparse 36 | parser = argparse.ArgumentParser(usage="This is intended to identify the best model based on the five fold " 37 | "cross-validation. Running this script requires alle models to have been run " 38 | "already. This script will summarize the results of the five folds of all " 39 | "models in one json each for easy interpretability") 40 | parser.add_argument("-m", '--models', nargs="+", required=False, default=['2d', '3d_lowres', '3d_fullres', '3d_cascade_fullres']) 41 | parser.add_argument("-t", '--task_ids', nargs="+", required=False, default=list(range(100))) 42 | 43 | args = parser.parse_args() 44 | tasks = args.task_ids 45 | models = args.models 46 | 47 | out_dir_all_json = join(network_training_output_dir, "summary_jsons") 48 | 49 | json_files = [i for i in subfiles(out_dir_all_json, suffix=".json", join=True) if i.find("ensemble") == -1] 50 | 51 | # do mean over foreground 52 | for j in json_files: 53 | foreground_mean(j) 54 | 55 | # for each task, run ensembling using all combinations of two models 56 | for t in tasks: 57 | t = int(t) 58 | json_files_task = [i for i in subfiles(out_dir_all_json, prefix="Task%02.0d_" % t) if i.find("ensemble") == -1] 59 | if len(json_files_task) > 0: 60 | task_name = json_files_task[0].split("/")[-1].split("__")[0] 61 | print(task_name) 62 | 63 | for i in range(len(json_files_task) - 1): 64 | for j in range(i+1, len(json_files_task)): 65 | # networks are stored as 66 | # task__configuration__trainer__plans 67 | network1 = json_files_task[i].split("/")[-1].split("__") 68 | network1[-1] = network1[-1].split(".")[0] 69 | task, configuration, trainer, plans_identifier, _ = network1 70 | network1_folder = get_output_folder(configuration, task, trainer, plans_identifier) 71 | name1 = configuration + "__" + trainer + "__" + plans_identifier 72 | 73 | network2 = json_files_task[j].split("/")[-1].split("__") 74 | network2[-1] = network2[-1].split(".")[0] 75 | task, configuration, trainer, plans_identifier, _ = network2 76 | network2_folder = get_output_folder(configuration, task, trainer, plans_identifier) 77 | name2 = configuration + "__" + trainer + "__" + plans_identifier 78 | 79 | if np.argsort((name1, name2))[0] == 1: 80 | name1, name2 = name2, name1 81 | network1_folder, network2_folder = network2_folder, network1_folder 82 | 83 | output_folder = join(network_training_output_dir, "ensembles", task_name, "ensemble_" + name1 + "--" + name2) 84 | # now ensemble 85 | print(network1_folder, network2_folder) 86 | p = call(["python", join(nnunet.__path__[0], "evaluation/model_selection/ensemble.py"), network1_folder, network2_folder, output_folder, task_name]) 87 | 88 | # now rerun adding the mean foreground dice 89 | json_files = subfiles(out_dir_all_json, suffix=".json", join=True) 90 | 91 | # do mean over foreground 92 | for j in json_files: 93 | foreground_mean(j) 94 | 95 | # now load all json for each task and find best 96 | with open(join(network_training_output_dir, "use_this_for_test.csv"), 'w') as f: 97 | for t in tasks: 98 | t = int(t) 99 | json_files_task = subfiles(out_dir_all_json, prefix="Task%02.0d_" % t) 100 | if len(json_files_task) > 0: 101 | task_name = json_files_task[0].split("/")[-1].split("__")[0] 102 | print(task_name) 103 | mean_dice = [] 104 | for j in json_files_task: 105 | js = load_json(j) 106 | mean_dice.append(js['results']['mean']['mean']['Dice']) 107 | best = np.argsort(mean_dice)[::-1][0] 108 | j = json_files_task[best].split("/")[-1] 109 | 110 | print("%s: submit model %s" % (task_name, j)) 111 | f.write("%s,%s\n" % (task_name, j)) 112 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2726620.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "Siemens", 4 | "ManufacturersModelName": "SOMATOM_Definition_AS+", 5 | "InstitutionName": "SH_RENJI_HOSPITAL", 6 | "InstitutionAddress": "Dongfang_Rd_5D7881_PuDong_new_Dist_CN", 7 | "DeviceSerialNumber": "64135", 8 | "StationName": "CTAWP64135", 9 | "BodyPartExamined": "ABDOMEN", 10 | "PatientPosition": "FFS", 11 | "SoftwareVersions": "syngo_CT_2012B", 12 | "SeriesDescription": "Delay_Phase_1.0_B31f", 13 | "ProtocolName": "5_Abd3_Bolus", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "CT", "SOM5", "SPI"], 15 | "SeriesNumber": 11, 16 | "AcquisitionTime": "09:58:48.450000", 17 | "AcquisitionNumber": 14, 18 | "XRayExposure": 300, 19 | "ReconMatrixPE": 512, 20 | "SliceTiming": [ 21 | 6.61719, 22 | 6.59375, 23 | 6.57813, 24 | 6.54688, 25 | 6.53125, 26 | 6.50781, 27 | 6.48438, 28 | 6.46094, 29 | 6.44531, 30 | 6.42188, 31 | 6.39844, 32 | 6.38281, 33 | 6.35156, 34 | 6.33594, 35 | 6.3125, 36 | 6.28906, 37 | 6.26563, 38 | 6.25, 39 | 6.22656, 40 | 6.20313, 41 | 6.17969, 42 | 6.15625, 43 | 6.14063, 44 | 6.11719, 45 | 6.09375, 46 | 6.07031, 47 | 6.05469, 48 | 6.02344, 49 | 6.00781, 50 | 5.98438, 51 | 5.96094, 52 | 5.94531, 53 | 5.92188, 54 | 5.89844, 55 | 5.875, 56 | 5.85938, 57 | 5.82813, 58 | 5.8125, 59 | 5.78906, 60 | 5.76563, 61 | 5.74219, 62 | 5.72656, 63 | 5.70313, 64 | 5.67969, 65 | 5.66406, 66 | 5.63281, 67 | 5.61719, 68 | 5.59375, 69 | 5.57031, 70 | 5.54688, 71 | 5.53125, 72 | 5.50781, 73 | 5.48438, 74 | 5.46875, 75 | 5.4375, 76 | 5.42188, 77 | 5.39844, 78 | 5.375, 79 | 5.35156, 80 | 5.33594, 81 | 5.3125, 82 | 5.28906, 83 | 5.27344, 84 | 5.24219, 85 | 5.22656, 86 | 5.20313, 87 | 5.17969, 88 | 5.15625, 89 | 5.14063, 90 | 5.10938, 91 | 5.09375, 92 | 5.07031, 93 | 5.04688, 94 | 5.03125, 95 | 5.00781, 96 | 4.98438, 97 | 4.96094, 98 | 4.94531, 99 | 4.91406, 100 | 4.89844, 101 | 4.875, 102 | 4.85156, 103 | 4.83594, 104 | 4.8125, 105 | 4.78906, 106 | 4.76563, 107 | 4.75, 108 | 4.71875, 109 | 4.70313, 110 | 4.67969, 111 | 4.65625, 112 | 4.64063, 113 | 4.61719, 114 | 4.59375, 115 | 4.57031, 116 | 4.55469, 117 | 4.52344, 118 | 4.50781, 119 | 4.48438, 120 | 4.46094, 121 | 4.4375, 122 | 4.42188, 123 | 4.39844, 124 | 4.375, 125 | 4.35938, 126 | 4.32813, 127 | 4.3125, 128 | 4.28906, 129 | 4.26563, 130 | 4.24219, 131 | 4.22656, 132 | 4.20313, 133 | 4.17969, 134 | 4.16406, 135 | 4.13281, 136 | 4.11719, 137 | 4.09375, 138 | 4.07031, 139 | 4.05469, 140 | 4.03125, 141 | 4.00781, 142 | 3.98438, 143 | 3.96875, 144 | 3.9375, 145 | 3.92188, 146 | 3.89844, 147 | 3.875, 148 | 3.85156, 149 | 3.83594, 150 | 3.8125, 151 | 3.78906, 152 | 3.77344, 153 | 3.74219, 154 | 3.72656, 155 | 3.70313, 156 | 3.67969, 157 | 3.65625, 158 | 3.64063, 159 | 3.61719, 160 | 3.59375, 161 | 3.57813, 162 | 3.54688, 163 | 3.53125, 164 | 3.50781, 165 | 3.48438, 166 | 3.46094, 167 | 3.44531, 168 | 3.42188, 169 | 3.39844, 170 | 3.38281, 171 | 3.35156, 172 | 3.33594, 173 | 3.3125, 174 | 3.28906, 175 | 3.26563, 176 | 3.25, 177 | 3.22656, 178 | 3.20313, 179 | 3.1875, 180 | 3.15625, 181 | 3.14063, 182 | 3.11719, 183 | 3.09375, 184 | 3.07031, 185 | 3.05469, 186 | 3.02344, 187 | 3.00781, 188 | 2.98438, 189 | 2.96094, 190 | 2.94531, 191 | 2.92188, 192 | 2.89844, 193 | 2.875, 194 | 2.85938, 195 | 2.82813, 196 | 2.8125, 197 | 2.78906, 198 | 2.76563, 199 | 2.74219, 200 | 2.72656, 201 | 2.70313, 202 | 2.67969, 203 | 2.65625, 204 | 2.63281, 205 | 2.61719, 206 | 2.59375, 207 | 2.57031, 208 | 2.54688, 209 | 2.53125, 210 | 2.50781, 211 | 2.48438, 212 | 2.46875, 213 | 2.4375, 214 | 2.42188, 215 | 2.39844, 216 | 2.375, 217 | 2.35156, 218 | 2.33594, 219 | 2.30469, 220 | 2.28906, 221 | 2.26563, 222 | 2.24219, 223 | 2.22656, 224 | 2.20313, 225 | 2.17969, 226 | 2.15625, 227 | 2.14063, 228 | 2.10938, 229 | 2.09375, 230 | 2.07031, 231 | 2.04688, 232 | 2.03125, 233 | 2.00781, 234 | 1.98438, 235 | 1.96094, 236 | 1.94531, 237 | 1.91406, 238 | 1.89844, 239 | 1.875, 240 | 1.85156, 241 | 1.83594, 242 | 1.8125, 243 | 1.78906, 244 | 1.76563, 245 | 1.75, 246 | 1.71875, 247 | 1.70313, 248 | 1.67969, 249 | 1.65625, 250 | 1.64063, 251 | 1.61719, 252 | 1.59375, 253 | 1.57031, 254 | 1.55469, 255 | 1.52344, 256 | 1.50781, 257 | 1.48438, 258 | 1.46094, 259 | 1.44531, 260 | 1.42188, 261 | 1.39844, 262 | 1.375, 263 | 1.35938, 264 | 1.32813, 265 | 1.3125, 266 | 1.28906, 267 | 1.26563, 268 | 1.24219, 269 | 1.22656, 270 | 1.20313, 271 | 1.17969, 272 | 1.16406, 273 | 1.13281, 274 | 1.11719, 275 | 1.09375, 276 | 1.07031, 277 | 1.05469, 278 | 1.03125, 279 | 1.00781, 280 | 0.98438, 281 | 0.96875, 282 | 0.9375, 283 | 0.92188, 284 | 0.89844, 285 | 0.875, 286 | 0.85156, 287 | 0.83594, 288 | 0.8125, 289 | 0.78906, 290 | 0.77344, 291 | 0.74219, 292 | 0.72656, 293 | 0.70313, 294 | 0.67969, 295 | 0.65625, 296 | 0.64063, 297 | 0.61719, 298 | 0.59375, 299 | 0.57813, 300 | 0.54688, 301 | 0.53125, 302 | 0.50781, 303 | 0.48438, 304 | 0.46094, 305 | 0.44531, 306 | 0.42188, 307 | 0.39844, 308 | 0.38281, 309 | 0.35156, 310 | 0.33594, 311 | 0.3125, 312 | 0.28906, 313 | 0.26563, 314 | 0.25, 315 | 0.22656, 316 | 0.20313, 317 | 0.17969, 318 | 0.15625, 319 | 0.14063, 320 | 0.11719, 321 | 0.09375, 322 | 0.07031, 323 | 0.05469, 324 | 0.02344, 325 | 0.00781, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 0, 333 | 0, 334 | 0, 335 | 0, 336 | 0 ], 337 | "ImageOrientationPatientDICOM": [ 338 | 1, 339 | 0, 340 | 0, 341 | 0, 342 | 1, 343 | 0 ], 344 | "ConversionSoftware": "dcm2niix", 345 | "ConversionSoftwareVersion": "v1.0.20190902" 346 | } 347 | -------------------------------------------------------------------------------- /nnunet/evaluation/model_selection/summarize_results_in_one_json.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from collections import OrderedDict 16 | from nnunet.evaluation.add_mean_dice_to_json import foreground_mean 17 | from batchgenerators.utilities.file_and_folder_operations import * 18 | from nnunet.paths import network_training_output_dir 19 | import numpy as np 20 | 21 | 22 | def summarize(tasks, models=('2d', '3d_lowres', '3d_fullres', '3d_cascade_fullres'), 23 | output_dir=join(network_training_output_dir, "summary_jsons"), folds=(0, 1, 2, 3, 4)): 24 | maybe_mkdir_p(output_dir) 25 | 26 | if len(tasks) == 1 and tasks[0] == "all": 27 | tasks = list(range(100)) 28 | else: 29 | tasks = [int(i) for i in tasks] 30 | 31 | for model in models: 32 | for t in tasks: 33 | t = int(t) 34 | if not isdir(join(network_training_output_dir, model)): 35 | continue 36 | task_name = subfolders(join(network_training_output_dir, model), prefix="Task%02.0d" % t, join=False) 37 | if len(task_name) != 1: 38 | print("did not find unique output folder for network %s and task %s" % (model, t)) 39 | continue 40 | task_name = task_name[0] 41 | out_dir_task = join(network_training_output_dir, model, task_name) 42 | 43 | model_trainers = subdirs(out_dir_task, join=False) 44 | for trainer in model_trainers: 45 | if trainer.startswith("fold"): 46 | continue 47 | out_dir = join(out_dir_task, trainer) 48 | 49 | validation_folders = [] 50 | for fld in folds: 51 | d = join(out_dir, "fold%d"%fld) 52 | if not isdir(d): 53 | d = join(out_dir, "fold_%d"%fld) 54 | if not isdir(d): 55 | break 56 | validation_folders += subfolders(d, prefix="validation", join=False) 57 | 58 | for v in validation_folders: 59 | ok = True 60 | metrics = OrderedDict() 61 | for fld in folds: 62 | d = join(out_dir, "fold%d"%fld) 63 | if not isdir(d): 64 | d = join(out_dir, "fold_%d"%fld) 65 | if not isdir(d): 66 | ok = False 67 | break 68 | validation_folder = join(d, v) 69 | 70 | if not isfile(join(validation_folder, "summary.json")): 71 | print("summary.json missing for net %s task %s fold %d" % (model, task_name, fld)) 72 | ok = False 73 | break 74 | 75 | metrics_tmp = load_json(join(validation_folder, "summary.json"))["results"]["mean"] 76 | for l in metrics_tmp.keys(): 77 | if metrics.get(l) is None: 78 | metrics[l] = OrderedDict() 79 | for m in metrics_tmp[l].keys(): 80 | if metrics[l].get(m) is None: 81 | metrics[l][m] = [] 82 | metrics[l][m].append(metrics_tmp[l][m]) 83 | if ok: 84 | for l in metrics.keys(): 85 | for m in metrics[l].keys(): 86 | assert len(metrics[l][m]) == len(folds) 87 | metrics[l][m] = np.mean(metrics[l][m]) 88 | json_out = OrderedDict() 89 | json_out["results"] = OrderedDict() 90 | json_out["results"]["mean"] = metrics 91 | json_out["task"] = task_name 92 | json_out["description"] = model + " " + task_name + " all folds summary" 93 | json_out["name"] = model + " " + task_name + " all folds summary" 94 | json_out["experiment_name"] = model 95 | save_json(json_out, join(out_dir, "summary_allFolds__%s.json" % v)) 96 | save_json(json_out, join(output_dir, "%s__%s__%s__%s.json" % (task_name, model, trainer, v))) 97 | foreground_mean(join(out_dir, "summary_allFolds__%s.json" % v)) 98 | foreground_mean(join(output_dir, "%s__%s__%s__%s.json" % (task_name, model, trainer, v))) 99 | 100 | 101 | if __name__ == "__main__": 102 | import argparse 103 | parser = argparse.ArgumentParser(usage="This is intended to identify the best model based on the five fold " 104 | "cross-validation. Running this script requires alle models to have been run " 105 | "already. This script will summarize the results of the five folds of all " 106 | "models in one json each for easy interpretability") 107 | parser.add_argument("-t", '--task_ids', nargs="+", required=True, help="task id. can be 'all'") 108 | parser.add_argument("-f", '--folds', nargs="+", required=False, type=int, default=[0, 1, 2, 3, 4]) 109 | parser.add_argument("-m", '--models', nargs="+", required=False, default=['2d', '3d_lowres', '3d_fullres', '3d_cascade_fullres']) 110 | 111 | args = parser.parse_args() 112 | tasks = args.task_ids 113 | models = args.models 114 | 115 | folds = args.folds 116 | summarize(tasks, models, folds=folds) 117 | 118 | -------------------------------------------------------------------------------- /nnunet/evaluation/model_selection/ensemble.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from multiprocessing.pool import Pool 16 | import shutil 17 | import numpy as np 18 | from nnunet.evaluation.evaluator import aggregate_scores 19 | from nnunet.inference.segmentation_export import save_segmentation_nifti_from_softmax 20 | from batchgenerators.utilities.file_and_folder_operations import * 21 | from nnunet.paths import network_training_output_dir, preprocessing_output_dir, default_plans_identifier 22 | import argparse 23 | 24 | 25 | def merge(args): 26 | file1, file2, properties_file, out_file = args 27 | if not isfile(out_file): 28 | res1 = np.load(file1)['softmax'] 29 | res2 = np.load(file2)['softmax'] 30 | props = load_pickle(properties_file) 31 | mn = np.mean((res1, res2), 0) 32 | save_segmentation_nifti_from_softmax(mn, out_file, props, 1, None, None, None) 33 | 34 | 35 | if __name__ == "__main__": 36 | parser = argparse.ArgumentParser(usage="This is intended to ensemble training images (from cross-validation) only. Use" 37 | "inference/ensemble_predictions.py instead") 38 | parser.add_argument("training_output_folder1") 39 | parser.add_argument("training_output_folder2") 40 | parser.add_argument("output_folder") 41 | parser.add_argument("task") # we need to know this for gt_segmentations 42 | 43 | args = parser.parse_args() 44 | 45 | training_output_folder1 = args.training_output_folder1 46 | training_output_folder2 = args.training_output_folder2 47 | output_folder = args.output_folder 48 | task = args.task 49 | 50 | # only_keep_largest_connected_component is the same for all stages 51 | dataset_directory = join(preprocessing_output_dir, task) 52 | plans = load_pickle(join(preprocessing_output_dir, task, default_plans_identifier + "_plans_2D.pkl")) # we need this only for the labels 53 | 54 | files1 = [] 55 | files2 = [] 56 | property_files = [] 57 | out_files = [] 58 | gt_segmentations = [] 59 | 60 | folder_with_gt_segs = join(dataset_directory, "gt_segmentations") 61 | folder_where_some_pkl_are = join(dataset_directory, "nnUNet_2D_stage0") # we can use this because npz are already 62 | # in the correct shape and we need the original geometry to restore the niftis 63 | 64 | folds = np.arange(5) 65 | 66 | for f in folds: 67 | validation_folder_net1 = join(training_output_folder1, "fold_%d" % f, "validation") 68 | validation_folder_net2 = join(training_output_folder2, "fold_%d" % f, "validation") 69 | patient_identifiers1 = subfiles(validation_folder_net1, False, None, 'npz', True) 70 | patient_identifiers2 = subfiles(validation_folder_net2, False, None, 'npz', True) 71 | # we don't do postprocessing anymore so there should not be any of that noPostProcess 72 | patient_identifiers1_nii = [i for i in subfiles(validation_folder_net1, False, None, suffix='nii.gz', sort=True) if not i.endswith("noPostProcess.nii.gz") and not i.endswith('_postprocessed.nii.gz')] 73 | patient_identifiers2_nii = [i for i in subfiles(validation_folder_net2, False, None, suffix='nii.gz', sort=True) if not i.endswith("noPostProcess.nii.gz") and not i.endswith('_postprocessed.nii.gz')] 74 | assert all([i[:-4] == j[:-7] for i, j in zip(patient_identifiers1, patient_identifiers1_nii)]), "npz seem to be missing. run validation with save_softmax=True" 75 | assert all([i[:-4] == j[:-7] for i, j in zip(patient_identifiers2, patient_identifiers2_nii)]), "npz seem to be missing. run validation with save_softmax=True" 76 | 77 | all_patient_identifiers = patient_identifiers1 78 | for p in patient_identifiers2: 79 | if p not in all_patient_identifiers: 80 | all_patient_identifiers.append(p) 81 | 82 | # assert these patients exist for both methods 83 | assert all([isfile(join(validation_folder_net1, i)) for i in all_patient_identifiers]) 84 | assert all([isfile(join(validation_folder_net2, i)) for i in all_patient_identifiers]) 85 | 86 | maybe_mkdir_p(output_folder) 87 | 88 | for p in all_patient_identifiers: 89 | files1.append(join(validation_folder_net1, p)) 90 | files2.append(join(validation_folder_net2, p)) 91 | property_files.append(join(folder_where_some_pkl_are, p)[:-3] + "pkl") 92 | out_files.append(join(output_folder, p[:-4] + ".nii.gz")) 93 | gt_segmentations.append(join(folder_with_gt_segs, p[:-4] + ".nii.gz")) 94 | 95 | p = Pool(8) 96 | p.map(merge, zip(files1, files2, property_files, out_files)) 97 | p.close() 98 | p.join() 99 | 100 | """for args in zip(files1, files2, property_files, out_files, [only_keep_largest_connected_component] * len(files1)): 101 | print(args[0], args[1]) 102 | merge(args)""" 103 | 104 | if not isfile(join(output_folder, "summary_allFolds.json")) and len(out_files) > 0: 105 | out_dir_all_json = join(network_training_output_dir, "summary_jsons") 106 | # now evaluate if all these gt files exist 107 | aggregate_scores(tuple(zip(out_files, gt_segmentations)), labels=plans['all_classes'], 108 | json_output_file=join(output_folder, "summary_allFolds.json"), json_task=task, 109 | json_name=task + "__" + output_folder.split("/")[-1], num_threads=4) 110 | json_out = load_json(join(output_folder, "summary_allFolds.json")) 111 | json_out["experiment_name"] = output_folder.split("/")[-1] 112 | save_json(json_out, join(output_folder, "summary_allFolds.json")) 113 | shutil.copy(join(output_folder, "summary_allFolds.json"), join(out_dir_all_json, "%s__%s.json" % (task, output_folder.split("/")[-1]))) 114 | -------------------------------------------------------------------------------- /nnunet/evaluation/model_selection/summarize_results_with_plans.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from batchgenerators.utilities.file_and_folder_operations import * 16 | import os 17 | from nnunet.evaluation.model_selection.summarize_results_in_one_json import summarize 18 | from nnunet.paths import network_training_output_dir 19 | import numpy as np 20 | 21 | 22 | def list_to_string(l, delim=","): 23 | st = "%03.3f" % l[0] 24 | for i in l[1:]: 25 | st += delim + "%03.3f" % i 26 | return st 27 | 28 | 29 | def write_plans_to_file(f, plans_file, stage=0, do_linebreak_at_end=True, override_name=None): 30 | a = load_pickle(plans_file) 31 | stages = list(a['plans_per_stage'].keys()) 32 | stages.sort() 33 | patch_size_in_mm = [i * j for i, j in zip(a['plans_per_stage'][stages[stage]]['patch_size'], 34 | a['plans_per_stage'][stages[stage]]['current_spacing'])] 35 | median_patient_size_in_mm = [i * j for i, j in zip(a['plans_per_stage'][stages[stage]]['median_patient_size_in_voxels'], 36 | a['plans_per_stage'][stages[stage]]['current_spacing'])] 37 | if override_name is None: 38 | f.write(plans_file.split("/")[-2] + "__" + plans_file.split("/")[-1]) 39 | else: 40 | f.write(override_name) 41 | f.write(";%d" % stage) 42 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['batch_size'])) 43 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['num_pool_per_axis'])) 44 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['patch_size'])) 45 | f.write(";%s" % list_to_string(patch_size_in_mm)) 46 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['median_patient_size_in_voxels'])) 47 | f.write(";%s" % list_to_string(median_patient_size_in_mm)) 48 | f.write(";%s" % list_to_string(a['plans_per_stage'][stages[stage]]['current_spacing'])) 49 | f.write(";%s" % list_to_string(a['plans_per_stage'][stages[stage]]['original_spacing'])) 50 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['pool_op_kernel_sizes'])) 51 | f.write(";%s" % str(a['plans_per_stage'][stages[stage]]['conv_kernel_sizes'])) 52 | if do_linebreak_at_end: 53 | f.write("\n") 54 | 55 | 56 | if __name__ == "__main__": 57 | summarize((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 24, 27), output_dir=join(network_training_output_dir, "summary_fold0"), folds=(0,)) 58 | base_dir = os.environ['RESULTS_FOLDER'] 59 | nnunets = ['nnUNetV2', 'nnUNetV2_zspacing'] 60 | task_ids = list(range(99)) 61 | with open("summary.csv", 'w') as f: 62 | f.write("identifier;stage;batch_size;num_pool_per_axis;patch_size;patch_size(mm);median_patient_size_in_voxels;median_patient_size_in_mm;current_spacing;original_spacing;pool_op_kernel_sizes;conv_kernel_sizes;patient_dc;global_dc\n") 63 | for i in task_ids: 64 | for nnunet in nnunets: 65 | try: 66 | summary_folder = join(base_dir, nnunet, "summary_fold0") 67 | if isdir(summary_folder): 68 | summary_files = subfiles(summary_folder, join=False, prefix="Task%02.0d_" % i, suffix=".json", sort=True) 69 | for s in summary_files: 70 | tmp = s.split("__") 71 | trainer = tmp[2] 72 | 73 | expected_output_folder = join(base_dir, nnunet, tmp[1], tmp[0], tmp[2].split(".")[0]) 74 | name = tmp[0] + "__" + nnunet + "__" + tmp[1] + "__" + tmp[2].split(".")[0] 75 | global_dice_json = join(base_dir, nnunet, tmp[1], tmp[0], tmp[2].split(".")[0], "fold_0", "validation_tiledTrue_doMirror_True", "global_dice.json") 76 | 77 | if not isdir(expected_output_folder) or len(tmp) > 3: 78 | if len(tmp) == 2: 79 | continue 80 | expected_output_folder = join(base_dir, nnunet, tmp[1], tmp[0], tmp[2] + "__" + tmp[3].split(".")[0]) 81 | name = tmp[0] + "__" + nnunet + "__" + tmp[1] + "__" + tmp[2] + "__" + tmp[3].split(".")[0] 82 | global_dice_json = join(base_dir, nnunet, tmp[1], tmp[0], tmp[2] + "__" + tmp[3].split(".")[0], "fold_0", "validation_tiledTrue_doMirror_True", "global_dice.json") 83 | 84 | assert isdir(expected_output_folder), "expected output dir not found" 85 | plans_file = join(expected_output_folder, "plans.pkl") 86 | assert isfile(plans_file) 87 | 88 | plans = load_pickle(plans_file) 89 | num_stages = len(plans['plans_per_stage']) 90 | if num_stages > 1 and tmp[1] == "3d_fullres": 91 | stage = 1 92 | elif (num_stages == 1 and tmp[1] == "3d_fullres") or tmp[1] == "3d_lowres": 93 | stage = 0 94 | else: 95 | print("skipping", s) 96 | continue 97 | 98 | g_dc = load_json(global_dice_json) 99 | mn_glob_dc = np.mean(list(g_dc.values())) 100 | 101 | write_plans_to_file(f, plans_file, stage, False, name) 102 | # now read and add result to end of line 103 | results = load_json(join(summary_folder, s)) 104 | mean_dc = results['results']['mean']['mean']['Dice'] 105 | f.write(";%03.3f" % mean_dc) 106 | f.write(";%03.3f\n" % mn_glob_dc) 107 | print(name, mean_dc) 108 | except Exception as e: 109 | print(e) 110 | -------------------------------------------------------------------------------- /nnunet/run/run_training.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import argparse 16 | from batchgenerators.utilities.file_and_folder_operations import * 17 | from nnunet.run.default_configuration import get_default_configuration 18 | from nnunet.paths import default_plans_identifier 19 | from nnunet.training.cascade_stuff.predict_next_stage import predict_next_stage 20 | from nnunet.training.network_training.nnUNetTrainer import nnUNetTrainer 21 | from nnunet.training.network_training.nnUNetTrainerCascadeFullRes import nnUNetTrainerCascadeFullRes 22 | 23 | if __name__ == "__main__": 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument("network") 26 | parser.add_argument("network_trainer") 27 | parser.add_argument("task") 28 | parser.add_argument("fold", help='0, 1, ..., 5 or \'all\'') 29 | parser.add_argument("-val", "--validation_only", help="use this if you want to only run the validation", 30 | action="store_true") 31 | parser.add_argument("-c", "--continue_training", help="use this if you want to continue a training", 32 | action="store_true") 33 | parser.add_argument("-p", help="plans identifier", default=default_plans_identifier, required=False) 34 | parser.add_argument("-u", "--unpack_data", help="Leave it as 1, development only", required=False, default=1, 35 | type=int) 36 | parser.add_argument("--ndet", help="Per default training is deterministic, " 37 | "nondeterministic allows cudnn.benchmark which will can give up to " 38 | "20%% performance. Set this to do nondeterministic training", 39 | required=False, default=False, action="store_true") 40 | parser.add_argument("--npz", required=False, default=False, action="store_true", help="if set then nnUNet will " 41 | "export npz files of " 42 | "predicted segmentations " 43 | "in the vlaidation as well. " 44 | "This is needed to run the " 45 | "ensembling step so unless " 46 | "you are developing nnUNet " 47 | "you should enable this") 48 | parser.add_argument("--find_lr", required=False, default=False, action="store_true", help="not used here, just for fun") 49 | parser.add_argument("--valbest", required=False, default=False, action="store_true", help="hands off. This is not intended to be used") 50 | parser.add_argument("--fp16", required=False, default=False, action="store_true", help="enable fp16 training. Makes sense for 2d only! (and only on supported hardware!)") 51 | 52 | args = parser.parse_args() 53 | 54 | task = args.task 55 | fold = args.fold 56 | network = args.network 57 | network_trainer = args.network_trainer 58 | validation_only = args.validation_only 59 | plans_identifier = args.p 60 | find_lr = args.find_lr 61 | unpack = args.unpack_data 62 | deterministic = not args.ndet 63 | valbest = args.valbest 64 | fp16 = args.fp16 65 | 66 | if unpack == 0: 67 | unpack = False 68 | elif unpack == 1: 69 | unpack = True 70 | else: 71 | raise ValueError("Unexpected value for -u/--unpack_data: %s. Use 1 or 0." % str(unpack)) 72 | 73 | if fold == 'all': 74 | pass 75 | else: 76 | fold = int(fold) 77 | 78 | plans_file, output_folder_name, dataset_directory, batch_dice, stage, \ 79 | trainer_class = get_default_configuration(network, task, network_trainer, plans_identifier) 80 | 81 | if trainer_class is None: 82 | raise RuntimeError("Could not find trainer class in nnunet.training.network_training") 83 | 84 | if network == "3d_cascade_fullres": 85 | assert issubclass(trainer_class, nnUNetTrainerCascadeFullRes), "If running 3d_cascade_fullres then your " \ 86 | "trainer class must be derived from " \ 87 | "nnUNetTrainerCascadeFullRes" 88 | else: 89 | assert issubclass(trainer_class, nnUNetTrainer), "network_trainer was found but is not derived from nnUNetTrainer" 90 | 91 | trainer = trainer_class(plans_file, fold, output_folder=output_folder_name, dataset_directory=dataset_directory, 92 | batch_dice=batch_dice, stage=stage, unpack_data=unpack, deterministic=deterministic, 93 | fp16=fp16) 94 | 95 | trainer.initialize(not validation_only) 96 | 97 | if find_lr: 98 | trainer.find_lr() 99 | else: 100 | if not validation_only: 101 | if args.continue_training: 102 | trainer.load_latest_checkpoint() 103 | trainer.run_training() 104 | elif not valbest: 105 | trainer.load_latest_checkpoint(train=False) 106 | 107 | if valbest: 108 | trainer.load_best_checkpoint(train=False) 109 | val_folder = "validation_best_epoch" 110 | else: 111 | val_folder = "validation" 112 | 113 | # predict validation 114 | trainer.validate(save_softmax=args.npz, validation_folder_name=val_folder) 115 | 116 | if network == '3d_lowres': 117 | trainer.load_best_checkpoint(False) 118 | print("predicting segmentations for the next stage of the cascade") 119 | predict_next_stage(trainer, join(dataset_directory, trainer.plans['data_identifier'] + "_stage%d" % 1)) 120 | -------------------------------------------------------------------------------- /nnunet/nnUNet_base/nnUNet_raw/Task11_LiverTumor/imagesTs/la_2723303.json: -------------------------------------------------------------------------------- 1 | { 2 | "Modality": "CT", 3 | "Manufacturer": "Siemens", 4 | "ManufacturersModelName": "SOMATOM_Definition_AS+", 5 | "InstitutionName": "SH_RENJI_HOSPITAL", 6 | "InstitutionAddress": "Dongfang_Rd_5D7881_PuDong_new_Dist_CN", 7 | "DeviceSerialNumber": "64135", 8 | "StationName": "CTAWP64135", 9 | "BodyPartExamined": "ABDOMEN", 10 | "PatientPosition": "FFS", 11 | "SoftwareVersions": "syngo_CT_2012B", 12 | "SeriesDescription": "Venous_Phase_1.0_B31f", 13 | "ProtocolName": "2_Abd2_Phase", 14 | "ImageType": ["ORIGINAL", "PRIMARY", "AXIAL", "CT", "SOM5", "SPI"], 15 | "SeriesNumber": 9, 16 | "AcquisitionTime": "11:31:25.993000", 17 | "AcquisitionNumber": 13, 18 | "XRayExposure": 300, 19 | "ReconMatrixPE": 512, 20 | "SliceTiming": [ 21 | 4.71094, 22 | 4.70313, 23 | 4.6875, 24 | 4.67188, 25 | 4.66406, 26 | 4.64844, 27 | 4.63281, 28 | 4.625, 29 | 4.60938, 30 | 4.59375, 31 | 4.58594, 32 | 4.57031, 33 | 4.55469, 34 | 4.54688, 35 | 4.53125, 36 | 4.51563, 37 | 4.5, 38 | 4.49219, 39 | 4.47656, 40 | 4.46094, 41 | 4.44531, 42 | 4.4375, 43 | 4.42188, 44 | 4.40625, 45 | 4.39844, 46 | 4.39063, 47 | 4.36719, 48 | 4.35938, 49 | 4.35156, 50 | 4.32813, 51 | 4.32031, 52 | 4.3125, 53 | 4.28906, 54 | 4.28125, 55 | 4.27344, 56 | 4.25781, 57 | 4.24219, 58 | 4.23438, 59 | 4.21875, 60 | 4.20313, 61 | 4.19531, 62 | 4.17188, 63 | 4.16406, 64 | 4.15625, 65 | 4.14063, 66 | 4.125, 67 | 4.11719, 68 | 4.10156, 69 | 4.08594, 70 | 4.07813, 71 | 4.0625, 72 | 4.04688, 73 | 4.03906, 74 | 4.02344, 75 | 4.00781, 76 | 4, 77 | 3.98438, 78 | 3.96875, 79 | 3.95313, 80 | 3.94531, 81 | 3.92969, 82 | 3.91406, 83 | 3.90625, 84 | 3.89063, 85 | 3.875, 86 | 3.86719, 87 | 3.85156, 88 | 3.83594, 89 | 3.82813, 90 | 3.8125, 91 | 3.79688, 92 | 3.78906, 93 | 3.77344, 94 | 3.76563, 95 | 3.74219, 96 | 3.73438, 97 | 3.72656, 98 | 3.70313, 99 | 3.69531, 100 | 3.6875, 101 | 3.66406, 102 | 3.65625, 103 | 3.64844, 104 | 3.625, 105 | 3.61719, 106 | 3.60938, 107 | 3.58594, 108 | 3.57813, 109 | 3.57031, 110 | 3.55469, 111 | 3.53906, 112 | 3.53125, 113 | 3.51563, 114 | 3.5, 115 | 3.49219, 116 | 3.47656, 117 | 3.46094, 118 | 3.45313, 119 | 3.4375, 120 | 3.42188, 121 | 3.41406, 122 | 3.39844, 123 | 3.38281, 124 | 3.375, 125 | 3.35938, 126 | 3.34375, 127 | 3.33594, 128 | 3.32031, 129 | 3.30469, 130 | 3.28906, 131 | 3.28125, 132 | 3.26563, 133 | 3.25, 134 | 3.24219, 135 | 3.22656, 136 | 3.21094, 137 | 3.19531, 138 | 3.1875, 139 | 3.17188, 140 | 3.16406, 141 | 3.14844, 142 | 3.14063, 143 | 3.11719, 144 | 3.10938, 145 | 3.10156, 146 | 3.07813, 147 | 3.07031, 148 | 3.0625, 149 | 3.03906, 150 | 3.03125, 151 | 3.02344, 152 | 3, 153 | 2.99219, 154 | 2.98438, 155 | 2.96094, 156 | 2.95313, 157 | 2.94531, 158 | 2.92188, 159 | 2.91406, 160 | 2.90625, 161 | 2.89063, 162 | 2.875, 163 | 2.86719, 164 | 2.85156, 165 | 2.83594, 166 | 2.82813, 167 | 2.8125, 168 | 2.79688, 169 | 2.78906, 170 | 2.77344, 171 | 2.75781, 172 | 2.75, 173 | 2.73438, 174 | 2.71875, 175 | 2.71094, 176 | 2.69531, 177 | 2.67969, 178 | 2.67188, 179 | 2.65625, 180 | 2.64063, 181 | 2.63281, 182 | 2.61719, 183 | 2.60156, 184 | 2.59375, 185 | 2.57813, 186 | 2.5625, 187 | 2.54688, 188 | 2.53906, 189 | 2.52344, 190 | 2.51563, 191 | 2.5, 192 | 2.48438, 193 | 2.47656, 194 | 2.45313, 195 | 2.44531, 196 | 2.4375, 197 | 2.41406, 198 | 2.40625, 199 | 2.39844, 200 | 2.375, 201 | 2.36719, 202 | 2.35938, 203 | 2.33594, 204 | 2.32813, 205 | 2.32031, 206 | 2.29688, 207 | 2.28906, 208 | 2.28125, 209 | 2.26563, 210 | 2.25, 211 | 2.24219, 212 | 2.22656, 213 | 2.21094, 214 | 2.20313, 215 | 2.1875, 216 | 2.17188, 217 | 2.16406, 218 | 2.14844, 219 | 2.13281, 220 | 2.125, 221 | 2.10938, 222 | 2.09375, 223 | 2.08594, 224 | 2.07031, 225 | 2.05469, 226 | 2.03906, 227 | 2.03125, 228 | 2.01563, 229 | 2, 230 | 1.99219, 231 | 1.97656, 232 | 1.96094, 233 | 1.95313, 234 | 1.9375, 235 | 1.92188, 236 | 1.91406, 237 | 1.89844, 238 | 1.89063, 239 | 1.875, 240 | 1.85938, 241 | 1.85156, 242 | 1.83594, 243 | 1.82031, 244 | 1.8125, 245 | 1.79688, 246 | 1.78125, 247 | 1.77344, 248 | 1.75781, 249 | 1.74219, 250 | 1.73438, 251 | 1.71875, 252 | 1.70313, 253 | 1.69531, 254 | 1.67969, 255 | 1.66406, 256 | 1.65625, 257 | 1.64063, 258 | 1.625, 259 | 1.61719, 260 | 1.60156, 261 | 1.58594, 262 | 1.57813, 263 | 1.5625, 264 | 1.54688, 265 | 1.53906, 266 | 1.52344, 267 | 1.50781, 268 | 1.5, 269 | 1.48438, 270 | 1.46875, 271 | 1.46094, 272 | 1.44531, 273 | 1.42969, 274 | 1.42188, 275 | 1.40625, 276 | 1.39063, 277 | 1.38281, 278 | 1.36719, 279 | 1.35156, 280 | 1.34375, 281 | 1.32813, 282 | 1.3125, 283 | 1.30469, 284 | 1.28906, 285 | 1.27344, 286 | 1.26563, 287 | 1.24219, 288 | 1.23438, 289 | 1.22656, 290 | 1.21094, 291 | 1.19531, 292 | 1.1875, 293 | 1.17188, 294 | 1.15625, 295 | 1.14844, 296 | 1.13281, 297 | 1.11719, 298 | 1.10938, 299 | 1.08594, 300 | 1.07813, 301 | 1.0625, 302 | 1.04688, 303 | 1.03906, 304 | 1.02344, 305 | 1.01563, 306 | 1, 307 | 0.98438, 308 | 0.97656, 309 | 0.96094, 310 | 0.94531, 311 | 0.92969, 312 | 0.92188, 313 | 0.90625, 314 | 0.89063, 315 | 0.88281, 316 | 0.86719, 317 | 0.85156, 318 | 0.84375, 319 | 0.82813, 320 | 0.8125, 321 | 0.80469, 322 | 0.78906, 323 | 0.77344, 324 | 0.76563, 325 | 0.75, 326 | 0.73438, 327 | 0.72656, 328 | 0.71094, 329 | 0.69531, 330 | 0.6875, 331 | 0.67188, 332 | 0.65625, 333 | 0.64844, 334 | 0.63281, 335 | 0.61719, 336 | 0.60938, 337 | 0.59375, 338 | 0.57813, 339 | 0.57031, 340 | 0.55469, 341 | 0.53906, 342 | 0.53125, 343 | 0.51563, 344 | 0.5, 345 | 0.49219, 346 | 0.47656, 347 | 0.46094, 348 | 0.45313, 349 | 0.4375, 350 | 0.42188, 351 | 0.41406, 352 | 0.40625, 353 | 0.39063, 354 | 0.375, 355 | 0.36719, 356 | 0.35156, 357 | 0.33594, 358 | 0.32813, 359 | 0.3125, 360 | 0.29688, 361 | 0.28125, 362 | 0.27344, 363 | 0.25781, 364 | 0.25, 365 | 0.23438, 366 | 0.21875, 367 | 0.21094, 368 | 0.19531, 369 | 0.17969, 370 | 0.17188, 371 | 0.15625, 372 | 0.14063, 373 | 0.13281, 374 | 0.11719, 375 | 0.10156, 376 | 0.09375, 377 | 0.07813, 378 | 0.0625, 379 | 0.05469, 380 | 0.03906, 381 | 0.02344, 382 | 0.01563, 383 | 0 ], 384 | "ImageOrientationPatientDICOM": [ 385 | 1, 386 | 0, 387 | 0, 388 | 0, 389 | 1, 390 | 0 ], 391 | "ConversionSoftware": "dcm2niix", 392 | "ConversionSoftwareVersion": "v1.0.20190902" 393 | } 394 | -------------------------------------------------------------------------------- /nnunet/training/data_augmentation/pyramid_augmentations.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from copy import deepcopy 16 | from skimage.morphology import label, ball 17 | from skimage.morphology.binary import binary_erosion, binary_dilation, binary_closing, binary_opening 18 | import numpy as np 19 | from batchgenerators.transforms import AbstractTransform 20 | 21 | 22 | class RemoveRandomConnectedComponentFromOneHotEncodingTransform(AbstractTransform): 23 | def __init__(self, channel_idx, key="data", p_per_sample=0.2, fill_with_other_class_p=0.25, 24 | dont_do_if_covers_more_than_X_percent=0.25): 25 | """ 26 | :param dont_do_if_covers_more_than_X_percent: dont_do_if_covers_more_than_X_percent=0.25 is 25\%! 27 | :param channel_idx: can be list or int 28 | :param key: 29 | """ 30 | self.dont_do_if_covers_more_than_X_percent = dont_do_if_covers_more_than_X_percent 31 | self.fill_with_other_class_p = fill_with_other_class_p 32 | self.p_per_sample = p_per_sample 33 | self.key = key 34 | if not isinstance(channel_idx, (list, tuple)): 35 | channel_idx = [channel_idx] 36 | self.channel_idx = channel_idx 37 | 38 | def __call__(self, **data_dict): 39 | data = data_dict.get(self.key) 40 | for b in range(data.shape[0]): 41 | if np.random.uniform() < self.p_per_sample: 42 | for c in self.channel_idx: 43 | workon = np.copy(data[b, c]) 44 | num_voxels = np.prod(workon.shape) 45 | lab, num_comp = label(workon, return_num=True) 46 | if num_comp > 0: 47 | component_ids = [] 48 | component_sizes = [] 49 | for i in range(1, num_comp + 1): 50 | component_ids.append(i) 51 | component_sizes.append(np.sum(lab == i)) 52 | component_ids = [i for i, j in zip(component_ids, component_sizes) if j < num_voxels*self.dont_do_if_covers_more_than_X_percent] 53 | #_ = component_ids.pop(np.argmax(component_sizes)) 54 | #else: 55 | # component_ids = list(range(1, num_comp + 1)) 56 | if len(component_ids) > 0: 57 | random_component = np.random.choice(component_ids) 58 | data[b, c][lab == random_component] = 0 59 | if np.random.uniform() < self.fill_with_other_class_p: 60 | other_ch = [i for i in self.channel_idx if i != c] 61 | if len(other_ch) > 0: 62 | other_class = np.random.choice(other_ch) 63 | data[b, other_class][lab == random_component] = 1 64 | data_dict[self.key] = data 65 | return data_dict 66 | 67 | 68 | class MoveSegAsOneHotToData(AbstractTransform): 69 | def __init__(self, channel_id, all_seg_labels, key_origin="seg", key_target="data", remove_from_origin=True): 70 | self.remove_from_origin = remove_from_origin 71 | self.all_seg_labels = all_seg_labels 72 | self.key_target = key_target 73 | self.key_origin = key_origin 74 | self.channel_id = channel_id 75 | 76 | def __call__(self, **data_dict): 77 | origin = data_dict.get(self.key_origin) 78 | target = data_dict.get(self.key_target) 79 | seg = origin[:, self.channel_id:self.channel_id+1] 80 | seg_onehot = np.zeros((seg.shape[0], len(self.all_seg_labels), *seg.shape[2:]), dtype=seg.dtype) 81 | for i, l in enumerate(self.all_seg_labels): 82 | seg_onehot[:, i][seg[:, 0] == l] = 1 83 | target = np.concatenate((target, seg_onehot), 1) 84 | data_dict[self.key_target] = target 85 | 86 | if self.remove_from_origin: 87 | remaining_channels = [i for i in range(origin.shape[1]) if i != self.channel_id] 88 | origin = origin[:, remaining_channels] 89 | data_dict[self.key_origin] = origin 90 | return data_dict 91 | 92 | 93 | class ApplyRandomBinaryOperatorTransform(AbstractTransform): 94 | def __init__(self, channel_idx, p_per_sample=0.3, any_of_these=(binary_dilation, binary_erosion, binary_closing, binary_opening), 95 | key="data", strel_size=(1, 10)): 96 | """ 97 | 98 | :param channel_idx: can be list or int 99 | :param p_per_sample: 100 | :param any_of_these: 101 | :param fill_diff_with_other_class: 102 | :param key: 103 | :param strel_size: 104 | """ 105 | self.strel_size = strel_size 106 | self.key = key 107 | self.any_of_these = any_of_these 108 | self.p_per_sample = p_per_sample 109 | 110 | assert not isinstance(channel_idx, tuple), "bäh" 111 | 112 | if not isinstance(channel_idx, list): 113 | channel_idx = [channel_idx] 114 | self.channel_idx = channel_idx 115 | 116 | def __call__(self, **data_dict): 117 | data = data_dict.get(self.key) 118 | for b in range(data.shape[0]): 119 | if np.random.uniform() < self.p_per_sample: 120 | ch = deepcopy(self.channel_idx) 121 | np.random.shuffle(ch) 122 | for c in ch: 123 | operation = np.random.choice(self.any_of_these) 124 | selem = ball(np.random.uniform(*self.strel_size)) 125 | workon = np.copy(data[b, c]).astype(int) 126 | res = operation(workon, selem).astype(workon.dtype) 127 | data[b, c] = res 128 | 129 | # if class was added, we need to remove it in ALL other channels to keep one hot encoding 130 | # properties 131 | # we modify data 132 | other_ch = [i for i in ch if i != c] 133 | if len(other_ch) > 0: 134 | was_added_mask = (res - workon) > 0 135 | for oc in other_ch: 136 | data[b, oc][was_added_mask] = 0 137 | # if class was removed, leave it at backgound 138 | data_dict[self.key] = data 139 | return data_dict 140 | -------------------------------------------------------------------------------- /nnunet/training/model_restore.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import nnunet 16 | import torch 17 | from batchgenerators.utilities.file_and_folder_operations import * 18 | import importlib 19 | import pkgutil 20 | from nnunet.training.network_training.nnUNetTrainer import nnUNetTrainer 21 | 22 | 23 | def recursive_find_trainer(folder, trainer_name, current_module): 24 | tr = None 25 | for importer, modname, ispkg in pkgutil.iter_modules(folder): 26 | # print(modname, ispkg) 27 | if not ispkg: 28 | m = importlib.import_module(current_module + "." + modname) 29 | if hasattr(m, trainer_name): 30 | tr = getattr(m, trainer_name) 31 | break 32 | 33 | if tr is None: 34 | for importer, modname, ispkg in pkgutil.iter_modules(folder): 35 | if ispkg: 36 | next_current_module = current_module + "." + modname 37 | tr = recursive_find_trainer([join(folder[0], modname)], trainer_name, current_module=next_current_module) 38 | if tr is not None: 39 | break 40 | 41 | return tr 42 | 43 | 44 | def restore_model(pkl_file, checkpoint=None, train=False): 45 | """ 46 | This is a utility function to load any nnUNet trainer from a pkl. It will recursively search 47 | nnunet.trainig.network_training for the file that contains the trainer and instantiate it with the arguments saved in the pkl file. If checkpoint 48 | is specified, it will furthermore load the checkpoint file in train/test mode (as specified by train). 49 | The pkl file required here is the one that will be saved automatically when calling nnUNetTrainer.save_checkpoint. 50 | :param pkl_file: 51 | :param checkpoint: 52 | :param train: 53 | :return: 54 | """ 55 | info = load_pickle(pkl_file) 56 | init = info['init'] 57 | name = info['name'] 58 | search_in = join(nnunet.__path__[0], "training", "network_training") 59 | tr = recursive_find_trainer([search_in], name, current_module="nnunet.training.network_training") 60 | 61 | if tr is None: 62 | """ 63 | Fabian only. This will trigger searching for trainer classes in other repositories as well 64 | """ 65 | try: 66 | import meddec 67 | search_in = join(meddec.__path__[0], "model_training") 68 | tr = recursive_find_trainer([search_in], name, current_module="meddec.model_training") 69 | except ImportError: 70 | pass 71 | 72 | if tr is None: 73 | raise RuntimeError("Could not find the model trainer specified in checkpoint in nnunet.trainig.network_training. If it " 74 | "is not located there, please move it or change the code of restore_model. Your model " 75 | "trainer can be located in any directory within nnunet.trainig.network_training (search is recursive)." 76 | "\nDebug info: \ncheckpoint file: %s\nName of trainer: %s " % (checkpoint, name)) 77 | assert issubclass(tr, nnUNetTrainer), "The network trainer was found but is not a subclass of nnUNetTrainer. " \ 78 | "Please make it so!" 79 | 80 | if len(init) == 7: 81 | print("warning: this model seems to have been saved with a previous version of nnUNet. Attempting to load it " 82 | "anyways. Expect the unexpected.") 83 | print("manually editing init args...") 84 | init = [init[i] for i in range(len(init)) if i != 2] 85 | 86 | # init[0] is the plans file. This argument needs to be replaced because the original plans file may not exist 87 | # anymore. 88 | trainer = tr(*init) 89 | trainer.process_plans(info['plans']) 90 | if checkpoint is not None: 91 | trainer.load_checkpoint(checkpoint, train) 92 | return trainer 93 | 94 | 95 | def load_best_model_for_inference(folder): 96 | checkpoint = join(folder, "model_best.model") 97 | pkl_file = checkpoint + ".pkl" 98 | return restore_model(pkl_file, checkpoint, False) 99 | 100 | 101 | def load_model_and_checkpoint_files(folder, folds=None): 102 | """ 103 | used for if you need to ensemble the five models of a cross-validation. This will restore the model from the 104 | checkpoint in fold 0, load all parameters of the five folds in ram and return both. This will allow for fast 105 | switching between parameters (as opposed to loading them form disk each time). 106 | 107 | This is best used for inference and test prediction 108 | :param folder: 109 | :return: 110 | """ 111 | if isinstance(folds, str): 112 | folds = [join(folder, "all")] 113 | assert isdir(folds[0]), "no output folder for fold %s found" % folds 114 | elif isinstance(folds, (list, tuple)): 115 | if len(folds) == 1 and folds[0] == "all": 116 | folds = [join(folder, "all")] 117 | else: 118 | folds = [join(folder, "fold_%d" % i) for i in folds] 119 | assert all([isdir(i) for i in folds]), "list of folds specified but not all output folders are present" 120 | elif isinstance(folds, int): 121 | folds = [join(folder, "fold_%d" % folds)] 122 | assert all([isdir(i) for i in folds]), "output folder missing for fold %d" % folds 123 | elif folds is None: 124 | print("folds is None so we will automatically look for output folders (not using \'all\'!)") 125 | folds = subfolders(folder, prefix="fold") 126 | print("found the following folds: ", folds) 127 | else: 128 | raise ValueError("Unknown value for folds. Type: %s. Expected: list of int, int, str or None", str(type(folds))) 129 | 130 | trainer = restore_model(join(folds[0], "model_best.model.pkl")) 131 | trainer.output_folder = folder 132 | trainer.output_folder_base = folder 133 | trainer.update_fold(0) 134 | trainer.initialize(False) 135 | all_best_model_files = [join(i, "model_best.model") for i in folds] 136 | print("using the following model files: ", all_best_model_files) 137 | all_params = [torch.load(i, map_location=torch.device('cuda', torch.cuda.current_device())) for i in all_best_model_files] 138 | return trainer, all_params 139 | 140 | 141 | if __name__ == "__main__": 142 | pkl = "/home/fabian/PhD/results/nnUNetV2/nnUNetV2_3D_fullres/Task04_Hippocampus/fold0/model_best.model.pkl" 143 | checkpoint = pkl[:-4] 144 | train = False 145 | trainer = restore_model(pkl, checkpoint, train) 146 | --------------------------------------------------------------------------------