共计 2839 个字符,预计需要花费 8 分钟才能阅读完成。
背景与痛点
医学影像分析中,3D 图像(如 CT、MRI)与传统 2D 图像处理有显著差异。3D 图像包含连续的切片信息,能提供更完整的解剖结构,但同时也带来了新的挑战。

- 数据特性差异:
- 3D 图像由体素(voxel)构成,而 2D 图像由像素(pixel)构成。处理 3D 数据时,需要考虑空间连续性,这对计算资源提出了更高要求。
-
医学影像通常具有高分辨率,单个体积数据可能达到 512x512x300 体素,显存占用极大。
-
数据获取困难:
- 医学影像标注依赖专业医生,标注成本高昂,导致公开数据集稀少。
-
小样本问题普遍存在,尤其是罕见病种,可能仅有几十例数据可用。
-
类别不平衡:
- 目标区域(如肿瘤)可能只占图像的极小部分,导致模型容易偏向背景预测。
- 例如,在脑肿瘤分割中,肿瘤区域占比可能不足 1%。
技术栈对比
- 2D vs 3D 卷积:
- 2D 卷积核(kernel)仅在长宽上滑动,而 3D 卷积增加深度维度,能捕捉空间信息但计算量立方增长。
-
显存占用示例:输入 128x128x128,3D 卷积层显存消耗是 2D 的 128 倍。
-
模型架构选择:
- nnUNet:自动化超参数调整,适合快速实验。
- V-Net:使用残差连接,擅长处理前列腺等小器官分割。
- 单模态 vs 多模态:MRI-T1/T2 双模态融合可提升脑肿瘤分割精度约 5%。
实战代码模块
数据加载与预处理
import SimpleITK as sitk
import numpy as np
# 加载 NIfTI 文件
def load_nii(path):
img = sitk.ReadImage(path)
data = sitk.GetArrayFromImage(img) # 转为 numpy 数组 (D,H,W)
return np.transpose(data, (2,1,0)) # 调整轴顺序为 (H,W,D)
# 窗宽窗位调整 (CT 图像常用)
def window_ct(data, window_level=40, window_width=80):
min_val = window_level - window_width//2
max_val = window_level + window_width//2
data = np.clip(data, min_val, max_val)
return (data - min_val) / (max_val - min_val)
3D 数据增强
import torchio as tio
transform = tio.Compose([tio.RandomFlip(axes=(0,1,2), p=0.5), # 随机翻转
tio.RandomAffine(scales=(0.9,1.1), degrees=10), # 随机仿射变换
tio.RandomElasticDeformation(num_control_points=7), # 弹性变形
])
# 使用示例
subject = tio.Subject(image=tio.ScalarImage('image.nii.gz'),
label=tio.LabelMap('mask.nii.gz')
)
augmented = transform(subject) # 自动同步处理图像和标签
3D U-Net 实现
import torch
import torch.nn as nn
class DoubleConv(nn.Module):
"""(卷积 => [BN] => ReLU) x 2"""
def __init__(self, in_ch, out_ch):
super().__init__()
self.double_conv = nn.Sequential(nn.Conv3d(in_ch, out_ch, kernel_size=3, padding=1),
nn.BatchNorm3d(out_ch),
nn.ReLU(inplace=True),
nn.Conv3d(out_ch, out_ch, kernel_size=3, padding=1),
nn.BatchNorm3d(out_ch),
nn.ReLU(inplace=True)
)
class UNet3D(nn.Module):
def __init__(self, in_ch=1, out_ch=1):
super().__init__()
# 编码器 (下采样)
self.enc1 = DoubleConv(in_ch, 64)
self.pool1 = nn.MaxPool3d(2)
# 解码器 (上采样使用转置卷积)
self.up4 = nn.ConvTranspose3d(128, 64, kernel_size=2, stride=2)
self.dec4 = DoubleConv(128, 64) # skip connection 拼接后通道数翻倍
# 输出层
self.outc = nn.Conv3d(64, out_ch, kernel_size=1)
def forward(self, x):
# 完整 forward 实现略...
return self.outc(x)
模型优化技巧
- 组合损失函数:
def dice_loss(pred, target, smooth=1e-5):
intersection = (pred * target).sum()
return 1 - (2. * intersection + smooth) / (pred.sum() + target.sum() + smooth)
class DiceFocalLoss(nn.Module):
def __init__(self, gamma=2):
super().__init__()
self.focal = FocalLoss(gamma)
def forward(self, pred, target):
return dice_loss(pred, target) + self.focal(pred, target)
- 测试时增强(TTA):
- 对同一图像进行多次增强(如旋转 90°/180°/270°)
-
将各增强版本的预测结果逆变换后取平均
-
Monai 加速技巧:
- 使用
CacheDataset缓存预处理结果 - 开启
amp自动混合精度训练
避坑指南
- 显存不足解决方案:
- Patch 训练:将大体积切分为 64x64x64 的小块
-
梯度累积:每 4 个 batch 更新一次参数
-
跨设备数据差异:
- 使用
N4 偏置场校正消除 MRI 扫描仪差异 -
对 CT 值进行标准 HU 单位校准
-
标注噪声处理:
- 训练时随机擦除部分标注区域
- 使用 Label Smoothing 技术
公开数据集示例
加载 TCIA 肺癌数据集:
from radiomics import imageoperations
# 从 DICOM 序列构建 3D 体积
dicom_paths = sorted(glob('LIDC-IDRI-0001/CT/*.dcm'))
image = imageoperations.load_dicom_series(dicom_paths)[0] # 返回 SimpleITK 图像
下一步学习路径
- 进阶:尝试 nnUNet 的自动化流程,体验其超参数搜索策略
- 前沿:研究 TransBTS 等基于 Transformer 的 3D 分割架构
- 扩展:探索多模态融合在 PET-CT 联合分割中的应用
正文完
发表至: 未分类
近两天内
