简介面向医学图像分割任务的人体骨骼图像多类别分割数据集共约3500张图片与对应掩膜标签标注覆盖L5及头位1-9、椎管10、L5/S1椎间盘间隙及头位11-19等19个类别适合医学影像分析、深度学习分割模型训练、论文实验和课程设计等场景。数据集已按训练集与测试集划分其中训练集约2800对、测试集约700对每个集合均包含images图片目录和masks模板目录压缩包共2000个文件以PNG图像为主1998个另含1个类别说明txt和1个可视化py脚本整体约367MB。附带Python可视化脚本可随机抽取一张样本将原始图片、GT图像、GT在原图上的蒙板效果一并展示并保存到当前目录便于快速核验标注质量也能直观呈现分割任务的输入输出关系。已有339人学习浏览包内目录结构清晰按训练/测试组织可直接用于多类别分割网络的训练、验证与结果对比也方便二次扩展与标注检查。1. 人体骨骼多类别分割3500张图和标签为什么值得先自己过一遍数据“医学图像分割数据集”这个词搜出来大多是下载链接和榜单指标但真正把一份人体骨骼图像分割数据集用进训练里卡点往往不在模型而在标签。3500张图像和对应掩膜量不大不小做多类别分割时图像模态、类别编号、掩膜格式、训练验证划分每一样都能让结果天差地别。这篇笔记把整条流程拆开多类别骨骼分割和二分类分割差在哪数据检查与预处理脚本怎么跑U-Net训练的关键参数和翻车点在哪。适合做骨折检测、骨密度分析、手术规划这类需要先分割出骨骼区域的人也适合拿多类别分割练手的数据集玩家。2. 骨骼分割数据集的格式与检查先盘清图像、标签和类别再动手拿到一份人体骨骼分割数据集直接解压开训是最快的翻车方式。多类别骨骼分割里标签的语义不是简单的前景和背景而是“这块像素属于哪一块骨”。这份约3500张的数据集如果连类别编号都没核对过训练跑出来的指标再漂亮也是自欺欺人。这一章先把数据侧的事情说明白。2.1 多类别骨骼分割和单类别分割差别在哪模态与类别体系决定后续一切单类别分割只回答一个问题这个像素是不是骨骼。多类别分割要回答这个像素属于哪一块骨。同样是分割模型输出通道从2变成N1损失从二分类变成多分类难度不是线性增长而是类别边界处会互相打架。膝关节数据里股骨远端和胫骨近端在X光片上几乎贴在一起标注线稍有偏差训练出来的边界就和金标准对不上。影像模态决定了这个数据集的难度上限。同样是3500张图来源不同模型要学的东西完全不同模态边界表现标注难度适合的网络结构X光DR骨骼重叠区域多软组织干扰明显需要解剖经验边界主观性强2D网络依赖增强和更大感受野CT骨骼与软组织灰度差大边界清晰切片多标注工作量大2D网络逐片训练或用3D网络MRI骨髓、软骨、肌肉对比复杂边界判定争议大2D网络需要针对性增强类别体系也要先确认。不同数据集的“多类别”定义差异很大有的是按部位分的髋关节数据标签是“骨盆、股骨近端”有的是膝关节数据标签是“股骨、胫骨、腓骨、髌骨”还有的是全身骨骼按区域编号。类别数越多小类别被大类别淹没的问题越严重。拿到数据后第一件事就是看类别元信息确认背景是0还是255确认有没有未标注的忽略区域这些直接决定损失函数的写法。2.2 目录结构和标注格式的常见形态解压后先画一张数据地图骨骼分割数据集的目录结构通常长这样bones_dataset/ ├── images/ │ ├── case_001.png │ ├── case_002.png │ └── ... ├── labels/ │ ├── case_001.png │ ├── case_002.png │ └── ... ├── train.txt ├── val.txt └── dataset.yamlimages目录放原始图像labels目录放同名的分割掩膜train.txt和val.txt是官方划分。有的数据集会直接按train/val子目录组织。文件清单一般涉及这几类文件/目录用途注意事项images/原始图像多为灰度图注意位深8bit还是16bit直接影响读取方式labels/分割掩膜与图像同名像素值等于类别编号需确认0是否代表背景train.txt / val.txt数据划分列表没有划分文件时要自己按患者划分不能随机划分dataset.yaml类别名称与编号有的话先读它能省掉猜标签含义的时间标注格式最常遇到两种PNG掩膜和COCO JSON。PNG掩膜是单通道图像每个像素值就是类别编号U-Net系模型直接用扫一眼就能确认格式是否合理。COCO JSON是用多边形坐标描述每个实例灵活但需要先转成掩膜才能训练。骨骼分割数据集中PNG掩膜更常见但如果拿到的是JSON就得准备好转换脚本。提示不管哪种格式先用训练脚本之外的小工具确认“图像、掩膜、类别”三者能对上再谈训练。这个习惯能省掉后面大量排查时间。2.3 用Python跑一遍数据完整性检查缺图、错尺寸、脏标签一次暴露我每次拿到新数据都会先跑一份完整性检查脚本20分钟能写完能挡住后面一周的坑。import os from PIL import Image import numpy as np img_dir images label_dir labels img_files sorted(os.listdir(img_dir)) label_files sorted(os.listdir(label_dir)) # 1. 文件一一对应检查 img_names {f.rsplit(., 1)[0] for f in img_files} label_names {f.rsplit(., 1)[0] for f in label_files} missing img_names - label_names print(缺标签的图像数量:, len(missing)) # 2. 尺寸一致性抽查 for f in img_files[:20]: img Image.open(os.path.join(img_dir, f)) lbl_name f.rsplit(., 1)[0] .png lbl_path os.path.join(label_dir, lbl_name) if not os.path.exists(lbl_path): continue lbl Image.open(lbl_path) if img.size ! lbl.size: print(尺寸不一致:, f, img.size, lbl.size) # 3. 标签像素值分布抽查 for f in label_files[:10]: lbl np.array(Image.open(os.path.join(label_dir, f))) print(f, 类别值:, np.unique(lbl))这段脚本的逻辑分三段先用文件名匹配查出缺标签的样本这类问题在数据搬运过程中最常见再抽查图像和掩膜尺寸是否一致很多时候预处理脚本单独对图片做了resize标签没跟上后面训练时索引就会错位最后看标签的像素值分布重点确认类别编号是否连续、是否只有0和1、有没有出现255这种异常值。如果数据里有DICOM格式的CT图像PIL读不了需要先用pydicom把像素数组解出来并转成PNG或npy再进这个流程。这一步做在前后面省的是大把训练时间和无效debug。3. 把骨骼分割数据集喂进U-Net预处理、标签映射与基准训练配置数据检查通过后下一步是把图像和标签整理成模型能吃的样子。骨骼分割和自然图像分割最大的不同在于原始图像是灰度图标签是稀疏的类别编号预处理每一步都要保证图像和标签对齐。U-Net是医学图像分割里最稳的基准模型3500张这个规模用它做实验正合适。3.1 预处理管线灰度归一化、尺寸标准化与同步增强常见做法是统一尺寸到512×512或256×256灰度图归一化到[0,1]区间训练时做同步增强。这里最容易被忽略的是图像resize用双线性插值没问题标签resize必须用最近邻插值否则会插出“1.5类”这种根本不存在的像素值。import cv2 import numpy as np import albumentations as A def load_and_preprocess(img_path, label_path, size512): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, (size, size), interpolationcv2.INTER_LINEAR) img img.astype(np.float32) / 255.0 label cv2.imread(label_path, cv2.IMREAD_GRAYSCALE) label cv2.resize(label, (size, size), interpolationcv2.INTER_NEAREST) return img, label # 同步增强图像和标签必须用同一组随机变换 transform A.Compose([ A.RandomRotate90(p0.5), A.HorizontalFlip(p0.5), A.ElasticTransform( alpha120, sigma120 * 0.05, alpha_affine0, p0.3, ), ]) def augment(img, label): out transform(imageimg, masklabel) return out[image], out[mask]读取统一走OpenCV的灰度模式避免PIL把16bit图像自动转成8bit导致灰度信息丢失。归一化用整体最大值还是直接用255取决于图像本身的位深。如果原始图就是8bit PNG直接除255就行。如果是从DICOM转出来的像素值范围可能是-1024到3071这种CT值区间那就不能粗暴除以255需要对窗宽窗位裁剪后再归一化。增强这块直接用albumentations的Compose它的内部机制会保证image和mask走同一组随机变换。ElasticTransform对骨骼这类形态变化有天然优势弹性形变模拟关节弯曲带来的结构变化。但要注意alpha和sigma参数不能乱给alpha太大图像会扭曲到解剖结构失真alpha太小等于没增强。120这个值对512×512图像是一个比较稳的起点实际使用时可以调成alpha80试一版对比。3.2 标签映射从标注像素值到模型输出通道做多类别分割模型输出通道数等于“类别数1”背景也要占一个通道。如果数据集给的标签恰好是0到N-1且连续那直接当索引用就行。但现实往往不这么听话常见的是0和255、缺号、类别编号从5开始。此时就需要重映射。# 假设原标签只有背景0和前景255改成二分类的0/1 label_map {0: 0, 255: 1} # 假设原标签是 0, 1, 2, 5缺了3和4压成 0, 1, 2, 3 label_map {0: 0, 1: 1, 2: 2, 5: 3} remapped np.zeros_like(label, dtypenp.uint8) for old, new in label_map.items(): remapped[label old] new注意这个映射不能用原地替换比如把5改成3如果label里本来就有3的像素第一次循环就把原3覆盖了第二次循环又覆盖成新3逻辑全乱。用新数组按old值匹配写入才是最稳的写法。如果拿到的是COCO JSON标注需要把多边形点集先转成掩膜再进训练流程。转换时用多边形填充后的掩膜做类别判断注意多边形自相交导致的问题常见的shapely库或OpenCV的fillPoly都能处理但填充前要先确认多边形坐标是归一化坐标还是绝对像素坐标这个看数据集说明即可。做过yolov8训练自己的数据集的读者刚接触这类医学分割数据时会下意识去找TXT标签文件。其实PNG掩膜才是分割数据集的常见答案目标检测的框和分割掩膜在标签表示上完全是两个体系在数据加载阶段就要把思维切换过来。如果数据集中部分边界区域标注本身就模糊可以考虑给边界像素做软标签。把边界几个像素的one-hot编码从“0或1”改成“0.7/0.3”的过渡值。软标签相当于告诉模型“这块区域标注本身不确定你别太自信”能提升边界稳定性但只建议在标注质量确实一般时用。数据集标注质量高的话软标签反而会让边界变钝。3.3 U-Net基准训练损失函数、学习率与类别权重怎么设3500张图的规模U-Net从零训练加上预训练迁移都可行。我的建议是先跑一个纯U-Net从零训练的基线把训练流程和数据处理验证通了再考虑加载ImageNet预训练权重或做更大模型。骨架代码和关键参数如下model UNet(in_channels1, out_channels5) # 背景 4个骨骼类别 # 类别权重按像素占比倒数计算防止大骨骼区域淹没小骨骼区域 freq np.bincount(all_label_ravel, minlength5) class_weight torch.tensor(1.0 / np.maximum(freq, 1), dtypetorch.float32) class_weight class_weight / class_weight.sum() * len(class_weight) criterion nn.CrossEntropyLoss(weightclass_weight) dice_loss DiceLoss() # 逐类算Dice后取平均 optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-5) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemax, factor0.5, patience10 ) # 每个epoch后验证保存验证Dice最高的权重 for epoch in range(60): train_one_epoch(model, train_loader, criterion, dice_loss, optimizer) val_dice validate(model, val_loader) scheduler.step(val_dice)out_channels有5个因为多类别分割要把背景也算一个类别。CrossEntropyLoss处理类别均衡问题类别权重按像素占比倒数算这样才能让胫骨这类中等大小的骨骼不被股骨完全压住。DiceLoss在医学分割里几乎是标配和CrossEntropy联合使用是常见做法前期CE提供稳定梯度后期Dice精细调整边界。AdamW配1e-4是医学图像小数据集上比较稳的组合Adam默认的1e-3在这个场景下容易把模型震荡到边界不收敛。ReduceLROnPlateau按验证Dice是否停滞来降学习率比按epoch硬降更实用。epoch设60是合理预算3500张图用512分辨率加batch size 8单卡24G显存大约几小时能跑完一版足够把数据处理问题暴露干净。这个阶段如果验证Dice一直很低别急着调学习率。先回数据侧查标签是不是错位了、类别编号是不是映射错了、增强是不是把标签破坏了。模型结构本身很成熟绝大多数训练失败都指向数据和标签这算是做分割数据集项目的一条血泪经验。4. 避坑骨骼多类别分割数据最常见的5个翻车点这一章写的都是真实训练时最容易翻车的五个场景每个都按现象、原因、解决的顺序说清楚。新手照着排查熟手做自查清单也够用。4.1 掩膜里全是0和255类别通道对不上loss直接崩现象数据检查时用看图软件打开掩膜图像上骨骼区域是白的背景是黑的看起来很正常。训练时loss要么一路nan要么不下降。打印标签的np.unique发现只有[0, 255]。原因很多标注工具导出的PNG掩膜是二值图最终保存成了8bit单通道背景0、前景255。模型输出通道一共只用5个0-4255这个值本质上等效于把类别索引写到越界位置CrossEntropyLoss按索引取值时直接飞到无效区域。解决所有训练流程接入之前先把标签统一重映射成从0开始的连续编号。二分类就映射成0和1多分类则按照2.3节的安全映射脚本处理。映射后再跑一次np.unique确认类别编号连续且最大编号等于输出通道数减一。4.2 标签和图像尺寸不一致resize把掩膜插出了“1.5类”现象验证集Dice看着正常但可视化预测结果时发现整个掩膜向左上角偏移或者骨骼边缘全部错位一个固定像素量。单看一个样本可能不太明显多对比几个就发现错位是系统性的。原因图像和标签分别做了resize图像用双线性插值标签也用了同样的插值方法。最近邻以外的插值方法会在类别边界处产生“过渡像素值”比如类别1和类别2之间出现1.3、1.7这种非整数。模型输出是离散类别和这种带小数标签对不上训练时梯度反复拉扯。解决标签resize一律用cv2.INTER_NEAREST代码写在3.1节里了。更彻底的排查方法是写一段断言确保标签数组里每个值都是整数且在合法类别范围内在Dataset的__getitem__入口加一次校验可以挡住绝大多数脏数据。4.3 验证集随机划分同一个患者的片子被拆进两边指标虚高现象训练Dice和验证Dice都到0.9以上但换到外部数据或者新患者上效果断崖式下跌。查看数据发现一个患者有多张图像比如一个膝关节X光正侧位就2张这个患者的部分片子在训练集另一部分在验证集。原因随机划分样本时没有按照患者ID分组。模型在训练时见过同一个患者的其他片子相当于提前偷看了答案。医学图像分割数据集中这是一个极容易被忽略的泄漏点症状就是验证指标好看但落地效果差。解决划分数据集时先提取患者ID字段再按患者ID分组划分。把同一个患者的所有图像全部放进训练集或验证集不能用sample级别随机。检查自己数据集里有没有患者维度如果没有明显ID就按序列前缀比如文件名前几位的case号分组。4.4 小骨骼类别整片丢失Dice再高也没有临床价值现象整体Dice达到0.9按类别看股骨Dice0.96髌骨Dice只有0.2。模型几乎没把髌骨分割出来但总的平均Dice被大骨骼拉高很难发现。原因骨骼数据天然类别不平衡。股骨和胫骨区域大髌骨区域小如果损失函数对所有像素一视同仁模型把大部分精力用来优化大区域小类别即使整片丢掉总体损失也只增加很小一点。解决多类别Dice必须逐类计算再平均不能把所有类别当成一个整体算全局Dice。损失函数层面CrossEntropyLoss加上按像素占比倒数计算的类别权重训练日志里每个epoch都打印各类别的Dice而不是只看平均值。只要发现某个类别Dice明显低于其他类别就要考虑小类别增强策略比如对小骨头区域做裁剪训练。4.5 数据增强把标签改坏了旋转和填充引入了幽灵类别现象翻转和旋转增强开了之后训练过程中出现过“意想不到的类别值”比如本来只有4类骨骼加背景训练时报错出现类别5。原因任意角度旋转时图像四角会露出空白区域需要用常量值填充。如果填充值设成和某个骨骼类别一样标签在边界处就会被写成对应类别的编号如果填充后标签区域产生了非整数插值结果那就是新的非法值。更隐蔽的情况是旋转过程中掩膜被插值成了非整数。解决增强配置里一是优先使用90度倍数的旋转比如A.RandomRotate90它不会产生角部空白问题二是必须用A.ElasticTransform这类弹性变换时albumentations内部会保持mask插值为最近邻但填充值要显式指定。检查方法也很简单每个epoch随机抽样几个增强后的标签打印np.unique的值集合确认它和原始类别集合完全一致。人的肉眼很难从可视化图上看出1像素级别的标签破坏所以这类问题要靠数据管线里的断言来防。我的习惯是在增强后加一行代码assert set(np.unique(label)) valid_classes集合判断只要有一次越界就直接中断训练留着跑总比带着病跑完一个epoch再回头查强。5. 用Dice和边界距离验证分割从指标到下游可用的最后一步Dice是医学图像分割最常用的指标但多类别场景下怎么算、算哪个类直接决定你看到的数字有没有意义。def dice_per_class(pred, gt, num_classes): dice_list [] for c in range(num_classes): p (pred c).astype(np.uint8) g (gt c).astype(np.uint8) inter np.sum(p * g) union np.sum(p) np.sum(g) dice_list.append((2.0 * inter) / (union 1e-6)) return dice_list预测输出是argmax后的类别索引与GT逐类别比较。返回的列表里每个元素对应一个类别的Dice这才是你要着重看的指标。整体Dice再高也不能掩盖髌骨这类小骨头Dice只有0.2的问题。Dice对小结构不敏感这是它固有的短板。一个直径10像素的小骨骼即使完全分割错位对整体Dice影响可能不到0.01。想验证模型真的学到了骨骼边界建议加上边界距离指标HD95Hausdorff距离的第95百分位。计算预测边界和真实边界之间的最远距离单位是像素对边缘质量特别敏感。边界错位几个像素Dice可能只掉0.01HD95能直观反映出毫米级别的偏移。验证通过后再谈下游。骨骼分割很少是终点常见下游方向包括分割掩膜用于骨折区域定位把多边形区域输入分类网络判断骨折类型分割结果做中心线提取辅助测量骨角度和长度连续切片的掩膜堆叠起来做三维重建用于手术规划。3500张数据训练的模型如果验证集Dice过0.9、HD95在合理像素范围内完全可以作为下游任务的输入不用再回头折腾分割模型。我自己吃过的教训是指标和可视化必须一起看。Dice再高也要随机抽20个验证集样本把预测和GT叠在一起看边界这一步能看到所有数值指标都看不到的结构性错误。拿到新数据集先跑一遍完整性检查再谈训练这个习惯省下来的时间足够跑完两轮消融实验。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
PromptX ToolSandbox源码剖析:自动依赖安装与CJS/ESM统一加载是如何实现的 PromptX ToolSandbox源码剖析:自动依赖安装与CJS/ESM统一加载是如何实现的 【免费下载链接】PromptX PromptX 领先的AI 智能体上下文平台 | PromptX Leading AI Agent Context Platform 项目地址: https://gitcode.com/Deepractice/PromptX
本文… · 2026/9/26 3:21:56
MySQL JDBC URL 参数调优实战:从字符集、时区到批量插入性能 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:21:56
ABDM v1.8.4实测:多线程分段下载与断点续传 1. 下载工具的选择:为什么我盯上了 AB Download Manager整理下载工具这件事,我陆陆续续折腾了好几年。从浏览器自带的下载器,到各种老牌下载软件,再到命令行的 aria2,基本都试了一圈。说实话,Windows 上不缺… · 2026/9/26 3:21:56
媒体库图片检索与显示实战:用 TaoToken 统一 Key 打通 AI 工具链 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:58:39
万字长文:仅花7天,用Cursor配TaoToken从0到1上线个人网站,保姆级教程! /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:58:39
奇安信天擎卸载全攻略:驱动残留与注册表清理实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 3:58:39
PromptX发布流水线揭秘:标签驱动CI/CD如何让dev/alpha/beta渐进式发布变简单 PromptX发布流水线揭秘:标签驱动CI/CD如何让dev/alpha/beta渐进式发布变简单 【免费下载链接】PromptX PromptX 领先的AI 智能体上下文平台 | PromptX Leading AI Agent Context Platform 项目地址: https://gitcode.com/Deepractice/PromptX
P… · 2026/9/26 3:58:33
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46