ARTICLE DETAIL

资讯详情

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

超声腹部器官图像分割数据集:构建流程与训练实践

超声腹部器官图像分割数据集:构建流程与训练实践 简介本资源是面向医学图像分析初学者与深度学习实践者的超声腹部器官二分类分割数据集专为训练和验证U-Net等分割模型设计适用于智能辅助诊断、器官定位与教学实验等场景。数据集共4600张配对样本分为训练集约3700张与测试集约900张每张超声图像均附带精确标注的mask文件类别明确划分为背景与腹部器官两类压缩包内含1998张PNG格式图像及mask、1个说明classes的TXT文档、1个可视化Python脚本总大小163.36MB结构清晰开箱即用。已有397人学习下载体现其在入门级医学影像项目中的实用热度。用户可直接加载训练/测试子目录进行模型训练并利用内置脚本快速可视化原始图、真值标签及叠加蒙版效果显著提升数据理解效率与调试便利性。 搞过超声图像分割的人应该都有同感找数据集是最让人头大的事。CT、MRI的公开数据集一抓一大把但超声图像因为成像机制特殊、图像质量参差公开的带标签数据少得可怜。很多时候只能在网上零星扒下来几百张质量还得不到保障训练出来的模型在真实场景里一测就是“见光死”。这次我把我自己整理的一套超声腹部器官图像分割数据集完整梳理了一遍约4600张图和对应的标签二类别分割也就是背景加腹部目标器官。文章的定位很明确如果你是刚入门医学图像分割、手头正好需要一份能直接训练的超声数据或者你想看看这类数据集的整理标准和标注细节这篇内容能让你少走不少弯路。我不光讲数据怎么用还会把采集、清洗、标注、质检、目录组织这些实操过程都摊开来说。1. 数据集定位为什么偏要做超声腹部器官分割1.1 医学图像分割里最费劲的“硬骨头”医学图像分割这几年热度一直很高很多人一听到这个话题第一个想到的就是UNet架构、iou分数、各种backbone的魔改。真正落过地的人清楚一个项目的成败往往在数据环节就定了一半。尤其是超声图像它本身的成像机制就决定了它跟CT、MRI不是一回事超声图像噪声重、分辨率低组织边界常常是渐变的灰度过渡加上探头角度、患者体型、呼吸运动都会让同一器官呈现出完全不同的形态。在这种图像上做分割模型要学的特征比CT要模糊得多而公开可用的带标签数据又特别稀缺。我这次整理的数据集就是在这样一个背景下来做的。选择了腹部这个部位主要是因为腹部超声检查是最常见、应用最广的超声检查类型肝脏、肾脏、胆囊、脾脏这些器官在体检和临床诊断里出现频率极高。做算法工程师或者相关方向的学生拿这份数据做模型验证、做算法比赛、做毕业设计都比用牙齿、眼科这类小众超声数据更贴近实际需求。还有一个考虑是腹部超声在分割任务里具备一定的挑战性。器官边界受呼吸影响会造成声影区不同深度组织的回声强度差异大很多器官的边缘在图像上其实是“靠经验猜”的。整个数据集如果能把这种复杂情况容纳进去模型的鲁棒性才能被真正逼出来。所以我并不追求把数据做成“白白净净”的简单任务而是让它保留超声图像的原始难度这样训练出来的模型才好在真实数据上迁移。1.2 2类别分割的设计逻辑背景加前景很多人可能会问腹部器官这么多为什么不做多类别分割我也想过多类别但实际标注时就会发现一个很现实的问题超声图像的器官边界经常不清晰如果做多类别标注员面对模糊区域时很难判断该归入哪一类标出来的标签一致性会大幅下降。而2类别分割即背景一个类别、目标腹部器官区域一个类别标注的一致性会高很多模型训练时也更容易收敛后续如果要做多类别可以把这份二值标签作为预训练基础再加新的标注类别去扩展。数据集的mask统一采用“黑色背景、白色前景”的编码方式前景像素值为255背景为0单通道8位PNG格式。这种格式几乎是所有分割框架的默认输入不管是PyTorch还是TensorFlow读取之后转成one-hot或者直接作为二类监督信号都可以。我特意不把前景区域细分为肝脏、肾脏之类的独立标签而是将当前批次关注的腹部目标器官统一归入前景类原因很简单在超声图像上很多相邻器官的边界在未经增强的B模式下难以精确区分强行细分类别容易造成标注错误反而降低标签的可用性。这里顺便提一下类别不平衡的问题。一张腹部超声图像中前景器官的面积通常只占整张图的10%~20%背景占多数。如果直接用普通的交叉熵损失函数训练模型很容易倾向于把全部像素预测成背景dice分数看起来还行但实际分割毫无意义。所以我一般在训练时会配合dice loss或者focal loss来缓解这个问题后文会给出具体的损失函数组合方案。1.3 4600张规模是怎么定下来的数据量定在约4600张这个规模经历了很长时间的试错。太少了不行超声图像形态差异大如果只有几百张模型根本无法覆盖不同探头的扫查角度、不同体型的组织形态太多了又有数据冗余问题同一患者连续帧之间只有细微差别全都标进去对提升模型泛化能力帮助有限反而浪费标注时间。最终保留约4600张有效图像是在剔除重复帧、模糊帧和严重遮挡帧之后得到的数量。这个规模对单人标注来说是可管理的对训练来说也足够我用UNet在这份数据上验证过Dice指数可以跑到0.88以上后续章节有具体结果继续增加数据量指标提升幅度已经非常小。如果是做迁移学习预训练模型加这一份数据微调效果就更明显了。所以4600张是一个性价比比较高的体量既不至于让训练时间过长又能保证模型见过足够多的形态变化。2. 图像来源与预处理流程2.1 从DICOM到PNG一个不能马虎的环节这套数据集的原始来源是超声设备采集的DICOM文件。DICOM格式保存的是设备原始的像素矩阵和元数据直接拿来做深度学习训练不太方便大部分框架还是更习惯读PNG、JPEG这类通用格式。但在转的过程中有个容易踩坑的地方DICOM里的像素值范围和超声图像显示的灰度范围不是一回事如果直接取原始值另存为PNG可能出现图像过暗或者过亮的情况因为超声设备在显示时是做过后处理的。我统一采用的转换逻辑很简单先读取DICOM的像素数组如果数组类型不是uint8就做一次线性归一化将像素值映射到0到255区间再存为8位灰度PNG。实际的转换代码长这样import pydicom import cv2 import numpy as np ds pydicom.dcmread(raw/case_00001.dcm) img ds.pixel_array.astype(np.float32) img (img - img.min()) / (img.max() - img.min()) * 255.0 img img.astype(np.uint8) cv2.imwrite(images/case_00001.png, img)这里有一个细节值得注意有些DICOM文件里包含覆盖层(overlay)数据显示时会在图像上叠加一些设备生成的文字、刻度线或者测量标记这些标记在原始pixel_array里不一定会体现。我转换的时候只保留纯粹的超声灰度信息设备信息覆盖层一律不保留这样后续训练时就不会出现模型去学习图像角落的文字特征这种假阳性情况。2.2 图像清洗哪些图必须淘汰数据不是越多越好有些图留着反而会污染模型。我在做清洗时定了一套筛选标准这里分享出来供参考。第一类必须淘汰的是严重伪影图像。超声检查时如果探头与皮肤之间耦合不好或者患者肠道气体过多图像上会出现大片高亮区域或深色声影区器官结构被完全遮挡这种图即使强行标注标签的可靠性也不高。第二类是设备信息遮挡过重的图像比如屏幕上大面积显示患者姓名、医院名称、测量数据等尽管这些信息在转换成灰度图后可能已经弱化但如果遮挡了主要器官区域同样不能用。第三类是重复且无意义增量的连续帧。超声视频流的帧间信息本来就高度冗余同一患者同一个扫查位置上连续几帧图像几乎一样我会用帧差法做一个简单判断相邻帧之间像素差异小于阈值的只保留一张。在切分数据集时还有一条很重要的原则必须以患者为粒度进行划分不能按图像随机打乱。同一患者的多张图像之间存在高度相似性如果一部分图进了训练集、另一部分进了测试集测试集指标会虚高这种情况在学术界叫数据泄漏。我最终划分的时候先按患者分组再把整个组的病例按比例分到train、val、test中保证任何一个患者的图像不会同时出现在训练集和验证集里。2.3 预处理与数据增强策略图像统一处理成灰度图后尺寸也需要保持一致。原始超声图像长宽比各有不同有些是4:3有些是16:9直接resize到正方形会拉伸变形影响器官的形状特征。我采用了padding的方式将图像等比缩放到长边为640像素然后在短边两侧填充黑色最终得到一个640x640的正方形图。这样做的好处是既保留了器官的原始比例又方便模型输入固定尺寸。数据增强方面我试过很多组合最后固定下来的方案包括水平翻转概率0.5因为腹部超声左右对称性较好翻转后器官语义基本不变随机旋转±15度增强模型对探头角度的鲁棒性旋转时mask要同步旋转亮度对比度扰动超声图像受设备增益设置影响很大适当扰动能提升泛化能力弹性形变模拟探头施压时组织的轻微变形但形变幅度要控制在小范围防止器官形态失真模拟斑点噪声在图像上叠加乘性噪声让模型对超声特有的散斑效应更鲁棒这里要记住一个原则所有图像层面的增强都要保证mask和图像经过完全一致的几何变换。有的初学者会分别对image和mask做增强结果图像翻转了mask没翻训练出来的模型完全没法用。我自己写了一个统一的transform把所有几何操作都封装在同一个流程里避免出现这种低级错误。3. 标签制作与质量保障3.1 标注工具选型Labelme够用但需要二次转换分割标签的标注方式业内主要分为两种像素级涂抹和多边形轮廓标注。像素级涂抹适合边界清晰的图像直接拿画笔在器官区域涂色操作直观。但超声图像边界模糊纯涂抹方式容易让标注员凭感觉涂出毛糙的边缘而且效率很低。我采用的是多边形标注用Labelme这类工具沿着器官边界打点勾出轮廓最后再统一转成二值mask。Labelme的好处是上手快支持导出JSON格式里面记录的是多边形顶点的坐标这个JSON文件占了很小存储空间后续可以随时修改顶点位置而不需要重画整张图。坏处是它本身不直接输出分割mask需要自己写一段转换脚本把多边形填充成掩膜。我给读者提供一个最常用的转换思路先用json.load读取文件中的shapes字段取出多边形坐标点数组然后使用cv2.fillPoly在空白画布上填充生成单通道mask。import json import cv2 import numpy as np with open(labels/case_00001.json, r, encodingutf-8) as f: data json.load(f) mask np.zeros((720, 960), dtypenp.uint8) for shape in data[shapes]: points np.array(shape[points], dtypenp.int32) cv2.fillPoly(mask, [points], 255) cv2.imwrite(masks/case_00001.png, mask)坐标的尺寸对齐问题必须留心。有的超声图像原始分辨率是960x720我统一成640x640之后JSON里的多边形坐标还是基于原始分辨率的转换mask时一定要先用和图像相同的resize方式处理否则轮廓会错位。我在脚本里写了一个检查函数每转完一张就把原图和mask叠加显示一次肉眼确认器官轮廓是否贴合。3.2 标注规范边界模糊时到底怎么选超声图像最大的标注难点在于边界定义。像肝脏和肾脏之间、胆囊和周围组织之间经常是一团低回声区和高回声区的渐变过渡不同标注员打出的轮廓可能相差十来个像素。为了让标签保持一致我制定了三条标注规则在实际工作中也验证了有效性。第一条以低回声区域的边缘作为主要参考边界。超声图像中实质性器官通常表现为均匀的低回声和周边的强回声包膜或脂肪组织形成分界这个分界在多数图像上是能识别的只是有时对比度低。第二条当边界完全无法分辨时优先参考解剖学位置关系比如肝脏和右肾的边界即使图像上不清晰也可以通过两者相对位置推算出大致分界点。第三条对于形态极不规则、多切面连续扫查的图像以包含器官实质的最外层边界为准不把局部高回声斑点单独分割出去。标注规范必须在动手之前打印出来放在手边不要凭感觉。我第一版标注时同一个肝脏区域前10张和后10张的打点风格差异明显回头质检时不得不返工。后来我先把所有图像预览一遍对典型的正常形态和各类异常形态做到心中有数再统一开始标注一致性问题改善了很多。3.3 多轮质检每一张都过三道关数据集的标签质量直接决定了模型效果的上限。我给自己定了一个不容妥协的原则每一张标注都要经过至少三道检查后才能进入最终版。第一道是自动检查用脚本检查mask和图像尺寸是否一致、mask是否为空、是否只有一个连通域、前景面积占比是否在合理区间。这些检查可以一次性批量跑完快速筛掉明显的问题。第二道是人眼复核把标注后的mask以半透明方式叠加在原图上逐张观察轮廓贴合程度。这一步最耗时但也是最必要的。第三道是交叉检查找另一位有医学图像标注经验的人随机抽取10%的样本重新独立标注一遍计算两组标注的dice相似系数。如果随机抽检的dice低于0.90就说明标注规范执行不够到位需要整体复查。第二道检查还有一个重要的任务识别“边界偏移”。有时标注时打点稍少光滑的器官轮廓被画成了多边形的折线肉眼单看mask不觉得叠到原图上就能明显看出轮廓边缘比真实器官边界整体内缩或外扩了一圈。这类问题我不会通过膨胀腐蚀去修而是重新编辑多边形顶点。用形态学操作去补边界虽然视觉上平滑了但像素位置已经偏离了真实结构对训练来说是得不偿失的。4. 数据集目录结构与快速使用4.1 目录组织让“拿来即用”不是一句空话数据集整理成下面这个结构目的是减少使用者的迁移成本。images存放全部图像文件masks存放一一对应的mask文件train.txt、val.txt、test.txt则是三份图像文件名列表每行一个文件名不包含扩展名。这样划分信息保存在文本里你不想用我给的划分可以随时按自己的需求重新生成列表。ultrasound_abdomen/ ├── images/ │ ├── case_00001.png │ ├── case_00002.png │ └── ... ├── masks/ │ ├── case_00001.png │ ├── case_00002.png │ └── ... ├── train.txt ├── val.txt └── test.txt图像和mask的文件名一一对应比如images/case_00001.png对应的标签就是masks/case_00001.png。没有单独的lableme的JSON文件因为最终交付的应该是开箱即用的分割标签而不是让使用者再去踩一次多边形转mask的坑。你如果想做数据可视化、分析标签分布直接读取这两份PNG就够了。4.2 数据划分按病例划分不要按图片划分train.txt、val.txt、test.txt的默认比例是8:1:1。但这里我必须再强调一遍全部划分操作都是基于“患者级”来做的。也就是说同一个患者的所有图像只会出现在三个集合中的某一个绝不会拆分到两个集合里。这样做是为了防止图像级别的数据泄漏导致测试集指标虚高。一个值得注意的地方是腹部超声图像里可能存在多张连续帧形态几乎一样的图。即使按患者划分如果这些连续帧被分到训练集和验证集仍然可能出现验证集与训练集图像过于相似的问题。因此我在做划分前先做了去重处理把相似度超过阈值的帧删掉保证最终数据集中不同的图像确实代表了不同的扫查内容和角度。另外test.txt被严格隔离我在调参和做数据增强实验时都不会去查看test集的结果只以val集作为开发指标参考最后才在test集上跑一次最终评测这样得到的结果才有说服力。4.3 加载示例一段能直接跑的PyTorch数据类给出一份可以直接用的数据加载代码读取图像和mask后做前处理。这个类支持传入数据列表文件和transform函数可以用在训练、验证、测试三个环节。import os import cv2 import numpy as np import torch from torch.utils.data import Dataset class UltrasoundSegDataset(Dataset): def __init__(self, root_dir, split_file, transformNone): self.root_dir root_dir self.transform transform with open(split_file, r, encodingutf-8) as f: self.names [line.strip() for line in f if line.strip()] def __len__(self): return len(self.names) def __getitem__(self, idx): name self.names[idx] img_path os.path.join(self.root_dir, images, name .png) mask_path os.path.join(self.root_dir, masks, name .png) image cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) image np.expand_dims(image, axis2) mask (mask 127).astype(np.float32) if self.transform: augmented self.transform(imageimage, maskmask) image augmented[image] mask augmented[mask] else: image image.astype(np.float32) / 255.0 image np.transpose(image, (2, 0, 1)) mask np.expand_dims(mask, axis0) return ( torch.from_numpy(image).float(), torch.from_numpy(mask).float() )这里我默认用了albumentations库来做增强它最方便的地方在于可以通过相同的随机种子同步处理图像和mask几何变换不会出现错位。5. 训练效果与坑点汇总5.1 用UNet做baseline的实测结果数据整理出来后我第一时间用UNet跑了一次baseline确认数据本身没有把模型“带偏”。输入尺寸640x640batch size设为8优化器用AdamW初始学习率1e-4训练200个epoch并配合了余弦退火学习率策略。损失函数用的是DiceLoss和Binary Cross Entropy的组合比例设为7:3这样既保证区域重叠度又兼顾像素级分类精度。最终在val集上的结果Dice指数约0.881IoU约0.786。这个效果作为baseline是够用的如果你用DeepLabV3、UNet甚至在训练前加一个图像增强模块指标还能更高一点。但从我的实际测试来看这类数据上提升最快的方法不是改网络结构而是把损失函数从纯BCE换成配合dice loss的组合第一次切换后Dice就提升了接近5个百分点。有一个数据层面的建议如果想做小样本验证不要直接随机抽几百张训练。更合理的方案是先用全部数据预训练再在小样本上进行微调这样能最大程度发挥大规模数据带来的初始化优势。超声图像数据规模本来就不大放弃预训练等于浪费了之前标注的所有努力。5.2 训练时常见问题排查速查表我在训练过程中踩过不少坑把最典型的几个问题整理出来做成一张速查表方便你遇到问题直接对照排查。问题现象可能原因解决方法损失函数不下降学习率过大或过小输入像素值分布异常先固定学习率1e-4确认输入归一化到0~1预测结果全黑或全白模型偏向背景类别类别不平衡严重换用dice lossCE组合或增加focal loss器官边缘粗糙、锯齿明显标注边界本身不够平滑或是数据增强时mask被resize成了低分辨率检查质检环节增强时mask插值方式改为最近邻验证集指标高但测试集差按图像划分导致数据泄漏验证集分布和测试集差异大改用按患者进行划分训练中尽量不看test集结果图像和mask出现错位读取路径不对或者是resize/padding时图像和mask用了不同的变换统一transform保证图像和mask执行完全一致的几何变换同一患者图像在train和val中同时出现患者级划分没有做随机打乱后跨集合泄漏按患者ID分组划分确保同一患者的图只属于一个集合5.3 这个数据集后续还能怎么扩展如果你已经把这套数据集跑通了还想往深做我可以从实际操作角度给你指几个方向。第一个方向是类别扩展。以当前二值分割模型为初始权重额外标注胰腺、脾脏等器官的mask然后把输出层的通道数从2改到3或4做增量学习这样会比从零开始训练效率高很多。第二个方向是结合视频帧信息。超声本质上是动态检查单帧分割丢失了时间维度的信息你可以把同一个扫查序列的连续几帧作为一个输入组利用时序模型追踪器官边界在呼吸运动下的变化。第三个方向是跨设备泛化。不同品牌、不同型号的超声设备图像纹理和增益风格会有差异你可以把这份数据集和另外一台设备采集的小样本数据合并做域适应实验看看模型能不能在保持原设备精度的前提下提升对新设备的泛化能力。一点实操后的真心话这套数据从采集、清洗到标注、质检我前后折腾了将近两个月。回头去看最大的体会不是某个模型调参多精妙而是“数据质量才是整个算法的天花板”。一张标注粗糙的图再怎么调损失函数也补不回来一个规范的标签流程能直接决定你的模型在真实场景里能不能站稳脚跟。如果你也准备做超声图像分割或者类似的医学影像项目我建议在动手之前先花时间把数据的清洗规则和标注规范定清楚否则后续返工的成本只会越来越高。最后分享一个小技巧在标注边界模糊的超声图像时把图像放大到200%再打点虽然会慢一点但边界精度提升非常明显。这个小习惯让我的标签质量在后续训练里获益不少。这份数据集还有很多可以玩的空间也希望你用它训练出更好的模型反哺到医疗影像这个方向上来。本文还有配套的精品资源点击获取
返回列表