nnU-Netv2新手教程(医学图像分割设计的自适应深度学习框架)


nnUnet方法源自论文
大佬文章 https://www.nature.com/articles/s41592-020-01008-z
《Automated Design of Deep Learning Methods for Biomedical Image Segmentation》,来自德国癌症研究中心。

源码地址:https://github.com/MIC-DKFZ/nnUNet
nnUNet浓缩了医学图像的语义分割领域的大部分知识,并且具备自动为不同任务设计不同训练方案的框架,不需要人工进行调参。

安装Anaconda

方式一:从官网下载
官网首页:https://www.anaconda.com/
官网下载页:https://www.anaconda.com/products/individual#Downloads
根据你的系统选择相应的installer即可

方式二:清华镜像下载(中国大陆推荐)
在官网下载比较慢,而且容易断开连接,推荐用下面清华镜像方式:

下载地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/
打开后,可以通过时间排序找到最新版本下载,或者选一个你想要的版本。
安装Anaconda
在这里插入图片描述
勾选添加环境变量(不勾选的话,需要手动添加)
第二个勾选则是选择anaconda的python优先级最高,不管电脑是否安装了其他版本的python

手动添加环境变量如下:

在这里插入图片描述
硬盘已分区的情况下,不建议将Anaconda安装在C盘,建议安装在D盘等非系统盘
将以下环境变量,相应用户的目录可能会有所不同,但是后面的路径名相同。点击编辑,添加至path中
C:\anaconda3
C:\anaconda3\Scripts
C:\anaconda3\Library\bin
C:\anaconda3\Library\mingw-w64\bin
安装完成后,在CMD下输入conda
在这里插入图片描述
显示如图即安装成功

首先打开anaconda版的cmd(Anaconda Prompt),创建一个虚拟环境,创建指令为

conda create -n nnU-Net python=3.9 

在这里插入图片描述
在这里插入图片描述
proceed 选择 y

创建完成后激活环境,指令为:

conda activate nnU-Net

在这里插入图片描述
我们可以看到已经切换到之前创建的nnU-Net环境

安装Pytorch,指令为:

在cmd下输入指令:nvidia-smi 红框为自己电脑的cuda版本。查看自己显卡的cuda版本
在这里插入图片描述注意,下载pytorch时,cuda只能向下兼容,例如我的cuda为12.0,那么选择11.8,不能选择12.1及以上,另一台电脑为12.7则选择12.4,12.1和11.8均可,建议选择与自己版本相近的)
ps:如果自己显卡cuda过旧,可点击红框,下载先前的pytorch版本,注意要选择自己对应的操作系统
在这里插入图片描述

复制Run this Command,安装pytorch(cmd下) 选择cuda对应的版本(cuda向下兼容)

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

在这里插入图片描述
创建文件夹,用pycharm打开 Open Folder as PyCharm Project

在这里插入图片描述
打开pycharm,给项目添加解释器,将conda下面的nnU-Net环境添加至项目

在这里插入图片描述
配置完成后检查cuda和CPU是否可用

在这里插入图片描述
返回结果

在这里插入图片描述

安装Git插件,在pycharm设置Git路径

下载安装
在这里插入图片描述
在这里插入图片描述
对pycharm进行git的配置
在这里插入图片描述

选择正确的git路径,点击测试,即可显示版本
在这里插入图片描述

安装nnunet库(使用Git的方法安装):

首先在设置中将工具终端的shell路径设置为cmd(以便于在pycharm终端进行项目管理)
在这里插入图片描述
在pycharm打开终端,开始安装nnU-Netv2
在这里插入图片描述

git clone https://github.com/MIC-DKFZ/nnUNet.git
cd nnUNet
pip install -e .

在这里插入图片描述

在nnUNet文件夹中创建一个新文件夹nnUNetFrame,在该文件夹中新建三个文件夹:

nnUNet_raw
nnUNet_preprocessed
nnUNet_results

文件夹名称固定(raw存放原始数据集,preprocessed存放预处理后的训练计划,results存放训练结果等
在这里插入图片描述

修改data_process.py,用于处理BraTS2021 原始数据集

原始数据集并不满足nnU-Net的数据规范,因此我们需要将数据转换为规范数据。
这里data_process.py的编写,可以参考Deeplearning\nnUNet\nnunetv2\dataset_conversion目录下的文件
这里我们用到的是BraTS2021的数据集,因此可以参考Dataset137
在这里插入图片描述
data_process.py代码如下

import multiprocessing
import shutil
from multiprocessing import Pool

import SimpleITK as sitk
import numpy as np
from batchgenerators.utilities.file_and_folder_operations import *
from nnUNet.nnunetv2.dataset_conversion.generate_dataset_json import generate_dataset_json

#定义函数 将BraTS标签转换为nnU-Net格式
def copy_BraTS_segmentation_and_convert_labels_to_nnUNet(in_file: str, out_file: str) -> None:
    # use this for segmentation only!!!
    # nnUNet wants the labels to be continuous. BraTS is 0, 1, 2, 4 -> we make that into 0, 1, 2, 3
    img = sitk.ReadImage(in_file)
    img_npy = sitk.GetArrayFromImage(img)

    uniques = np.unique(img_npy)
    for u in uniques:
        if u not in [0, 1, 2, 4]:
            raise RuntimeError('unexpected label')

    seg_new = np.zeros_like(img_npy)
    seg_new[img_npy == 4] = 3
    seg_new[img_npy == 2] = 1
    seg_new[img_npy == 1] = 2
    img_corr = sitk.GetImageFromArray(seg_new)
    img_corr.CopyInformation(img)
    sitk.WriteImage(img_corr, out_file)

#定义函数 将标签转换回BraTS格式
def convert_labels_back_to_BraTS(seg: np.ndarray):
    new_seg = np.zeros_like(seg)
    new_seg[seg == 1] = 2
    new_seg[seg == 3] = 4
    new_seg[seg == 2] = 1
    return new_seg


def load_convert_labels_back_to_BraTS(filename, input_folder, output_folder):
    a = sitk.ReadImage(join(input_folder, filename))
    b = sitk.GetArrayFromImage(a)
    c = convert_labels_back_to_BraTS(b)
    d = sitk.GetImageFromArray(c)
    d.CopyInformation(a)
    sitk.WriteImage(d, join(output_folder, filename))

#定义函数 处理包含预测结果的文件夹
def convert_folder_with_preds_back_to_BraTS_labeling_convention(input_folder: str, output_folder: str, num_processes: int = 12):
    """
    reads all prediction files (nifti) in the input folder, converts the labels back to BraTS convention and saves the
    """
    maybe_mkdir_p(output_folder)
    nii = subfiles(input_folder, suffix='.nii.gz', join=False)
    with multiprocessing.get_context("spawn").Pool(num_processes) as p:
        p.starmap(load_convert_labels_back_to_BraTS, zip(nii, [input_folder] * len(nii), [output_folder] * len(nii)))


if __name__ == '__main__':
    # brats_data_dir = r'D:\brats\dataset\BraTS2021_Training_Data'
    brats_data_dir = r'C:\Users\Ai\Desktop\project\Deeplearning\BraTS2021_TrainingData'

    task_id = 100
    task_name = "BraTS2021"

    foldername = "Dataset%03.0d_%s" % (task_id, task_name)
    print(foldername)
    # setting up nnU-Net folders
    nnUNet_raw = r'C:\Users\Ai\Desktop\project\Deeplearning\nnUNet\nnUNetFrame\nnUNet_raw'
    # nnUNet_raw = r'D:\brats\dataset\data_process.py'

    print(nnUNet_raw)
    out_base = join(nnUNet_raw, foldername)
    imagestr = join(out_base, "imagesTr")
    labelstr = join(out_base, "labelsTr")
    # imagestr = join(out_base, "imagesTs")
    # labelstr = join(out_base, "labelsTs")
    maybe_mkdir_p(imagestr)
    maybe_mkdir_p(labelstr)

    case_ids = subdirs(brats_data_dir, prefix='BraTS', join=False)

    for c in case_ids:
        shutil.copy(join(brats_data_dir, c, c + "_t1.nii.gz"), join(imagestr, c + '_0000.nii.gz'))
        shutil.copy(join(brats_data_dir, c, c + "_t1ce.nii.gz"), join(imagestr, c + '_0001.nii.gz'))
        shutil.copy(join(brats_data_dir, c, c + "_t2.nii.gz"), join(imagestr, c + '_0002.nii.gz'))
        shutil.copy(join(brats_data_dir, c, c + "_flair.nii.gz"), join(imagestr, c + '_0003.nii.gz'))

        copy_BraTS_segmentation_and_convert_labels_to_nnUNet(join(brats_data_dir, c, c + "_seg.nii.gz"),
                                                             join(labelstr, c + '.nii.gz'))

    generate_dataset_json(out_base,
                          channel_names={0: 'T1', 1: 'T1ce', 2: 'T2', 3: 'Flair'},
                          labels={
                              'background': 0,
                              'whole tumor': (1, 2, 3),
                              'tumor core': (2, 3),
                              'enhancing tumor': (3, )
                          },
                          num_training_cases=len(case_ids),
                          file_ending='.nii.gz',
                          regions_class_order=(1, 2, 3),
                          license='see https://www.synapse.org/#!Synapse:syn25829067/wiki/610863',
                          reference='see https://www.synapse.org/#!Synapse:syn25829067/wiki/610863',
                          dataset_release='1.0')



将本代码中的brats_data_dir改为实际的数据集地址
nnUNet_raw改为上一步中nnUNet_raw的地址
同时可以用task_id,name自定义编号和项目名
运行代码,初始化后的数据即可载入在nnUNet_raw文件夹
在这里插入图片描述
值得一提的是,我们可以使用更改输出文件名的方法,在数据集里面选择子集,后创建测试集(imageTslabelTs
在这里插入图片描述

运行data_process.py 可以看到nnUNet_raw文件夹下有处理好的数据文件。

imageTr,labelsTr均为训练集,可建立imagesTs文件夹作为模型的测试集,这里并不会自动创建。之后要将测试集的数据项放入imagesTs文件夹

修改环境变量:

$Env:nnUNet_raw = "nnUNet_raw文件夹位置"
$Env:nnUNet_preprocessed = "nnUNet_preprocessed文件夹位置"
$Env:nnUNet_results = "nnUNet_results文件夹位置"

将引号内路径修改为实际文件夹路径,在cmd命令行中运行
*本方法修改环境变量为一次性,关闭命令行重新打开需重新输入
或者直接在系统中添加环境变量,添加nnUNet_raw、nnUNet_preprocessed、nnUNet_results三个变量,其path对应为所在位置。
在这里插入图片描述

进行预处理,命令行运行

nnUNetv2_plan_and_preprocess -d 100 --verify_dataset_integrity

进行数据预处理。
在这里插入图片描述

这里系统会提示

Dropping 3d_lowres config because the image size difference to 3d_fullres is too small. 
INFO: Configuration 3d_lowres not found in plans file nnUNetPlans.json of dataset Dataset100_BraTS2021. Skipping.

这里是因为nnU-Net觉得数据中lowres的分辨率和3d_fullers的分辨率差别不大,因此跳过3d_lowres的预处理
所以为了彻底解决这类提示,应该对BraTS2021数据集进行部分的重采样,降低分辨率。(暂时没有做出来)
为了实现the 3D full resolution U-Net of the cascade,
提出解决方案,在nnUNetPlans.json新建3d_lowers的plan,重新运行预处理命令,可以得到3d_lowers的预处理数据。

nnUNetPlans.json位置 :nnUNet\nnUNetFrame\nnUNet_preprocessed\数据集名称(例如:Dataset100_BraTS2021)
在这里插入图片描述

训练数据集:

包括三种U-Net网络配置,分别是2D U-Net,3D全分辨率U-Net,3D
U-Net级联(包括3D低分辨率U-Net和3D全分辨率U-Net),要进行级联下的3D全分辨率U-Net需先完成3D低分辨率U-Net。注意不是所有的数据集都能触发级联,在完成数据集预处理的图中可以看到,本数据集仅有2d和3d_fullres,3d_lowres,对于部分数据集来说,还有3d_lowres, 3d_cascade_fullres

在开始训练之前,我们需要修改epoch,epoch初始默认为1000,这样会极大延长训练时间
推荐在200-500个epoch之间,不要过高或过低
在这里插入图片描述

nnUNetv2_train 100 UNET_CONFIGURATION FOLD -npz

UNET_CONFIGURATION为选择的训练设置
有以下可选项:
2d
3d_fullres
3d_lowres
3d_cascade_fullres
FOLD为0至4的数字,代表第几折,因为nnUNet采用了五折交叉验证,每次训练后都会得到一个模型,一共五个,对这五个模型在验证集上的性能进行评估,得到可靠的模型性能指标。
Note that the 3D full resolution U-Net of the cascade requires the five folds of the low resolution U-Net to be completed!
要想进行3d_cascade_fullres的训练,必须先完成3d_lowres
实例命令:

nnUNetv2_train 100 3d_fullres 0

训练正常启动如下:
在这里插入图片描述
在这里插入图片描述
心得:若是重新启动了同一个数据集的模拟,建议清空所属results文件夹,不然模型寻优的时候会出现以下结果

RuntimeError: More than one of your folds has a prediction for case BraTS2021_00016.nii.gz

在这里插入图片描述

验证、推理和模型寻优

可以使用如下命令,这里用明确指定所有折-f 0 1 2 3 4,避免 nnU-Net 自动寻找 fold all(集成文件夹)

验证

对训练完后的模型输入以下命令(例如100数据集只训练了2d和3d_fullers,就输入以下命令)

nnUNetv2_train 100 2d 0  --val --npz
nnUNetv2_train 100 2d 1  --val --npz
nnUNetv2_train 100 2d 2  --val --npz
nnUNetv2_train 100 2d 3  --val --npz
nnUNetv2_train 100 2d 4  --val --npz
nnUNetv2_train 100 3d_fullres 0  --val --npz
nnUNetv2_train 100 3d_fullres 1  --val --npz
nnUNetv2_train 100 3d_fullres 2  --val --npz
nnUNetv2_train 100 3d_fullres 3  --val --npz
nnUNetv2_train 100 3d_fullres 4  --val --npz
nnUNetv2_train 100 3d_cascade_fullres 0  --val --npz
nnUNetv2_train 100 3d_cascade_fullres 1  --val --npz
nnUNetv2_train 100 3d_cascade_fullres 2  --val --npz
nnUNetv2_train 100 3d_cascade_fullres 3  --val --npz
nnUNetv2_train 100 3d_cascade_fullres 4  --val --npz

模型寻优

nnUNetv2_find_best_configuration 100
nnUNetv2_find_best_configuration 100 -c 2d 3d_fullres 3d_lowres 3d_cases_fullres -f 0 1 2 3 4

你会得到
在这里插入图片描述

Best: nnUNetTrainer__nnUNetPlans__3d_fullres: 0.817876361594065

Determining postprocessing for best model/ensemble Removing all but the largest foreground region did not improve results! Removing
all but the largest component for (1, 2, 3) did not improve results!
Dice before: 0.85902 after: 0.85325 Removing all but the largest
component for (2, 3) did not improve results! Dice before: 0.82286
after: 0.81074 Removing all but the largest component for (3,) did not
improve results! Dice before: 0.77175 after: 0.75838

Run inference like this:

nnUNetv2_predict -d Dataset100_BraTS2021 -i INPUT_FOLDER -o
OUTPUT_FOLDER -f 0 1 2 3 4 -tr nnUNetTrainer -c 3d_fullres -p
nnUNetPlans

Once inference is completed, run postprocessing like this:

nnUNetv2_apply_postprocessing -i OUTPUT_FOLDER -o OUTPUT_FOLDER_PP
-pp_pkl_file C:\Users\Ai\Desktop\project\Deeplearning\nnUNet\nnUNetFrame\nnUNet_results\Dataset100_BraTS2021\nnUNetTrainer__nnUNetPlans__3d_fullres\crossval_results_folds_0_1_2_3_4\postprocessing.pkl
-np 8 -plans_json C:\Users\Ai\Desktop\project\Deeplearning\nnUNet\nnUNetFrame\nnUNet_results\Dataset100_BraTS2021\nnUNetTrainer__nnUNetPlans__3d_fullres\crossval_results_folds_0_1_2_3_4\plans.json
(nnU-Net) PS C:\Users\Ai\Desktop\project\Deeplearning>
nnUNetv2_evaluate -d 100 -c 3d_fullres -f 0 1 2 3 4

你也可以在nnUNet_results/DATASET_NAME可以找到inference_instructions.txt,内有预测时建议使用的命令。
在这里插入图片描述
在这里插入图片描述

找到最佳预测,推理后续代码

推理预测

nnUNetv2_predict -d Dataset00100_BraTS2021 -i INPUT_FOLDER -o OUTPUT_FOLDER -f  0 1 2 3 4 -tr nnUNetTrainer -c 3d_fullres -p nnUNetPlans

例子

nnUNetv2_predict -i C:/Users/Ai/Desktop/project/Deeplearning/nnUNet/nnUNetFrame/nnUNet_raw/Dataset100_BraTS2021/imagesTs -o C:/Users/Ai/Desktop/project/Deeplearning/nnUNet/nnUNetFrame/nnUNet_results/BraTS2021_3d_predict -d 100 -c 3d_lowres -f 0 1 2 3 4 --save_probabilities
nnUNetv2_ensemble -i hippocampus_2d_predict hippocampus_3d_fullres_predict -o hippocampus_ensemble -np 8

例子

nnUNetv2_ensemble -i C:/Users/Ai/Desktop/project/Deeplearning/nnUNet/nnUNetFrame/nnUNet_results/BraTS2021_3d_predict -o C:/Users/Ai/Desktop/project/Deeplearning/nnUNet/nnUNetFrame/nnUNet_results/BraTS2021_3d_ensemble -np 8                                                                                                                                                                                                                       

nnUNetv2_apply_postprocessing -i C:/Users/Ai/Desktop/project/Deeplearning/nnUNet/nnUNetFrame/nnUNet_results/BraTS2021_3d_ensemble -o C:/Users/Ai/Desktop/project/Deeplearning/nnUNet/nnUNetFram
e/nnUNet_results/BraTS2021_3d_ensemble_PP -pp_pkl_file C:\Users\Ai\Desktop\project\Deeplearning\nnUNet\nnUNetFrame\nnUNet_results\Dataset100_BraTS2021\nnUNetTrainer__nnUNetPlans__3d_fullres\crossval_results_folds_0_1_2_3_4\postprocessing.pkl -np 8 -plans_json C:\Users\Ai\Desktop\project\Deeplearning\nnUNet\nnUNetFrame\nnUNet_results\Dataset100_BraTS2021\nnUNetTrainer__nnUNetPlans__3d_fullres\crossval_results_folds_0_1_2_3_4\plans.json

在对应nnUNet_results文件夹查看dice值,progress.png以及其他数据。
在这里插入图片描述

summary.json: 包含验证集的指标(如 Dice、Hausdorff 距离等)
在这里插入图片描述
prediction_n.nii.gz: 模型对验证集样本的预测结果,保存为 NIfTI 格式文件

如有其他心得后续继续更新

写在起起伏伏的2024年,能够为今年划上完美的句号
所有的前进都可能伴随着苦痛和弯曲
未来的道路也不会是阔野坦途
道之所在,虽千万人吾往矣
祝大家2025年新年快乐

### 将数据从nnUNet格式转换为nnUNetV2兼容格式 为了使现有数据集与nnUNetV2兼容,需遵循特定的文件命名约定和结构。这不仅涉及调整输入文件夹中的文件扩展名以匹配训练模型所使用的数据集格式[^2],还可能涉及到更新配置参数或脚本。 #### 准备工作环境 确保已安装最新版本的nnUNet框架及其依赖项。如果尚未设置开发环境,则建议通过Conda创建一个新的虚拟环境并安装必要的包[^4]。 ```bash conda create -n nnunet python=3.9 conda activate nnunet pip install nnunetv2 ``` #### 数据预处理步骤概述 对于每个病例的数据: - 图像文件应按照`<patient_id>_0000.nii.gz`, `<patient_id>_0001.nii.gz`...这样的模式命名。 - 对于分割标签,采用相同的基础名称加上后缀`_seg.nii.gz`来表示对应的标注信息。 例如,假设有一个病人的CT扫描图像以及相应的肿瘤区域标记,在原始nnUNet中可能是这样存储的: - `case_001_ct.nii.gz` - `case_001_label.nii.gz` 而在迁移到nnUNetV2时则需要重命名为: - `case_001_0000.nii.gz` (代表第一个模态) - `case_001_seg.nii.gz` (用于保存ground truth) 此外,还需注意的是,当使用2D网络架构进行实验时,应当确认所提供的数据确实是以二维切片的形式存在,并且这些切片已经过适当裁剪、归一化等前处理操作以便更好地适应算法需求[^1]。 最后一步是验证所有路径都指向正确的目录位置,并执行官方提供的工具来进行最终的一致性检查和必要修改。通常情况下,可以通过运行如下命令完成此过程: ```python from batchgenerators.utilities.file_and_folder_operations import join, isfile import shutil import os base_dir = 'path/to/your/dataset' output_dir = 'desired/output/path' if not os.path.exists(output_dir): os.makedirs(output_dir) for case in sorted(os.listdir(base_dir)): if "_ct." in case.lower(): new_name = case.replace("_ct.", "_0000.") src_file = join(base_dir, case) dst_file = join(output_dir, new_name) shutil.copy(src_file, dst_file) elif "_label." in case.lower(): new_name = case.replace("_label.", "_seg.") src_file = join(base_dir, case) dst_file = join(output_dir, new_name) shutil.copy(src_file, dst_file) ``` 这段Python代码片段展示了如何批量更正文件名为符合新标准的要求。当然也可以根据实际情况编写更适合自己的自动化脚本来简化这一流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值