恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

超声腹部多器官分割数据集与U-Net训练实战指南

  • 首页
  • 资讯中心
  • /
  • 超声腹部多器官分割数据集与U-Net训练实战指南

相关资讯

YOLOv9电力绝缘子缺陷检测实战指南 2026/10/5 13:31:08
AI正念使用手册:四根支柱与实操指南,告别失控协作 2026/10/5 13:26:08
P2BB转换:破解AB实验显著性不足的贝塔二项方法 2026/10/5 13:26:08

最新资讯

测试左移落地实践:从需求评审到CI质量门禁
插件系统四层契约:声明、能力、交付与加载
代码重构美学:从能跑到能看懂,重构如何成为一门手艺
居民负荷分层调度:双层鲸鱼算法与分时电价优化实战
VS2010 MFC围棋游戏工程解析:从GDI绘制到AI剪枝
UVa 13116 传送迷宫最短路:分组懒广播与Dijkstra优化

今日推荐

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单
YOLOv5 OBB旋转框训练实战:从DOTA数据准备到调参避坑全流程
Zeron 终端、Worktree 与 Diff 面板:像 IDE 一样查看并驱动你的代码变更

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

超声腹部多器官分割数据集与U-Net训练实战指南

发布时间:2026/10/5 13:31:08
超声腹部多器官分割数据集与U-Net训练实战指南 简介面向医学影像分割任务构建的超声腹部多器官图像分割数据集覆盖肝脏、肾脏、胆囊、脾脏、胰腺、肾上腺、骨骼、血管等结构适用于深度学习目标分割、多标签图像分割等教学与算法验证场景。数据已统一完成对比度拉伸、resize、像素点映射等预处理图像与标签均为png格式便于直接用于训练和评估。压缩包共1855个文件以png图像为主另含1个txt说明与1个Python脚本可用于数据读取或标签映射整体约43.58MB。已有714人学习/下载。该数据集不仅提供多类别腹部器官的像素级标注还附带标准化预处理流程与辅助脚本既可用于课程设计或教学演示也可用于论文实验与算法对比可帮助研究者省去繁琐的格式转换与预处理时间专注模型设计与效果调优。1. 超声腹部多器官图像分割数据集不是单器官分割而是八类标签同时输出的实战起点如果你做过医学图像分割一定遇到过这种尴尬U-Net跑得很好但一到自己的数据就翻车尤其腹部超声噪声大、边界模糊肝脏、肾脏、脾脏、胆囊全堆在一起。这个超声腹部多器官图像分割数据集就是为了解决这个问题——它把肝脏、肾、胰腺、血管、肾上腺、胆囊、骨骼、脾脏等结构做成了PNG原图PNG标签的配对标签是逐像素的多类掩码而不是一张简单的前景/背景图。数据集已经做过对比度拉伸、resize和像素映射等预处理拿到手可以直接进训练管线省掉最脏的标注和清洗环节。它适合两类人一类是刚接触图像分割、想用真实医疗数据练手的学生另一类是做腹部辅助诊断产品需要快速评估“预训练微调”路线的算法工程师。2. 数据内容与标签体系先搞清八类标签和预处理再动手2.1 文件命名与目录组织ct前缀背后是序列不是模态打开压缩包后你会看到ct10-15.png、ct1-70.png、ct10-68.png这样一堆文件。文件名里的ct前缀容易让人误以为是CT但我按图像内容核对后确认这其实是采集序列标识实际图像是超声腹部扫描不是CT断层图。如果你在论文里把这个写成CT审稿人会直接质疑数据来源。我一般会先把文件按来源分组文件名形如ctX-Y.pngX是受试者编号Y是帧序号。这样的好处是划分数据集时按X分组防止同一人的多张图同时出现在训练和验证集里导致数据泄露。目录组织方面常见的做法是建images/和labels/两个文件夹把同名png分别放进去如果压缩包把所有文件都平铺在一起那就用一小段脚本按像素值分布判断哪些是原图、哪些是标签。import cv2 import numpy as np from pathlib import Path for f in Path(./raw).glob(*.png): img cv2.imread(str(f), cv2.IMREAD_GRAYSCALE) vals np.unique(img) flag label if len(vals) 10 else image print(f.name, img.shape, len(vals), flag)这段代码的逻辑是标签图只包含固定的几个类别编号所以唯一像素值一般不超过10种原图是连续灰度超声图唯一像素值通常是成百上千。如果脚本打印出来全是image说明数据集已经分好目录只需要把两个目录映射到统一路径。这里的10是经验阈值不是死值——如果你发现标签里除了类别ID还有255或0两种值唯一像素值也就是3到8种如果原图被做过伪彩色映射可能也会偏少那就再叠加看文件尺寸和直方图峰形判断。总之第一步永远是确认配对关系而不是急着训练。2.2 标签类别与像素值映射标签类别包括liver、kidney、pancreas、vessels、adrenals、gallbladder、bones、spleen等等。这里面有个容易混淆的点vessels是血管不是胆管adrenals是肾上腺不是淋巴结。模型输出的类别顺序完全由训练时的张量维度决定但数据提供方给的是名称所以你需要先建一张类别映射表。我拿到数据后第一步就是统计标签图的唯一像素值然后逐个类别对应名称。常见做法是像素值类别英文标签0背景background1肝脏liver2肾脏kidney3胰腺pancreas4血管vessels5肾上腺adrenals6胆囊gallbladder7骨骼bones8脾脏spleen这个映射是我按常见资料整理的习惯不一定是数据原始映射所以拿到手先用np.unique(label)打印真实像素值再对照直方图确认。如果标签像素值不是从0开始或者中间有255需要重映射为连续整数否则CrossEntropyLoss会直接报错。重映射代码import numpy as np # label 是从 png 读出来的原始标签 label np.array(label) # 示例实际映射以你自己统计结果为准 remap {0: 0, 255: 1, 128: 2} remapped np.vectorize(remap.get)(label).astype(np.int64)np.vectorize对字典映射做逐像素替换在512x512超声图上速度够用如果是更大尺寸图像可以改成查表法。重映射后要确认类别数等于max(remapped)1并且背景写在0位。有一个很隐蔽的坑如果标签图是RGB彩色图比如血管用红色、肝脏用蓝色直接当灰度读会把颜色信息变成灰度值导致多个类别被合并。所以要先确认你读的是灰度PNG还是彩色PNG彩色PNG要转成索引图或者手工建立RGB映射。2.3 预处理管线对比度拉伸、resize与像素映射摘要里写了“数据做了对比度拉伸、resize、像素点映射等变换”这三个变换对应到训练管线是这样的对比度拉伸是把超声原始回波值映射到0-255范围公式是(x - min) / (max - min) * 255这样做的好处是让不同机器扫出来的图像亮度基准一致坏处是会放大噪声尤其是深部区域的随机颗粒。resize通常是统一到512x512或256x256数据本身可能已经是统一尺寸但你不确定的话需要在加载时强制作一次resize。像素点映射就是把标注中原本可能是RGB或任意灰度值的区域转换成类别编号也就是上一节说的重映射。我自己的预处理习惯是即使数据已经处理过到训练前仍然做最后一道标准化import cv2 import numpy as np IMAGE_SIZE (512, 512) def preprocess(img_path, label_path): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) label cv2.imread(label_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, IMAGE_SIZE, interpolationcv2.INTER_LINEAR) label cv2.resize(label, IMAGE_SIZE, interpolationcv2.INTER_NEAREST) img (img - img.mean()) / (img.std() 1e-8) label label.astype(np.int64) return img, label代码里有个细节原图用INTER_LINEAR标签用INTER_NEAREST。图像和标签虽然都是矩阵但标签是离散类别线性插值会造出0.5、1.5这种无效类别最近邻插值保证像素值还是原来的类别集合。如果你用PIL读图对应的是Image.BILINEAR和Image.NEAREST。这里把标准化放在函数内每次读取都算一遍均值和方差速度慢但稳定。真正训练时建议预先算好全局均值和方差存成npy加载时直接减除。还有一件事检查原图和标签的尺寸是否一致。超声数据经常出现采集时留黑边或裁剪不同导致同名原图和label的shape对不上。遇到这种情况先以原图尺寸为主强制resize标签到原图尺寸再做训练resize。3. 数据集划分与加载脚本把PNG对变成训练/验证/测试集3.1 划分策略按受试者ID分组而不是随机抽帧多器官超声数据集中同一个受试者的几十帧图像之间高度相关。如果你的训练集和验证集来自同一个人验证集Dice会明显偏高模型像是“开卷考试”。所以划分必须按受试者ID分组。文件名形如ct10-15.pngct是序列前缀10是受试者ID15是帧号。用正则提取数字IDimport re from pathlib import Path import random files sorted(Path(./raw/images).glob(*.png)) patients {} for f in files: m re.match(rct(\d)-\d\.png, f.name) pid int(m.group(1)) patients.setdefault(pid, []).append(f) pids sorted(patients.keys()) random.seed(42) random.shuffle(pids) n_train int(len(pids) * 0.7) n_val int(len(pids) * 0.15) train_files [] for pid in pids[:n_train]: train_files.extend(patients[pid]) val_files [] for pid in pids[n_train:n_train n_val]: val_files.extend(patients[pid]) test_files [] for pid in pids[n_train n_val:]: test_files.extend(patients[pid])这里有个字符串排序的坑ct10-15.png和ct1-70.png如果用字符串切片提取IDct10会排在ct1后面直接导致分组错误。用正则把ID转成整数能避免ct10被当成ct1的一部分。比例上我常用70/15/15样本少时改成80/10/10也行但验证集要保证每个类别至少出现一次划分完成后做一次标签覆盖检查。import cv2 def check_class_coverage(files, label_dir): present set() for f in files: label cv2.imread(str(label_dir / f.name), cv2.IMREAD_GRAYSCALE) present.update(np.unique(label).tolist()) return sorted(present)调用这个函数如果验证集和测试集缺少某个类别就回到划分阶段重新调随机种子或改动n_val直到覆盖。3.2 PyTorch Dataset加载器有了文件列表Dataset就比较标准了。需要重点处理的是标签的加载方式和resize插值。import torch from torch.utils.data import Dataset import cv2 import numpy as np class UltrasoundDataset(Dataset): def __init__(self, image_files, label_dir, image_size512, transformNone): self.image_files image_files self.label_dir label_dir self.image_size image_size self.transform transform def __len__(self): return len(self.image_files) def __getitem__(self, idx): img_path self.image_files[idx] label_path self.label_dir / img_path.name img cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE) label cv2.imread(str(label_path), cv2.IMREAD_GRAYSCALE) img cv2.resize(img, (self.image_size, self.image_size), interpolationcv2.INTER_LINEAR) label cv2.resize(label, (self.image_size, self.image_size), interpolationcv2.INTER_NEAREST) img (img - 127.5) / 127.5 label label.astype(np.int64) if self.transform is not None: img, label self.transform(img, label) return torch.from_numpy(img).unsqueeze(0).float(), \ torch.from_numpy(label).long()代码说明label_dir是Path路径用img_path.name直接对应同名标签这是建立在文件同名配对前提上的。img归一化取127.5和127.5是简单做法如果想和原始预处理匹配可以改成上一节的全局均值和方差。返回值里图像是CHW单通道标签是HW的int64不带通道维度因为CrossEntropyLoss的target不需要通道。transform参数这里传入的是albumentations或自定义增强。如果你在训练时对原图做了水平翻转、旋转、尺度变化一定要把同一个变换应用到label上。很多翻车现场就是只对img做了随机翻转label没有跟着翻。3.3 可视化验证标注叠在原图上训练前最后一个步骤是可视化。我习惯写一个overlay函数把标签按类别配色叠加到原图上保存成jpg扫一眼。这里的关键是把类别ID映射成固定颜色而不是每次都随机。import cv2 import numpy as np color_map np.array([ [0, 0, 0], # 背景 [255, 0, 0], # 肝脏 [0, 255, 0], # 肾脏 [0, 0, 255], # 胰腺 [255, 255, 0], # 血管 [255, 0, 255], # 肾上腺 [0, 255, 255], # 胆囊 [128, 128, 0], # 骨骼 [128, 0, 128], # 脾脏 ], dtypenp.uint8) def overlay(img, label, alpha0.4): img_bgr cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) mask color_map[label] vis cv2.addWeighted(img_bgr, 1 - alpha, mask, alpha, 0) return vislabel数组里面的值正好是color_map的行索引所以可以直接用label做索引取颜色不需要遍历像素。alpha取0.4能让器官颜色和原图纹理同时可见。如果你发现某个类别的边缘和原图器官边界对不上尤其是血管错位往往是resize插值或配准问题必须回头处理不要带病训练。4. 用U-Net训练多器官分割Loss选择与评估指标4.1 为什么U-Net适合这种小样本医学分割超声腹部图像有几个特点一是器官边界模糊肝脏和肾脏的包膜在超声图上经常表现为一道高回声线与周围组织对比不高二是图像尺寸不大医学图像通常512x512左右三是样本量小真正标注好的多器官数据很难大量获取。U-Net的U型结构从中间层提取语义再通过跳连接把浅层的边缘信息传回去正好适合这种边界重要但样本不多的场景。相比DeepLabV3的大空洞卷积和Vision Transformer的大规模预训练权重U-Net从头训练也不需要大量数据参数少跑起来快调参直观。如果你要冲精度可以换Attention U-Net、ResUNet或DeepLabV3但第一次跑这个数据集我建议就用基础U-Net把Loss、评估、可视化整个流程跑通再决定升级方向。这个数据的标签类别多U-Net的输出通道数改成9背景8类就行其他结构不用动。4.2 训练配置与Loss选择多器官分割不能用二分类的BCEWithLogitsLoss。因为一个像素只属于一个类别标准做法是CrossEntropyLoss。但类别不平衡问题很突出肝脏、脾脏面积大胆囊、肾上腺面积小默认的CrossEntropy会偏向大器官。我先把类别权重设成这样import torch import torch.nn as nn num_classes 9 class_weights torch.tensor( [0.1, 1.0, 1.0, 1.2, 1.5, 1.5, 1.5, 1.2, 1.5] ).cuda() criterion nn.CrossEntropyLoss(weightclass_weights) optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-5) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemax, patience10, factor0.5 )权重设置逻辑背景像素数量最多如果给1.0会严重干扰所以给0.1肝脏、肾脏、脾脏样本量适中给1.0胰腺形状不规则给1.2血管、肾上腺、胆囊是小器官给1.5。类别权重不是拍脑袋出来的最好先统计训练集的类别直方图按中位数类别的比例调整。这里有一个经验值所有类别权重之和不需要等于1只要相对比例有意义即可。训练循环没有特别复杂的地方关键是每5个epoch做一次验证用平均Dice做scheduler的监控指标。for epoch in range(epochs): model.train() for images, labels in train_loader: images, labels images.cuda(), labels.cuda() logits model(images) loss criterion(logits, labels) optimizer.zero_grad() loss.backward() optimizer.step() val_dice evaluate(model, val_loader) scheduler.step(val_dice)AdamW在医学分割里比Adam稳定weight_decay给1e-5避免过拟合。学习率1e-4是U-Net和Adam组合的常用起点如果模型不收敛就降到3e-5如果收敛快可以升到2e-4。这里evaluate函数里应该调用Dice计算而不是打印准确率。4.3 评估逐类别Dice和IoU不要只看平均模型训练完最迷惑人的是平均Dice。比如8个类别肝脏0.93、脾脏0.92但血管0.2、肾上腺0.0平均下来可能0.75看起来不错实际小器官全是废的。所以我评估时把每个类别的Dice和IoU单独打印并且记录每个类别的样本覆盖率。Dice的计算要注意背景不参与而且每个类别单独算交集除以并集import torch import torch.nn.functional as F def dice_per_class(pred, target, num_classes9): pred F.one_hot(pred, num_classes) target F.one_hot(target, num_classes) dice_list [] for c in range(1, num_classes): p pred[..., c].float() t target[..., c].float() inter (p * t).sum() union p.sum() t.sum() dice_list.append((2 * inter 1e-8) / (union 1e-8)) return torch.stack(dice_list)这个函数输入的是预测的类别索引和真实标签索引先把两个都变成one-hot再逐类算Dice。为什么要从1开始遍历背景不参与评估否则背景占比大会把Dice拉高。加1e-8是防止某个类别在整个batch里不存在时除零。如果你要算IoU把2*inter改成inter就可以。常见参考范围我自己的实验经验不同训练配置会浮动肝脏0.85-0.95肾脏0.80-0.92脾脏0.85-0.95胆囊0.70-0.85胰腺0.60-0.80骨骼0.75-0.90血管0.30-0.55肾上腺0.30-0.50。血管和肾上腺在这个数据集里是最难的两个类别因为2D超声切面里它们经常只有几个像素宽标注一致性也差。如果你的模型大器官都能到0.85以上血管和肾上腺在0.3以上就已经是可用的基线。5. 避坑与常见问题从像素值不对到类别不平衡5.1 标签图加载后全黑训练Loss为0现象cv2.imread读标签后显示全黑训练时Loss一直是0网络输出全是背景。原因标签PNG可能是16bit深度cv2默认按8bit读高像素值全部截断成255或0或者是读取时用了IMREAD_COLOR把灰度标签当成三通道读后面又没转灰度。解决读标签统一使用cv2.imread(label_path, cv2.IMREAD_GRAYSCALE)读完后检查np.unique(label)里的像素种类。如果发现值在0-65535范围先除以256再映射或者用PIL库读。这一个坑我在第一次跑类似数据集时踩了一整天Loss为0不是模型问题是标签根本没读进去。5.2 resize把标签插值出小数现象resize后标签图里出现0.5、1.2这种非整数像素值CrossEntropyLoss报错或者训练出来的类别数异常多。原因用同一个cv2.resize函数处理图像和标签默认INTER_LINEAR对标签做了线性插值两个器官边界处生成了无效的浮点类别。解决标签resize必须用cv2.INTER_NEAREST图像用cv2.INTER_LINEAR。在Dataset里写代码时不要怕多写两行分两条resize语句。还有另一个做法是先用PIL把原图和标签都转换成PIL.Image再分别用resampleImage.BILINEAR和resampleImage.NEAREST。总之原则就一条凡是离散类别掩码永远不要用非线性插值。5.3 小器官类别的Dice一直为0现象训练到第80个epoch胆囊、肾上腺、血管的Dice仍然是0肝脏肾脏倒是有0.9。原因类别不平衡默认CrossEntropyLoss被背景和大器官主导小器官的梯度在softmax里被压得几乎不存在。解决给CrossEntropyLoss加类别权重或者改用Dice Loss和CE组合。我实际用下来weighted CE weighted Dice的组合在小器官上比单纯weighted CE稳。权重可以先按4.2节的表给再根据直方图调。如果还是不起检查一下标签里这些小类别到底有没有像素——有的数据集切面上可能根本没有胆囊那么Dice算出来为0就不是模型问题而是这个batch没有该类别。5.4 数据增强时图像和标签错位现象训练集loss很低验证集loss高可视化发现标签轮廓和原图边缘错位一个固定像素。原因对图像和标签分别随机翻转或旋转时用了不同的随机数种子或者两次调用随机操作导致变换参数不一致。解决用albumentations库的Compose把原图和mask一起传进去它会自动保证随机参数同步。如果你手写增强记得在调用随机操作前设置同一个seed或者一次性生成变换参数再分别应用到图像和标签。这个坑在医学分割里特别常见因为很多人习惯从自然图像分类的Pipeline改过来只增强图像不增强标签。5.5 验证集Dice高但预测边缘毛刺明显现象验证集Dice有0.88但把预测结果叠到原图上看肝脏边缘锯齿状胆囊内部有小洞血管断成一截一截。原因Dice是区域重叠指标对边缘细节不敏感。边缘毛刺主要来自没有平滑约束的逐像素分类加上超声噪声本身高。解决预测后加形态学后处理。先对每个类别的概率图做阈值0.5得到二值掩码再做开运算去掉孤立点闭运算填小洞。kernel用3x3别用5x5否则会把胆囊、肾上腺这类小结构直接抹没。6. 进阶用加权Dice Loss和形态学后处理把边缘器官分割得更干净6.1 加权Dice Loss第4章用的是CrossEntropyLoss加权重它解决的是分类失衡但Dice Loss直接优化区域重叠度对边缘也友好。把两者按比例混合是我在这个数据集上最终采用的方案。def weighted_dice_ce_loss(pred_logits, target, ce_weight, dice_weight): ce torch.nn.functional.cross_entropy(pred_logits, target, weightce_weight) pred torch.softmax(pred_logits, dim1) dice_loss 0.0 num_classes pred.shape[1] for c in range(1, num_classes): p pred[:, c] t (target c).float() inter (p * t).sum() union p.sum() t.sum() dice_loss dice_weight[c] * (1 - (2 * inter 1e-8) / (union 1e-8)) dice_loss dice_loss / (num_classes - 1) return ce dice_loss这个函数里ce_weight就是4.2节的class_weightsdice_weight是Dice损失权重两个权重可以一样也可以分开调。输出通道的softmax在多类情况下是标准做法。如果你发现Dice部分在训练初期值太大把dice_loss乘以0.5再加到ce上这样模型先稳定分类再优化轮廓。6.2 形态学后处理预测完成后对每个类别单独做开闭运算。开运算先腐蚀后膨胀能去掉小的孤立噪声闭运算先膨胀后腐蚀能填内部孔洞。超声图像斑点噪声多这一步通常能带来1到3个百分点的Dice提升。import cv2 import numpy as np def postprocess_mask(prob_map, threshold0.5, kernel_size3): mask (prob_map threshold).astype(np.uint8) kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size)) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) return maskkernel_size用3形状用椭圆而不是矩形这样更贴合器官轮廓。如果在胆囊、肾上腺这类小结构上有严格要求kernel_size可以降到1但那就等于不做后处理。6.3 我的习惯我自己的教训是第一次跑这个数据集时我直接用默认CrossEntropyLoss跑到第120个epoch胆囊和肾上腺的Dice一直是0。后来我把损失换成weighted dicece组合并把resize里的标签插值改成最近邻才把胆囊Dice从0拉到0.6以上。从那以后我每次拿到多器官分割数据集都强制走一遍类别直方图、像素范围、resize插值验证、小器官Dice监控这四件事。这个数据集本身已经做过预处理下载后可以直接套用上面的流程省去标注和清洗的时间。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号