医疗物品实例分割数据集处理全攻略:从解压到YOLOv8训练避坑
发布时间:2026/10/11 17:06:20 锦皓数字建站

简介这是一份面向目标检测与实例分割任务的医疗物品数据集包含1990张训练图片及对应YOLO格式标注覆盖磁共振成像设备、牙科物品、医生制服、注射器四个类别。数据集由领域专家完成实例分割标注边界精确适配主流深度学习框架可直接用于YOLO系列等模型的训练与评估适用于医疗AI系统开发、医疗机器人感知、医院设备管理等场景。资源包共2000个文件核心为1990个txt标注文件另含8张jpg示例图片、1个yaml配置文件及1个docx说明文档整体大小约150.19MB结构清晰便于快速上手。目前已有212人学习使用。借助该数据集研究者可省去大量数据采集与清洗时间直接开展多类别医疗物品检测与分割实验也可作为医疗环境智能监控、人员着装规范识别等项目的训练基准。1. 医疗物品实例分割数据集解压前先想清楚你拿到的是原料还是半成品“医疗物品实例分割数据集_20251117_182346.zip”这个文件名里其实藏着不少信息2025年11月17日、18:23:46大概率是某个标注工具导出的数据快照。实例分割要做的是把手术钳、止血棉、注射器、缝合针这些目标从手术台、器械盘的照片里检测出来同时给每个目标画出一整块像素级轮廓比单纯的目标检测多一层掩膜标注。拿到这种zip的人多半在做什么清点、复核、手术室行为识别之类的工程。下面讲的是一条能直接照着走的路从解压、校验、转格式一路走到能训练、能评估、能判断能不能用。适合正在给医疗物品做检测分割训练、却在数据上反复翻车的从业者。2. 解压与体检医疗物品实例分割数据集的目录结构、标注格式和统计脚本拿到后缀是.zip的数据集第一反应是解压但医疗物品分割数据集的坑往往就埋在这第一步。很多标注平台导出的压缩包在Windows下打包内部文件名是GBK编码直接扔到Linux服务器上unzip就会出现乱码。我一般先建一个干净目录再指定编码解压mkdir -p medical_seg unzip -O gbk 医疗物品实例分割数据集_20251117_182346.zip -d medical_seg cd medical_seg du -sh .这里的-O gbk告诉unzip用GBK解码文件名能救回八成Windows系zip的中文乱码问题。如果你的unzip不认-O参数macOS自带版本就不支持那就用Python的zipfile在读取时把cp437转回gbk后面避坑章节会给出具体写法。解压完先别急着打开训练脚本用find . -maxdepth 2 -type d看目录层级再配合ls -lh确认图片大小区间。医疗物品数据集里常出现两种极端要么图片全是2MB以上的原图要么是几十KB的压缩图。前者训练时IO吃紧后者标注本身看不清器械边缘两种都要提前知道。2.1 解压后的标准目录images、masks、labels各司其职一个能直接用于训练的医疗物品实例分割数据集内部目录通常长这样路径内容说明images/原始图片jpg或png手术台、器械盘、托盘实拍masks/像素级掩膜单通道png背景0实例为1、2、3这类编号labels/YOLO分割txt每行类别id 归一化多边形点annotations/COCO格式jsonsegmentation存polygon或RLEdata.yaml类别名与路径配置训练时喂给YOLO是不是五类都齐决定了这份数据是“半成品”还是“成品”。很多标注平台只导出images和annotations/instances.jsonmasks目录要自己生成也有平台反着来只给掩膜不给COCO json类别信息全部写在文件名里。我拿到手第一件事是把每个目录的文件数量拉出来对账ls images | wc -l和ls masks | wc -l必须一致不一致就说明标注过程有漏图或漏掩膜后面训练必翻车。掩膜本身也要抽查。单通道png里0是背景1、2、3是实例编号训练框架会按索引去查类别。如果标注平台导出的是三通道彩色掩膜必须做一次映射把每个RGB值对应到“类别id 实例id”再转成单通道编号图。这一步在医疗物品场景很常见因为不少标注工具有可视化需求保存成彩色图更容易查看。别嫌麻烦转换时一帧一帧校验叠加图轮廓对不准就退回重标。注意还有一类隐蔽问题手机或相机拍的图片带EXIF方向直接用OpenCV读会横过来而标注平台按显示方向标注mask与图片差90度。第一次训练前把所有图片统一转正再配mask避免数据管线里出现“人眼看不出来、模型全学错”的错位。2.2 三种标注格式的取舍COCO JSON、YOLO分割txt、PNG掩膜医疗物品实例分割的数据格式就我见过的标注平台无非三种COCO JSON、YOLO分割txt、PNG掩膜。三者的关系是同一批标注内容的不同表达方式但选型会直接决定后面训练和审核的工作量。COCO JSON的核心是annotations数组每个元素有segmentation、area、bbox、category_id其中segmentation可以是多边形坐标也可能是RLE压缩串。它的优势是生态最全MMDetection、Detectron2、Mask R-CNN全是原生读取劣势是JSON体积大一个过万张图的数据集instances.json可能上GB解析速度慢而且人没法直接看图确认标注。YOLO分割txt的每一行是“类别id 一串归一化坐标点”文件体积极小YOLOv8-seg开箱即用劣势是坐标点一旦归一化就很难还原出“原始标注到底是什么形状”对医疗场景的审核诉求不友好。PNG掩膜最直观但同一个png里多个实例怎么索引、值到底是类别id还是实例id不同平台规则不同。我的建议很直接如果医院方或标注平台最终交付的是COCO JSON就把COCO当作“底账”永久留档审核、复现、换框架都靠它训练前再单独转换一份YOLO txt。如果团队是YOLO全家桶确实可以直接用txt但至少写一个“txt还原成mask”的脚本每训练一轮就随机抽20张检查轮廓有没有错位。2.3 首轮统计要算清的三本账类别数、实例数、mask面积占比数据体检比调模型参数重要得多这是医疗物品这类小样本项目里最容易亏时间的地方。拿到COCO JSON后我会先跑一段统计脚本import json from collections import Counter from PIL import Image import numpy as np, glob coco json.load(open(annotations/instances.json, encodingutf-8)) cats {c[id]: c[name] for c in coco[categories]} cnt Counter(a[category_id] for a in coco[annotations]) print(类别总数:, len(cnt)) for cid, n in cnt.most_common(): print(cats[cid], n) for fp in sorted(glob.glob(masks/*.png))[:5]: mask np.array(Image.open(fp)) print(fp.split(/)[-1], mask占图比例:, round((mask 0).mean(), 4))这段脚本解决三件事。第一类别数如果医疗物品类别超过15类但每个类别的实例数参差不齐先合并相近类别再启动训练比硬上模型快。第二实例数每一类至少要有50个实例否则即使训练出轮廓换个拍摄角度就找不回来。第三mask面积占比器械大多只占画面几个百分点细的缝合针甚至只有0.1%这类小目标要提前决定好imgsz和增强策略。我还习惯顺手统计图片分辨率分布如果同一份数据集里混着720p和4K图训练时要么统一resize要么按分辨率分桶训练不然边框和轮廓的尺度会互相干扰。这个统计结果我一般存成一个report.txt留在数据集目录里下次任何人接手先看它再决定动不动训练参数。3. 转成YOLOv8能训练的数据集COCO转YOLO分割txt与场景聚簇划分数据体检完接下来把标注转成训练框架能吃的格式。如果你打算用YOLOv8-seg做基线这一步要把COCO JSON里的polygon转成归一化的txt。很多开源脚本只处理简单情况遇到RLE或多段多边形直接报错所以最好自己写一个能兜底的转换脚本。3.1 用python把COCO JSON转成YOLO分割txtimport json, os, numpy as np import cv2 from pycocotools import mask as coco_mask CATS {1: 0, 2: 1, 3: 2} # 把COCO类别id映射成连续训练id def poly_to_yolo(seg, img_w, img_h): coords [] for i in range(0, len(seg), 2): x min(max(seg[i] / img_w, 0.0), 1.0) y min(max(seg[i 1] / img_h, 0.0), 1.0) coords.extend([f{x:.6f}, f{y:.6f}]) return .join(coords) def convert(coco_path, out_dir): os.makedirs(out_dir, exist_okTrue) with open(coco_path, encodingutf-8) as f: coco json.load(f) img_of {im[id]: im for im in coco[images]} out_lines {} for ann in coco[annotations]: seg ann[segmentation] im img_of[ann[image_id]] label CATS.get(ann[category_id]) if label is None: continue if isinstance(seg, dict): m coco_mask.decode(coco_mask.frPyObjects(seg, im[height], im[width])) contours, _ cv2.findContours(m.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) seg [c.flatten().tolist() for c in contours] fname im[file_name].rsplit(., 1)[0] .txt for poly in seg: if len(poly) 6: continue out_lines.setdefault(fname, []).append(f{label} {poly_to_yolo(poly, im[width], im[height])}) for fname, lines in out_lines.items(): with open(os.path.join(out_dir, fname), w) as f: f.write(\n.join(lines) \n)这段代码的逻辑分三层。第一层是坐标归一化把像素坐标除以图片宽高再强制clip到[0,1]避免标注点在图片边缘被画出去后超出边界这是YOLO训练报“label out of bounds”最常见的原因。第二层是RLE处理当COCO里的segmentation是dict而不是list时先解码成二值掩膜再用OpenCV提取轮廓如果没有pycocotools和OpenCV这步会直接抛异常建议先执行pip install pycocotools opencv-python。第三层是类别映射CATS把标注里的原始类别号映射成从0开始的连续id如果原数据集已经是0到N-1这个映射就写成恒等即可。参数上有两个容易踩的点。归一化坐标保留6位小数是经验值位数太多txt体积暴涨训练时并不会更准位数太少会导致小物体轮廓退化。另外YOLO格式约定一个实例一行如果一个器械被标注成两个断开的多边形环我一般转换成两行训练时YOLO会当成两个实例处理对清点场景这样能数出单个器械对像素级评估影响也不大。转换完必须做一次对账images和生成的labels两个目录文件数相等再把每条txt的第一列去重确认类别id都在0到N-1之间。3.2 数据集划分按场景聚簇而不是按文件随机抽划分train/val/test新手最容易直接shuffle文件名。医疗物品数据多半来自视频抽帧或连拍同一个托盘、同一台手术会被拍下几十张高度相似的帧随机划分等于把“同一场景的不同帧”同时塞进训练集和验证集验证指标虚高得离谱部署到真实环境立刻打回原形。我一般按“场景ID”分桶再在每个桶内做随机抽样import os, random from collections import defaultdict random.seed(42) groups defaultdict(list) for name in os.listdir(images): scene name.rsplit(_, 1)[0] # OR_07_163254.jpg - OR_07_163254 groups[scene].append(name) train, val, test [], [], [] for scene, names in groups.items(): random.shuffle(names) n len(names) train names[:int(n * 0.7)] val names[int(n * 0.7):int(n * 0.9)] test names[int(n * 0.9):] with open(train.txt, w) as f: f.writelines(fimages/{n}\n for n in sorted(train)) with open(val.txt, w) as f: f.writelines(fimages/{n}\n for n in sorted(val)) with open(test.txt, w) as f: f.writelines(fimages/{n}\n for n in sorted(test))name.rsplit(_, 1)[0]这条逻辑要求文件名里场景和帧号用下划线分隔比如OR_07_163254.jpg会归到场景OR_07_163254。如果文件名没有这种规律就需要一个映射表来标注每张图的拍摄场景这个表比任何超参数都值钱。固定随机种子42是为了让团队复现同一个划分否则每次训练都在不同的val上比没法横向比较模型。7:2:1是我常用的分配比例如果实例总数少val可以再降一点但test一定要留够一个完整场景专门用来模拟没见过的手术台布局。做完场景聚簇划分后我还建议跑一遍图片感知哈希去重把重复或近似帧从整个数据集里筛一遍步骤放在避坑章节说明。3.3 增强参数怎么设翻转、变形、马赛克都要克制医疗物品实例分割的增强策略和自然场景不一样。自然场景可以大力出奇迹但手术器械是刚性物体类别形态差异集中在细长、反光、遮挡上开满增强反而会让轮廓学歪。我常用的YOLOv8-seg训练参数如下yolo train modelyolov8n-seg.pt datamedical.yaml epochs200 imgsz800 \ hsv_h0.01 hsv_s0.4 hsv_v0.4 degrees5 fliplr0.3 mosaic0.3 copy_paste0.1degrees5是控制旋转角度细长的缝合针和刀片如果旋转太大掩膜长宽比会被畸变模型会对方向产生错误记忆5度已经足够覆盖拍摄时的摆放偏移。hsv三个参数保留适度值因为金属器械在不同光照下的反光确实需要颜色泛化但hsv_h0.01表示色相几乎不动医疗器械的颜色往往是类别线索比如止血棉是白色、手术钳是金属色不该被大幅左右。mosaic0.3把Mosaic增强参与概率从默认值调低因为四图拼接会把细长目标切碎让模型学到半截轮廓copy_paste0.1是分割专用增强把小器械复制粘贴到其他图上对样本少的小目标很有效。如果发现某个细长类别缝合针、穿刺针始终学不到优先检查imgsz而不是增强。把imgsz从640提到800再试一轮小目标的像素占比会明显改善。数据增强不是越强越好每次只改一个参数用val的mask mAP对比才找得准到底是谁在拖后腿。4. 医疗物品实例分割训练前必查的5个坑从乱码到泄漏的排查清单这一章我按“现象→原因→解决”写都是真实会踩的问题。你照着顺序排查能省下至少一星期调参时间。4.1 验证集mask全黑、mAP恒为0现象训练loss在正常下降验证集却一直输出全黑的maskmAP始终是0。原因有三种。第一val图片对应的labels文件不存在YOLO会在日志里打印“no labels found in val”但很多人没注意。第二类别id不是从0开始标注平台导出的类别id可能是1、3、7这种原始编号映射没做干净越界的类别被当成背景丢弃。第三多边形坐标越界归一化前没clip某些点在图片边缘外侧变成负值或大于1训练时被丢弃。解决训练前写一个校验脚本逐条扫描labels目录中所有txt检查类别和坐标范围import os N_CLASSES 3 bad 0 for fn in os.listdir(labels): for line in open(os.path.join(labels, fn), encodingutf-8): parts line.split() if not (0 int(parts[0]) N_CLASSES): bad 1 continue coords list(map(float, parts[1:])) if any(v 0 or v 1 for v in coords): bad 1 if bad 0: print(fn, 有, bad, 条非法标签)N_CLASSES要和data.yaml里的类别数一致。这个脚本跑完再检查val目录下所有图片都有同名txt两个条件都满足后重新启动训练。4.2 mask轮廓锯齿严重边缘噪声大现象验证集mAP还能看但放大了看每个器械边缘都像锯齿一些器械边缘还拖出几根细长伪影完全不像人工标注的平滑轮廓。原因一是标注阶段多边形点太稀疏工具自动插值后本来就有锯齿二是网络输出mask的分辨率只有输入图的四分之一左右边缘天然被钝化三是没有加边界相关的损失模型对边缘不敏感。解决分三层。训练层面打开YOLO的seg_loss相关配置或用Lovasz loss这类loss直接优化IoU边界对锯齿改善明显推理层面把预测mask上采样回原图尺寸后再和GT比较不要在半分辨率下算指标后处理层面对二值mask做一次开运算去掉孤立噪点from scipy import ndimage import numpy as np pred ndimage.binary_opening(pred 0.5, structurenp.ones((3, 3)))但根本解法在标注端。如果抽查发现GT本身就锯齿严重说明标注规范里缺少“点间距不超过5像素”的约束这部分只能返工或抽重标后处理救不回标注原罪。4.3 zip解压乱码或图片加载失败现象解压后文件名变成“锟斤拷”或乱码开头Python的Pillow报OSError: cannot identify image file。原因Windows下打包zip默认用GBK编码文件名Linux和macOS的unzip默认按UTF-8解码两边一撞就乱码。有些标注平台的zip里还混入Windows特有的非法时间戳解压会报bad header。解决优先在命令行指定编码解压unzip -O gbk 医疗物品实例分割数据集_20251117_182346.zip -d medical_seg如果unzip不支持-O改用Python读取把文件名从cp437校正回gbkimport zipfile, os with zipfile.ZipFile(医疗物品实例分割数据集_20251117_182346.zip) as z: for name in z.namelist(): fixed name.encode(cp437).decode(gbk) z.extract(name, medical_seg) os.rename(os.path.join(medical_seg, name), os.path.join(medical_seg, fixed))解压后立刻抽查三张图用Pillow读取并打印尺寸确认图片没有截断、文件头完好再进入下一步。这一步翻车的概率不低尤其在标注工具从Windows导出、算法工程师在Linux服务器上接手时。4.4 细长器械类别始终学不到现象手术钳、纱布这种大目标mask能出来缝合针、刀片这种细长或微型目标在val上几乎全部漏检mAP比大物体低十几个点。原因细长目标经过下采样后只剩几十个像素加上Mosaic增强把它们切碎模型根本看不到完整轮廓同时Loss被大面积背景和大目标主导小目标就算预测错了对总体loss影响也很小。解决一是在训练参数里把mosaic降到0.2避免细长物体被拼接切碎二是打开copy_paste增强把小器械实例复制到更多样化的背景上相当于免费扩充样本三是做按实例数的类别加权在小器械类别的loss项上加权重让梯度不再被大类别淹没。如果项目目标是清点数量可以先把这个细类合并进“锋利器械”粗类训练稳定后再细分比在一开始就逼模型分细类稳得多。4.5 同场景泄漏导致指标虚高部署就翻车现象训练日志上val的mask mAP超过90%团队都很高兴拿到医院真实环境一测准确率只有76%肉眼可见漏检。原因随机划分train/val时同一个场景的连拍帧被同时分到了两边。模型在训练时见过这张托盘的布局验证时等于“开卷考试”指标自然虚高真实环境里器械摆位、背景、光线全变了立刻现形。解决参考3.2节按场景分桶划分并用感知哈希把近似帧去重from PIL import Image import imagehash, os hashes {} for fn in os.listdir(images): h imagehash.phash(Image.open(os.path.join(images, fn))) if any(h - old 4 for old in hashes): print(疑似重复:, fn, 与已有图片相似) hashes[h] fnphash差小于4表示图片结构几乎相同医疗连拍里两台相机同时拍同一个托盘就是这种情况。把重复帧剔除后重新划分再用保留的test场景做一次完全没见过的盲测才敢谈部署。这个坑最容易出现在“标注数据来之不易”的项目里越是舍不得删数据越要警惕指标泡沫。5. 用mask mAP、Dice与连通域分析验证医疗物品分割的可部署性模型训练完先别急着写技术文档先用三个层面的验证把结论钉死mask mAP看整体、Dice看掩膜贴合度、连通域分析看预测是否符合解剖常识。评估层面我至少会同时报告mask mAP0.5、mask mAP0.5:0.95、Dice均值。mAP0.5是宽松指标适合看全体类别的粗检情况mAP0.5:0.95对边缘拟合更敏感医疗物品要求轮廓精确不能只看前者。Dice对像素重叠敏感计算时用预测mask与GTmask两个都二值化再套公式def dice(pred, gt): inter (pred gt).sum() return 2 * inter / (pred.sum() gt.sum() 1e-6)医疗物品里dice低于0.7的类别基本不能用于自动清点只能做人工复核辅助。再往下走用连通域分析看预测mask有没有“碎片化”这对细长器械特别有效from scipy import ndimage lab, num ndimage.label(pred 0.5) pieces lab lab.max() area_ratio pieces.sum() / pred.sum()一个器械的mask本应是一个连通域如果num远大于GT的实例数说明模型在过分割手术钳被切成好几块area_ratio明显小于1说明主连通域占比低输出碎片太多后处理或阈值需要调整。我自己的习惯是每轮训练后固定抽50张val图同时看这三个数任何一个明显波动就停训练改配置。去年做手术器械清点项目时我用随机划分得出val mAP 92%按场景分桶后掉到84%再去医院用一段没参与训练的手术视频做盲测只有76%。从此我拿到任何医疗物品实例分割数据集第一件事不是写网络结构而是先跑场景聚簇划分和去重脚本把数据账算明白再开训练。指标是给人看的部署效果才是给患者和医生用的希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。