ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

医学图像分割实战:大脑肿瘤2分割的数据集处理与可视化

医学图像分割实战:大脑肿瘤2分割的数据集处理与可视化 简介这份医学图像分割数据集面向需要大脑肿瘤Tumor分割训练数据的算法工程师与科研人员提供416×416分辨率的人脑MRI图像及对应的二分类掩码标签背景简洁、前景标注完整适合快速搭建图像分割模型并验证算法效果。数据集划分清晰训练集包含1632张图像和1632张Mask测试集包含240张图像和240张Mask除数据外附带一个Python可视化脚本可随机抽取样本并并排展示原图、真实分割标签以及标签叠加在原图上的效果便于直观核对标注质量。整个资源压缩包共2000个文件以PNG图像1759个和JPG原图240个为主另含1个可视化脚本7z压缩包大小为48.17MB轻量易下载。已有1879人学习使用适合做医学图像分割入门实践、模型训练数据扩充或分割结果评测。1. 大脑肿瘤2分割数据集和可视化代码才是真门槛把“医学图像分割”从论文标题变成一个能跑的实验卡住的往往不是模型而是数据怎么读、标签怎么映射、结果怎么看。大脑肿瘤分割这几年基本是BraTS系列数据集的事实标准但BraTS原生标签是4类坏死区、水肿、增强肿瘤和背景。标题里写的“2分割”也就是把前景压成一个肿瘤类背景算另一类。很多公开代码和预训练模型也都是按这个简化方案组织的。这篇不打算复读“医学图像分割很重要”这类铺垫直接讲清楚三件事大脑肿瘤2分割的数据集怎么组织类别标签怎么从多类合并成2类才合理以及所谓“可视化代码”到底能验证什么。数据集部分会以BraTS的NIfTI格式为例因为这是做脑肿瘤分割最可能碰到的落地形态但合并标签和可视化逻辑可以平移到任意医学影像分割任务。适合想快速拿一套数据、跑通训练前所有前处理步骤、并且能肉眼确认数据质量的人。2. 数据集结构NIfTI文件里的图像和mask怎么对应2.1 四模态MRI和“2分割”的真实含义大脑肿瘤分割数据集的常见组成是每个患者一个文件夹里面包含四个MRI序列的NIfTI文件以及一个分割标签文件。四个序列分别是T1加权、T1对比增强、T2加权和FLAIR各位用t1、t1ce、t2、flair这类后缀命名。不同机构的数据集命名不完全一致但只要掌握了_t1.nii.gz和_seg.nii.gz这种模式就可以按矩阵下标对齐。先看“2分割”这个说法。BraTS原始标签的数值含义通常在官方文档里定义得很清楚标签0是背景标签1是坏死和非增强肿瘤核心区标签2是瘤周水肿标签4是增强肿瘤核心。这里的“4”不是连续编号而是历史编码遗留。2分割的做法是把1、2、4合并成同一个前景类标签值置为1背景置为0。有些方案还会只取1和4合并成“肿瘤核心区”去掉水肿这时水肿被当成背景处理。合并策略取决于临床问题不要无脑把全部非背景类都并进去。如果目标是做脑肿瘤检测或者粗筛全前景合并是首选如果目标是评估手术预后或者放疗靶区勾画水肿和增强核分开往往更有意义。把一个4类问题改成2类问题表面上是简化标签实际上改变了分割目标的语义边界。对“大脑肿瘤分割2分割”这类数据集产品来说全前景合并最通用也最容易验证模型在二分类分割上的Dice指标。2.2 NIfTI读取和标签映射的表用Python读NIfTI最稳的组合是SimpleITK或nibabel。前者在医学图像处理管道里更常见后者在深度学习工具链中引用更多。两者返回的特征略有不同但都能拿到三维数组注意不要直接用numpy.load去读文件NIfTI本质是headerraw data的结构直接load会漏掉仿射矩阵和方向信息。标签合并的具体做法是先读取分割文件取出numpy数组然后做一次条件替换。核心逻辑import SimpleITK as sitk seg_path BraTS2021_BraTS_00000_seg.nii.gz seg_img sitk.ReadImage(seg_path, sitk.sitkUInt8) seg sitk.GetArrayFromImage(seg_img) # 形状 (D, H, W)注意NIfTI的axis顺序 # 把BraTS原标签1/2/4合并成前景1背景0 binary_label seg.copy() binary_label[binary_label 0] 1 print(原始标签分布:, {v: (seg v).sum() for v in [0, 1, 2, 4]}) print(二值标签前景体素数:, (binary_label 1).sum())这段代码里有个隐藏细节GetArrayFromImage返回的数组维度顺序是z、y、x而NIfTI文件中记录的仿射矩阵定义的是体素坐标到解剖坐标的映射。绝大多数深度学习框架处理3D医学影像时默认使用这种z-first布局因为MRI扫描的层间分辨率通常低于层内分辨率沿z轴切2D切片是常见做法。类别标签分布统计必须放在合并之前做目的是确认这个数据集里各类占比是否合理。BraTS 2021的训练集单样本类别的体素数差异能达到几百倍到上千倍水肿通常是体素数最多的肿瘤成分。合并成2类之后前景和背景的不平衡会缩小一些但仍然可能存在几十比一的比例后文会讲怎么处理。2.3 数据集划分时的常见错误拿到数据集后不要急着训练先做split。很多初学者直接按患者列表顺序切出训练集和验证集这在跨中心采集的医学数据上容易引入站点偏差。比如数据是按医院或采集年份排列的顺序切分可能导致训练集和验证集来自不同的扫描仪Dice虚高或虚低都不稳定。常见做法是先把患者ID列表shuffle再按8:1:1或类似比例划分三个子集同时保证同一个患者的所有模态和标签只出现在一个子集中。提示绝对不要对同一个患者既做训练又做验证。医学图像分割里患者级别的数据泄漏比同图多轮增强的问题严重得多会让测试指标失去参考意义。大类别的划分方式建议保存成CSV或JSON文件留档。很多可视化代码需要这个映射关系来判断哪些切片来自训练集。下面这个numpy的split代码可以复用到多数3D分割数据import numpy as np import json patient_ids [fBraTS2021_{i:05d} for i in range(100)] rng np.random.default_rng(42) rng.shuffle(patient_ids) n_train int(len(patient_ids) * 0.8) n_val (len(patient_ids) - n_train) // 2 splits { train: sorted(patient_ids[:n_train]), val: sorted(patient_ids[n_train:n_train n_val]), test: sorted(patient_ids[n_train n_val:]) } with open(dataset_split.json, w) as f: json.dump(splits, f, indent2) print({k: len(v) for k, v in splits.items()})这里显式设置random_seed42是实验可复现的关键。医学图像分割论文里经常看到“用相同split的测试集进行对比”如果split逻辑里没有固定种子复现别人结果基本不可能。另外JSON里存储的是患者ID列表而不是文件路径好处是下次更换数据根目录时split文件不用重生成。3. 预处理与2D切片训练管线搭建3.1 窗口截断、归一化和重采样顺序大脑MRI数值范围常因扫描设备和序列参数差异错开几个量级直接喂给网络会让训练不稳定。做法是先把每个模态的像素值做百分位截断把1%到99%分位之间的像素保留下来低于和高于的都截到边界再做z-score标准化。严格按官方定义z-score计算公式是(x - mean) / std这里的mean和std是截断后有效像素的统计量不是全图的。之所以强调这一点是因为全图统计会把头部之外的空气区域计入导致前景对比度被稀释。关于是否重采样BraTS系列数据一般已经被插值到统一尺寸例如1mm各向同性分辨率每个样本的数组shape大致相同。但自建数据集或者从其他项目下载的数据不一定做过这步。如果spacing差异明显建议先用SimpleITK.Resample统一到目标分辨率否则后续做2D切片时会发现层厚对应的解剖位置在样本间不一致。常见做法是先统一到1.0×1.0×1.0mm再决定是存成预处理的numpy数组还是在训练时即时处理。前者省训练时间后者省磁盘空间看项目规模选。3.2 从全脑volume生成有意义的切片数据集训练2D分割网络时如果直接从155层中随机抽切片会有大量切片完全落在肿瘤区之外给训练带来无效样本。关键在于先定位标签中有前景的z轴范围再在肿瘤存在的区间内采样同时保留少量无肿瘤切片作为负样本。更保险的做法是计算每张切片的肿瘤面积过滤掉面积过小的切片因为小于几十个像素的target在网络下采样过程中很容易被池化掉。一个可执行的预处理流程可以参照这个结构project/ ├── preprocess.py ├── dataset.py └── visualize.pypreprocess.py负责读原始NIfTI、截断、标准化、过滤无前景切片并保存为npz文件。dataset.py重写PyTorch的Dataset类从npz里按索引读取单个切片。visualize.py则是后文要讲的可视化脚本。这种结构的好处是训练前把脏活集中做完训练代码只关心张量流调bug时定位也更快。3.3 切片级数据增强要照顾医学语义自然图像分割常用的随机旋转、翻转、裁切可以直接用在MRI切片上但要注意轴向翻转的含义。大脑MRI的左右翻转在解剖上是合法的上下翻转则不符合常规读取方向。如果使用了前面提到的z-first数组做2D切片增强时默认是在H和W维上操作不要把D维卷进去。增强强度也不必过大。医学图像分割翻车最常见的问题之一是增强产生了标签错位比如裁切后没有同步裁切mask或者旋转时用了不兼容的插值方式。推荐使用albumentations库它的Compose同时对image和mask做变换并且旋转参数用border_modecv2.BORDER_CONSTANT补零。import albumentations as A import numpy as np # H, W格式的切片和对应二值mask做同步增强 transform A.Compose([ A.RandomRotate90(p0.5), A.HorizontalFlip(p0.5), A.ShiftScaleRotate(shift_limit0.05, scale_limit0.1, rotate_limit15, border_mode0, p0.3), A.RandomBrightnessContrast(brightness_limit0.1, contrast_limit0.1, p0.3) ]) def augment_slice(image, mask): if image.ndim 2: image image[..., None] result transform(imageimage, maskmask) return result[image].squeeze(), result[mask]这段代码的增强顺序是按代价从低到高排列的。前置的翻转和旋转几乎无成本后面的亮度对比度调整模拟不同扫描参数带来的信号差异。注意ShiftScaleRotate的border_mode0也就是补零避免平移时在边界产生不存在的组织信号。3.4 类别不平衡处理的三个层面2分割虽然只有两个类别但肿瘤区域在整张切片里占比经常小于5%这时候不能只用Dice Loss。常见处理方式按粒度分层损失函数层使用Dice Loss与Focal Loss的组合Dice Loss缓解整体不平衡Focal Loss降低易分样本的梯度权重。采样层按前景面积比例加权抽样前景占比低于某个阈值的切片少抽一点。后处理层对预测概率图做条件随机场或取连通域过滤消除孤立预测点。三个层面不需要全上先加损失函数层面的约束再根据验证集的表现决定要不要动采样策略。推荐初学者用一个简单公式总损失 0.5 × Dice Loss 0.5 × Focal Loss(γ2)。这个组合在多数医学影像分割任务里足够作为基线。4. 可视化代码从切片叠加到动态浏览4.1 单张切片叠加mask的最短实现可视化不是训练完才做的事数据准备阶段就要反复看看原始图、看标签、看预处理结果。最基础的是把MRI切片和标签mask做半透明叠加下面代码用matplotlib实现并标注当前切片对应的z轴索引import matplotlib.pyplot as plt import numpy as np def show_slice(image, mask, slice_idx, save_pathNone): # image形状(H,W)mask取0/1 fig, axes plt.subplots(1, 3, figsize(12, 4)) axes[0].imshow(image, cmapgray) axes[0].set_title(fMRI slice {slice_idx}) axes[1].imshow(mask, cmapgray) axes[1].set_title(Label) axes[2].imshow(image, cmapgray) axes[2].imshow(mask, cmapjet, alpha0.5) axes[2].set_title(Overlay) for ax in axes: ax.axis(off) if save_path: fig.savefig(save_path, dpi150, bbox_inchestight) plt.close(fig)从这里能很容易发现两类问题。一类是预处理后的图像灰度范围是否符合预期如果截断后的图像看起来整体过黑或过白说明百分位截断的参数不合适。另一类是mask边缘是否平滑如果标签在解剖边界上出现锯齿状或碎片状区域说明原始数据标注粗糙或合并策略里包含了体积过小的噪声标签。4.2 生成gif做全脑三维浏览单张切片只能看到局部规范的验证方式是沿z轴滑动浏览整个volume。生成gif文件成本很低却不引入额外的交互依赖。下面代码先把三维数组拆成若干切片再用imageio合成gif并设定每帧间隔时间import imageio def volume_to_gif(volume, mask, output_pathslice_view.gif, fps8): frames [] for i in range(volume.shape[0]): fig, ax plt.subplots(1, 1, figsize(4, 4)) ax.imshow(volume[i], cmapgray) ax.imshow(mask[i], cmapjet, alpha0.5) ax.set_title(fz{i}) ax.axis(off) fig.canvas.draw() frame np.array(fig.canvas.renderer.buffer_rgba())[:, :, :3] frames.append(frame) plt.close(fig) imageio.mimsave(output_path, frames, duration1000/fps)生成gif后重点观察三个位置肿瘤最大层面、肿瘤从出现到消失的边缘层面、相邻层面之间mask有无突变。如果mask在某一层突然从很大变成几乎没有很可能是在预处理阶段丢失了切片信息。还有一点值得注意gif帧数不宜太多155层全部导出来会变成一段很长的视频习惯上每隔1到3层采样一帧就够了。4.3 可视化预测结果时必须对齐坐标空间训练好模型后把预测mask叠加到原始图上需要通过同一套仿射矩阵完成重采样。很多“高分可视化”翻车是因为预测是在预处理后的数组空间里做的又直接把结果当成原始图像空间来展示造成器官位置偏移几毫米到几厘米。简单说如果一个模型训练时输入是重采样后的1mm各向同性数据预测结果要和原始NIfTI坐标对齐就用SimpleITK.Resample把预测图像重采样回原始spacing和方向。验证坐标对齐的一种直观办法是提取预测mask和真实mask的质心距离。两个mask质心在x、y、z三个方向上偏差小于一个体素基本可以排除空间错位问题如果距离明显偏大先检查是否把itk.ReadImage出来的顺序弄反了。5. 评估指标与2D/3D方案的收敛判断5.1 Dice之外还要看哪些指标二分类分割的评估不能只看Dice。Dice对前景面积小的目标比较敏感当肿瘤体素很少时模型即使只预测出一小片区域也能拿高分。另一个常用指标是Hausdorff距离它衡量两个边界表面之间的最大不匹配程度对边缘分割质量更敏感。Brain tumor dataset的标准评估通常同时给出Dice和HD9595%分位的Hausdorff距离这在BraTS官方评测里很常见。各指标的含义与适用场景可以简化成下面这样指标度量对象对什么敏感建议场景Dice体素重叠率区域整体匹配程度快速对比模型好坏基线必看IoU交并比重叠区域的保守程度与Dice相关性高但数值更严苛HD95表面距离最差情况下的边界偏差肿瘤边缘评估对孤立预测点惩罚大Precision预测为正的样本中真正比例假阳性临床筛查更关注这个Recall真实肿瘤被检出的比例假阴性漏检更致命的场景更关注这个Dice和IoU的逻辑关系是Dice 2×IoU / (1IoU)两个指标本质一致。实操中如果Dice高但HD95很高说明大部分区域预测对了但某一块边界偏离很远这种结果在图像引导手术场景里是不能接受的。5.2 Dice Loss实现时的平滑项取值训练2分割模型时如果直接用PyTorch的nn.Module写Dice Loss要注意平滑系数epsilon对梯度的影响。epsilon过大时损失被稀释过小时在标签全零的切面上会出现除零问题。常用取法是1e-6到1e-5之间。一个稳定实现import torch import torch.nn as nn class BinaryDiceLoss(nn.Module): def __init__(self, smooth1e-5): super().__init__() self.smooth smooth def forward(self, pred, target): # pred为概率值target为0/1二值标签 pred pred.contiguous().view(pred.size(0), -1) target target.contiguous().view(target.size(0), -1) intersection (pred * target).sum(dim1) union pred.sum(dim1) target.sum(dim1) dice (2.0 * intersection self.smooth) / (union self.smooth) return 1.0 - dice.mean()这里按batch内每个样本独立计算Dice再取平均而不是把整个batch合并成一个大矩阵。两种做法在样本间前景面积差异大的时候结果差别明显按样本平均能让体积小的肿瘤样本获得梯度。5.3 2D切片与3D体积模型的收敛判断标准在训练早期先用2D切片模型跑通流程是最快的验证方式。2D模型的Dice一般能做到0.85到0.9这个区间但会面临切片间预测不连续的问题比如同一个肿瘤在相邻z轴上mask边缘明显震荡。解决这个问题有两条路一是后处理阶段用形态学闭运算做三维平滑二是换用3D U-Net这类直接输入整块volume的模型。3D模型对GPU显存要求高一个常用输入patch是128×128×128不需要整块拉进去。判断是否值得从2D升级到3D看三个信号2D模型在测试集上是否已经把边缘误差降到可接受范围、预测mask的棋盘格效应是否明显、推理延迟允许范围内能不能塞下足够多的3D patch。如果3D模型的Dice只比2D高0.01但要求显存翻倍实际工程里不见得划算。另一种快速验证方法是做“切片级 bias 检查”把测试集预测结果分成肿瘤面积大和肿瘤面积小两组分别计算Dice。如果大病灶组Dice很高而小病灶组掉得厉害说明模型对边界细节欠拟合优先调损失函数或增加切片采样次数而不是盲目加网络层级。最后落到一个实用的验收技巧准备好3到5个可视化的失败样本标注出预测mask在解剖结构上“过度渗透”或“欠分割”的部分配合HD95和Recall决定是否进一轮训练。数据、标签和可视化代码整套串起来以后这个验收流程每次训练都能复用。本文还有配套的精品资源点击获取
返回列表