nnUNet V2代码——图像增强(三)

本文阅读的nnU-Net V2图像增强有亮度调整对比度调整低分辨率调整

各个类内的各个函数的调用关系见前文nnUNet V2代码——图像增强(一)BasicTransform

安装batchgeneratorsv2,nnU-Net V2关于图像增强的代码都在这个库中,点击链接,将其clone到本地后,在命令行进入文件夹内,pip install -e . 即可(注意-e后有个点)。

一 MultiplicativeBrightnessTransform

该类包含亮度调整,继承自ImageOnlyTransform类,只对image施加,seg不施加。

代码在batchgeneratorsv2 \ transforms \ intensity \ brightness.py文件中

MultiplicativeBrightnessTransform代码比SpatialTransform类GaussianNoiseTransform类GaussianBlurTransform类简洁,但代码逻辑一致

1. __init__函数

定义必要的类内变量,代码清晰,不做粘贴,变量在用到时再介绍作用

2. get_parameters函数

def get_parameters(self, **data_dict) -> dict:
	## 获取image大小
    shape = data_dict['image'].shape
    ## 确定哪些通道要施加亮度调整
    ### self.p_per_channel = 1,nnU-Net V2对每个通道都施加亮度调整
    apply_to_channel = torch.where(torch.rand(shape[0]) < self.p_per_channel)[0]
    ## 各通道同步施加相同的亮度调整
    if self.synchronize_channels:
        multipliers = torch.Tensor([sample_scalar(self.multiplier_range, image=data_dict['image'], channel=None)] * len(apply_to_channel))
    ## 各通道各自施加自己的亮度调整
    else:	### self.synchronize_channels = False,nnU-Net V2不同步施加
        multipliers = torch.Tensor([sample_scalar(self.multiplier_range, image=data_dict['image'], channel=c) for c in apply_to_channel])
    ## 收集参数后返回
    return {
        'apply_to_channel': apply_to_channel,
        'multipliers': multipliers
    }

sample_scalar函数见nnUNet V2代码——图像增强(一)其余函数

3. _apply_to_image函数

def _apply_to_image(self, img: torch.Tensor, **params) -> torch.Tensor:
	## 没有要调整的通道,直接返回
    if len(params['apply_to_channel']) == 0:
        return img
    ## 遍历施加亮度调整
    for c, m in zip(params['apply_to_channel'], params['multipliers']):
        img[c] *= m
    return img

二 ContrastTransform

该类负责对比度调整,继承自ImageOnlyTransform类,只对image施加,seg不施加。

代码在batchgeneratorsv2 \ transforms \ intensity \ contrast.py文件中

1. __init__函数

定义必要的类内变量,代码清晰,不做粘贴,变量在用到时再介绍作用

2. get_parameters函数

MultiplicativeBrightnessTransform的get_parameters函数一致

3. _apply_to_image函数

def _apply_to_image(self, img: torch.Tensor, **params) -> torch.Tensor:
    if len(params['apply_to_channel']) == 0:
        return img
    ## 遍历通道
    for i in range(len(params['apply_to_channel'])):
        c = params['apply_to_channel'][i]
        ## 获取图像某一通道的平均值
        mean = img[c].mean()
        ## 是否保留数值范围,nnU-Net V2设置self.preserve_range = True
        if self.preserve_range:
            minm = img[c].min()
            maxm = img[c].max()

        ## 对比度调整
        img[c] -= mean
        img[c] *= params['multipliers'][i]
        img[c] += mean
		
		## 是否保留数值范围
        if self.preserve_range:
            img[c].clamp_(minm, maxm)

    return img

三 SimulateLowResolutionTransform类

该类负责施加低分辨率,继承自ImageOnlyTransform类,只对image施加,seg不施加。

代码在batchgeneratorsv2 \ transforms \ spatial\ low_resolution.py文件中

1. __init__函数

变量名称含义
self.scale图像放缩范围
self.synchronize_channels通道是否施加相同的低分辨率处理
self.synchronize_axes是否施加相同的低分辨率处理
self.ignore_axes某轴不能施加低分辨率处理,与nnUNet V2代码——图像增强(一)Convert3DTo2DTransformConvert2DTo3DTransform有关
self.allowed_channels可能会施加低分辨率处理的通道
self.p_per_channel某通道施加低分辨率处理的概率
self.upmodes各维度采样方法
self.scale = scale
self.synchronize_channels = synchronize_channels
self.synchronize_axes = synchronize_axes
self.ignore_axes = ignore_axes
self.allowed_channels = allowed_channels
self.p_per_channel = p_per_channel

self.upmodes = {
    1: 'linear',
    2: 'bilinear',
    3: 'trilinear'
}

2. get_parameters函数

def get_parameters(self, **data_dict) -> dict:
    shape = data_dict['image'].shape
    ## nnU-Net V2设置为None,所有通道按概率施加低分辨率处理
    if self.allowed_channels is None:
        apply_to_channel = torch.where(torch.rand(shape[0]) < self.p_per_channel)[0]
    else:
        apply_to_channel = [i for i in self.allowed_channels if torch.rand(1) < self.p_per_channel]
    ## nnU-Net V2设置为False
    if self.synchronize_channels:
        ## nnU-Net V2设置为True
        if self.synchronize_axes:
            ## 各通道、各轴施加相同的
            scales = torch.Tensor([[sample_scalar(self.scale, image=data_dict['image'], channel=None, dim=None)] * (len(shape) - 1)] * len(apply_to_channel))
        else:
        	## 各通道施加相同的,各轴施加各自的
            scales = torch.Tensor([[sample_scalar(self.scale, image=data_dict['image'], channel=None, dim=d) for d in range(len(shape) - 1)]] * len(apply_to_channel))
    else:
        if self.synchronize_axes:
        	## 各轴施加相同的,各通道施加各自的
            scales = torch.Tensor([[sample_scalar(self.scale, image=data_dict['image'], channel=c, dim=None)]  * (len(shape) - 1) for c in apply_to_channel])
        else:
        	## 各通道、各轴施加各自的
            scales = torch.Tensor([[sample_scalar(self.scale, image=data_dict['image'], channel=c, dim=d) for d in range(len(shape) - 1)] for c in apply_to_channel])
    ## 对忽略的轴单独处理
    if len(scales) > 0:
        scales[:, self.ignore_axes] = 1
    return {
        'apply_to_channel': apply_to_channel,
        'scales': scales
    }

3. _apply_to_image函数

def _apply_to_image(self, img: torch.Tensor, **params) -> torch.Tensor:
    orig_shape = img.shape[1:]
    # 注释机翻:我们无法对这些内容进行批处理,因为每个通道的下采样 SHAP 值会有所不同。
    for c, s in zip(params['apply_to_channel'], params['scales']):
        ## 按照放缩尺度确定下采样后的图像大小
        new_shape = [round(i * j.item()) for i, j in zip(orig_shape, s)]
        ## 使用某一种最近邻插值进行采样
        downsampled = interpolate(img[c][None, None], new_shape, mode='nearest-exact')
        ## 还原图像大小
        img[c] = interpolate(downsampled, orig_shape, mode=self.upmodes[img.ndim - 1])[0, 0]
    return img
### nnUNet v2 使用指南 #### 环境配置 为了顺利运行nnUNet v2,需先创建并激活Python虚拟环境。推荐使用`conda`来管理依赖项。安装必要的库可以通过以下命令完成: ```bash conda create -n nnunet python=3.8 -y conda activate nnunet pip install --upgrade pip setuptools wheel pip install torch torchvision torchaudio pip install -r requirements.txt ``` 上述操作确保了环境中包含了所有必需的软件包[^1]。 #### 数据准备 对于自定义数据集,在遵循特定目录结构的同时,还需转换成nnUNet认可的数据格式。这通常涉及将原始图像和标签重采样至统一尺寸,并保存为NIfTI文件(.nii.gz)。官方提供了脚本帮助自动化此过程: ```python from batchgenerators.utilities.file_and_folder_operations import * import shutil import os def convert_to_nifti(input_dir, output_dir): maybe_mkdir_p(output_dir) patients = subdirs(input_dir, join=False) for p in patients: patient_dir = join(input_dir, p) t1_file = join(patient_dir, 'T1.nii') label_file = join(patient_dir, 'label.nii') assert all([ isfile(t1_file), isfile(label_file)]), f"Some files are missing for patient {p}" # Convert and save as .nii.gz format shutil.copy(t1_file, join(output_dir, p + "_0000.nii.gz")) shutil.copy(label_file, join(output_dir, p + ".nii.gz")) convert_to_nifti('/path/to/raw_data', '/path/to/preprocessed_data') ``` 这段代码展示了如何批量处理患者级别的医学影像资料,将其转化为适合模型输入的形式。 #### 训练流程启动 一旦完成了前期准备工作,则可以着手于实际训练阶段。通过调整参数设置以及指定使用的GPU设备编号,可以在本地机器上轻松发起一次完整的实验周期: ```bash export CUDA_VISIBLE_DEVICES=0,1,2,3 nnUNet_train 3d_fullres Task503_Brats2021 1 -f 0 1 2 3 --npz ``` 这里假设任务ID为Task503_Brats2021,折数(fold)设定了四个独立子集用于交叉验证测试;而最后附加选项--npz指示程序保留预测期间产生的中间特征图谱以便后续分析之用。 #### 自定义训练器类 如果希望进一步优化默认行为或是引入新的特性功能,那么修改位于`/home/user/nnUNet/nnunetv2/training/nnUNetTrainer`下的`nnUNetTrainer.py`是一个不错的选择。比如增加早停机制(Early Stopping),或者更改损失函数形式等高级定制化需求均能在此实现[^2]: ```python class CustomNNUnetTrainer(NNUNetTrainer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.patience = 20 # Number of epochs with no improvement after which training will be stopped. self.best_loss = None self.counter = 0 def on_epoch_end(self): current_val_loss = self.epoch_losses_val[-1] if not isinstance(current_val_loss, (int,float)): raise ValueError('Validation loss must be a number.') if self.best_loss is None or current_val_loss < self.best_loss: self.best_loss = current_val_loss self.counter = 0 elif current_val_loss >= self.best_loss: self.counter += 1 if self.counter >= self.patience: print("Early stopping triggered.") self.stop_training_flag = True CustomNNUnetTrainer().run() ``` 该段落介绍了继承原有训练框架的基础上新增加了一个简单的早期停止策略,当连续多个epoch验证集上的表现未见提升时自动终止迭代进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值