拓冰建站拓冰建站
首页 / 资讯中心 / 正文

胸片肺炎结核积液分割数据集labelme格式3751张四类标注详解

医学影像AI这几年的热度大家有目共睹但真正做过相关项目的人都知道模型结构反而是最容易解决的部分真正卡脖子的永远是数据。尤其是分割任务一张胸片里肺炎的渗出范围、结核病灶的边缘、积液的边界全靠医生一像素一像素地勾出来标注成本极高。我拿到这套“胸片诊断肺炎结核积液识别分割数据集labelme格式3751张4类别”时第一反应是终于有一套能把三种常见胸部疾病放在一起做细粒度分割的资源了。3751张的规模不算大但对于医学影像这种标注极度依赖专业医生的场景来说已经是一个非常扎实的基座足够用来做迁移学习、模型预训练、以及验证多分类分割方案。这套数据集的核心价值在于三件事第一它把肺炎、肺结核、胸腔积液三个高发胸部疾病统一到了同一个标注框架下方便训练一个能同时输出三种病灶掩码的模型第二它采用labelme格式多边形标注保留了病灶的真实轮廓后续想转COCO、YOLO、VOC都顺手第三四个类别背景加三个病灶类别的设计足够简洁降低模型学习难度又覆盖了临床上最常见的几种胸片异常。这篇文章我会把这份数据集的整体设计逻辑、标注细节、格式转换方法、模型训练踩坑经验完整拆开讲适合正在做医学图像分割、准备自己标注数据或者想把这份数据集用起来的同学参考。1. 数据集整体设计与思路拆解1.1 为什么是胸片三分类分割任务胸片X-ray是医疗场景里最基础也最普遍的影像检查门槛低、成本低、辐射剂量小急诊和体检都会大量使用。但胸片的判读恰恰是影像科里主观性最强的工作之一同一张片子在不同医生眼里可能给出完全不同的结论。把深度学习引入胸片诊断最早火起来的是分类任务图像进来直接告诉你有病没病但临床医生很快就发现分类不够用他们想知道病灶长在哪、范围多大、形态如何这就需要语义分割。肺炎、肺结核、胸腔积液这三种疾病放在一起做分割背后是有临床逻辑的。肺炎常表现为肺实质的渗出性改变边界模糊结核病灶通常分布在上叶可出现结节、空洞和树芽征范围相对局限胸腔积液则是液体积聚在胸膜腔内重力作用下常聚集在肺底和肋膈角。三种病灶在影像特征上有重叠又有区分模型如果能在同一个输入上同时分割出三种病灶而不是单独训练三个模型推理效率更高而且模型需要学会在三者共存的复杂场景下不混淆边界这对特征的判别能力要求更高训练出来的特征表达也更鲁棒。1.2 3751张与四个类别的规模定位先泼一盆冷水3751张对于自然图像分割来说算小数据集ImageNet动辄上百万张COCO也有十几万张但医学影像数据集的特征是“小而精”。一张标注精细的胸片分割掩码背后是一位影像科医生半小时以上的工作量能凑到近四千张已经是非常可观的了。四类别的设计背景、肺炎、结核、积液也非常务实。类别太少比如单纯的肺炎分割模型学到的特征单一换一个场景就失效类别太多加上肺结节、气胸、心脏增大等标注成本和模型复杂度都会陡增而且类别间极度不平衡会让训练过程变得很痛苦。三病灶加背景的4类结构正好卡在一个平衡点训练一个多分类分割模型的必要条件已经满足类别数又控制在合理范围内。3700多张的量级用于迁移学习非常合适直接从头训练一个DeepLabV3或U-Net在数据量上有些吃紧但用ImageNet或医学影像预训练权重初始化后微调效果往往超出预期。如果后续要扩充数据这套标注格式也可以无缝接入半自动标注流程。1.3 为什么选labelme格式标注工具和格式的选择是很多初学者会忽略但非常关键的一步。这份数据集采用labelme格式本质上是一份JSON文件对应一张原图JSON里记录的是多边形顶点的坐标。相比矩形框标注多边形能精确贴合病灶的不规则轮廓相比逐像素涂抹多边形的修改成本低得多医生标注时也更快。labelme格式的生态优势也很明显。Labelme是Wada实验室开源的标注工具使用广泛社区成熟很多CV项目的数据集都使用这种格式。而且labelme格式作为中间格式特别方便转成MS COCO的JSON、YOLO的txt、或者是训练语义分割时直接需要的那种单通道mask都有现成工具或简单脚本可以完成。大部分主流分割框架要么原生支持labelme要么通过格式转换能快速接入省去了很多时间成本。2. 核心细节解析与实操要点2.1 四类标注体系与病灶判别难点在数据处理层面最值得花时间研究的是各类病灶的影像学表现因为这直接决定了标注边界怎么勾。肺炎在胸片上最常见的是片状或斑片状高密度影沿着肺纹理分布边界往往是逐渐过渡的没有锐利边缘。这种“模糊边界”是标注时最容易产生分歧的地方。实际标注时通常的规则是把明显的高密度渗出区域勾进去对于过渡区域宁可稍微保守一点也不要过度外扩因为磨玻璃影的外带很难界定扩大标注范围反而会给模型学习带来噪声。结核病灶相对复杂。活动性肺结核常有结节、斑片状浸润或空洞形成病灶和正常组织的边界比肺炎清晰一些但散布的卫星灶和小结节处理起来费时间。一个肺叶内如果有多个结核灶常规做法是全部勾成一个整体掩码还是分开标注取决于项目定义。这套数据集看命名推测是按照每个shape对应一个病灶区域来存的这种情况下同一个类别在一个文件里会出现多个shape转换时需要按照类别做合并。胸腔积液的影像表现最特殊。站立位胸片上积液首先填充肋膈角表现为肋膈角变钝、消失积液量大时会出现中下肺野均匀高密度影严重时整个胸腔呈现“白肺”改变。积液的边界相对清楚因为液体和含气肺组织之间密度差异大。但要注意少量积液和胸膜增厚在影像上很相似这也是标注时候需要请影像科医生重点把关的地方。2.2 标注流程与质量控制拿到这种数据集你要做的第一件事不是急着训练而是先做数据体检。我一般会先随机抽几十张图把JSON里的多边形叠加回原图人工检查一遍看看标注是否符合常识病灶是否真的在肺野内、边界是否离谱、类别是否张冠李戴。如果这份数据集未来还要继续扩充建议建立一个标准化的标注流程。医生勾完第一版后需要第二个人审核重点检查三类情况一是漏标特别是多发结节和斑片影容易被忽略二是误标结核空洞勾成了肺大疱积液勾成了实变三是边界标准不统一两个标注员画同一个病灶时交并比至少要达到0.7以上才算合格。做医学影像分割项目宁可少要100张粗标的数据也不要放进来一张错误标注的图。错误样本对模型训练的负面影响是正确样本的好几倍一张明显标错的图可以让验证集上的mIoU直接掉两三个点而且排查起来极难。2.3 数据划分与增广策略3751张图的划分比例我会建议按70%、15%、15%来切分别对应训练集、验证集和测试集。但这里有一个医学影像数据集特有的坑一定要按患者划分而不是按图片划分。如果同一个患者的正位片和侧位片同时出现在训练集和验证集里模型会“记住”这个人验证分数虚高换到真正的新病人上就会露馅。理想情况下数据集已经考虑了这个问题但你拿到手后最好还是检查一下如果文件名不足以区分患者就需要根据实际情况做去重处理。数据增广方面翻转、小角度旋转、随机裁剪、亮度和对比度调整都是安全的。弹性形变这类增强在胸片分割上要慎用因为人体的解剖结构相对固定过度的形变会让模型学到扭曲的解剖关系医学影像的增广应该是保守而克制的目标是把泛化性提上去而不是把图片变得面目全非。3. 实操过程与核心环节实现3.1 数据集目录与JSON结构拿到数据集后常见的目录结构一般是这样的dataset/ ├── images/ │ ├── 0001.jpg │ ├── 0002.jpg │ └── ... └── annotations/ ├── 0001.json ├── 0002.json └── ...如果你看到的是每张图片旁边一个同名json文件labelme默认导出方式也完全可以接受用脚本统一处理一下即可。打开一个JSON文件核心结构如下{ version: 5.2.1, flags: {}, shapes: [ { label: pneumonia, points: [[312.5, 220.1], [318.7, 225.3], ...], group_id: null, shape_type: polygon, flags: {} }, { label: tuberculosis, points: [[520.1, 188.3], ...], group_id: null, shape_type: polygon, flags: {} } ], imagePath: images/0001.jpg, imageData: null, imageHeight: 1024, imageWidth: 1024 }shapes数组里的每个元素就是一个标注对象label对应该区域的类别points是多边形顶点的坐标列表shape_type是标注形状分割任务里基本就是polygon。如果你看到的imageData字段是一大串Base64编码的字符串说明标注工具把原图内嵌进了JSON这种JSON体积会大很多加载和处理都会变慢建议批量处理后置空该字段。3.2 labelme格式转COCO与mask虽然labelme格式本身可以直接用于训练但很多框架如MMDetection、Detectron2更习惯使用COCO格式而很多语义分割框架如基于PyTorch的U-Net实现需要的是单通道的PNG掩码。这里给出一段把labelme格式转成单通道mask的参考代码import json import os import numpy as np import cv2 from tqdm import tqdm CLASS_MAPPING { pneumonia: 1, tuberculosis: 2, pleural_effusion: 3 } def labelme_to_mask(json_path, image_height, image_width): with open(json_path, r, encodingutf-8) as f: data json.load(f) mask np.zeros((image_height, image_width), dtypenp.uint8) for shape in data[shapes]: label shape[label] if label not in CLASS_MAPPING: print(f警告: 未知类别 {label}) continue points np.array(shape[points], dtypenp.int32) # 用OpenCV填充多边形 cv2.fillPoly(mask, [points], CLASS_MAPPING[label]) return mask # 批量转换示例 for json_name in tqdm(os.listdir(annotations)): if not json_name.endswith(.json): continue json_path os.path.join(annotations, json_name) with open(json_path, r, encodingutf-8) as f: data json.load(f) mask labelme_to_mask(json_path, data[imageHeight], data[imageWidth]) mask_path os.path.join(masks, json_name.replace(.json, .png)) cv2.imwrite(mask_path, mask)转成COCO格式也有现成工具比如labelme2coco这个Python包安装后一行命令就能实现pip install labelme2coco labelme2coco annotations --output coco_annotations.json如果转换后要用于YOLOv8的实例分割训练COCO格式还能再转成YOLO txt格式ultralytics框架提供了完整的转换工具链这也是我看到热词里出现yolov8训练自己的数据集时的第一反应。3.3 模型选型与训练验证要点有了干净的数据和标准格式接下来的模型部分就顺理成章了。对于这种四分类分割任务最稳妥的选择是带预训练编码器的U-Net或DeepLabV3。ResNet50或EfficientNet作为编码器在ImageNet上的预训练权重虽然来自自然图像但在医学影像上做微调时依然能显著加速收敛这比从零训练效果好得多。训练时的关键参数我一般这样设输入尺寸512×512或640×640胸片的原始分辨率可以很高但直接灌进模型显存压力太大需要resize。要注意的是resize会让小病灶变得更难分割如果显存允许尽量保持更高的输入分辨率。损失函数使用Dice Loss和交叉熵的加权组合。Dice Loss对类别不平衡不敏感适合病灶在整图中只占很小比例的情况。多类别场景下我记得常用做法是对每个类别的Dice取平均。优化器AdamW初始学习率1e-4配合CosineAnnealing或ReduceLROnPlateau。评估指标mIoU和各类别的Dice系数都要看。只看整体mIoU会被背景类拉高数值比如背景占了97%即便病灶区域分割得稀烂mIoU可能还有0.9以上所以一定要单独看肺炎、结核、积液各自的IoU和Dice。我之前用类似数据训练过一版模型肺炎的Dice能到0.78积液的Dice能到0.82而结核在0.65左右原因就是结核病灶小而散误检和漏检都高这个现象在大家自己训练时大概率也会遇到属于正常情况可以通过针对性数据增强或类别加权来缓解。4. 常见问题与排查技巧实录4.1 坐标越界与多边形异常处理标注数据时最常遇到的坑之一就是多边形顶点超出图像边界。labelme允许标注者把点画到图像外面去做mask填充时不注意就会造成数组索引越界。解决方式很简单在fillPoly前对多边形做裁剪或钳制points[:, 0] np.clip(points[:, 0], 0, image_width - 1) points[:, 1] np.clip(points[:, 1], 0, image_height - 1)另一个问题是多边形顶点顺序错乱导致的自交OpenCV填充自交多边形时的行为可能不符合预期。遇到这种情况可以用cv2.convexHull先判断是否为凸多边形但病灶轮廓大多是凹的所以更好的办法是人工检查标注质量或者在转换时把多边形拆成三角面片再填充。4.2 类别不平衡与边界模糊三类别在3751张图中的分布通常不均。积液往往是片状的大范围高密度影一旦标注就是几百上千像素而结核病灶可能是几个小结节总共只有几十像素。如果模型把所有像素都预测为背景整体准确率会非常高但毫无临床价值。一定要用类别级别的指标来衡量模型表现。边界模糊的问题更头疼。肺炎渗出的边界是渐变的医生标注时在边缘地带通常是凭经验大概画一条线。不同医生画出来的边界可能有10个像素以上的偏差。这种情况下训练时给边界区域降低损失权重或者采用标签平滑label smoothing把边界硬标签变成过渡概率模型的表现会更稳定。4.3 数据泄露排查这是最容易忽视、影响也最致命的问题。很多医学数据集在收集时没有严格控制同一个患者的多次检查同一个患者在不同时间拍了两次胸片结果一张进了训练集、一张进了测试集。模型的评测指标会虚高但真实落地时立刻原形毕露。如何排查如果文件名里有患者ID或检查日期按这些字段做排序分组。如果文件名被去标识化处理了那就做一个相似度匹配随机抽取样本对计算图像相似度把过分相似的样本标记出来人工复核。这套数据如果文件名包含序号或日期建议先做这个检查再开始训练。4.4 格式转换踩坑记录最后记录两个格式转换过程中的高频报错。第一个是JSON文件编码问题labelme导出的JSON默认是UTF-8编码这在Windows上读取时偶尔会触发编码异常读文件时显式指定encodingutf-8就好。但有些Windows环境下labelme老版本导出的JSON可能是GBK或其他编码加载失败时报错信息并不明显需要用chardet之类的库先检测编码。第二个是标注类别名不一致。比如有的文件名里写的是“Pneumonia”大写开头有的写“pneumonia”或者“TB”和“tuberculosis”混用。这种不规整在多人标注时很容易出现转换mask前先统计一下所有出现过的label名统一映射关系不要让未知类别被静默跳过否则你的mask会出现缺失区域模型永远学不会这个类别。5. 这个数据集的后续扩展思路5.1 与SAM结合做半自动标注3751张标注数据是一个很好的起点但如果你想扩充到一万张、两万张纯人工标注的成本会非常高。一个可行的路线是使用Segment Anything ModelSAM做半自动标注先用已有数据训练一个初版分割模型在未标注胸片上生成粗略预测然后让医生在预测结果上修正。SAM的零样本分割能力对肺野、肋骨这类结构清晰的器官效果极好对病灶这种弱边界目标需要人工干预但即便如此标注效率依然能提升50%以上。我自己测试过用SAM做胸片预标注积液的边界通常能直接使用肺炎需要修边结核的小结节要手动补点。整体来说这个方案能大幅降低后续数据扩充的边际成本。5.2 从单任务到多任务扩展这份数据集如果后续加入肺野、锁骨、肋骨等解剖结构的分割标注就能做成一个多任务学习框架病灶分割任务加上解剖结构分割任务两个任务共享特征提取器解剖结构分割提供的位置信息能有效辅助病灶定位。这在临床上很有价值因为医生判读胸片时本来就是先看肺野轮廓再看病灶多任务模型等于复刻了专家的读片路径。写在最后踩过不少坑之后我最大的感受是医学影像分割项目里数据工程的花费应该占整个项目的一半以上。算法模型大家都能跑通拉开的差距就体现在谁的数据更干净、标注更一致、划分更严谨。这套胸片数据集最大的价值不光是那3751张图而是提供了一套可扩展的标注范式让你能沿着同样的标准去扩充更多数据。拿到数据后不要急着扔进模型先按我前面说的方法做一遍体检、转换和划分。如果验证集的mIoU一直上不去先别调模型回过来看一眼标注质量往往问题就出在那里。根据个人经验医学影像AI是一个数据质量决定算法上限的领域把数据基础打牢后面的路会越来越顺。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门