
简介面向医学影像分析与深度学习实践者的医疗图像分割项目基于SAM2模型实现高精度分割可用于CT、核磁共振等影像中病变区域与解剖结构的自动识别。压缩包共77个文件整体大小约31.86MB含源码、交互式教程、说明文档、演示图像与视频笔记可直接运行示例图片与动画直观展示分割前后效果。源码部分覆盖模型搭建、训练、推理与预处理教程笔记帮助初学者循序渐进地掌握分割全流程。已有74人学习适合算法研发和课题研究。项目中集成了与多种经典分割网络的对比实验并附带参考文档、权重转换脚本等辅助材料同时提供点提示、文本提示等交互式分割方式便于用户根据实际场景微调感兴趣区域可帮助理解SAM2的改进思路并直接应用至真实医学数据完成训练与评估。1. SAM2做医疗图像分割为什么先要解决的往往不是模型而是数据拿一份“基于SAM2的医疗图像分割项目”源码包最容易翻车的不是模型而是数据CT的HU值没处理、标签格式不统一、单通道灰度图硬塞给三通道编码器。SAM2作为第二代提示型分割模型提示能力很强但输入侧对数据形态同样严格。这份资源把segment_anything工程、训练/推理脚本、GUI、CT/MR预处理工具和教程notebook打包在一起适合找医疗图像分割项目源码来跑实验的算法工程师和研究生也适合想快速验证SAM2落地路径的团队。下面按源码结构、数据准备、训练推理、避坑、进阶技巧五个部分拆尽量做到可复现。2. SAM2的原理与项目源码拆解先把源码结构看清楚再动手2.1 从SAM到SAM2为什么医疗场景值得换SAM2继承的是“提示型分割”的路子一个图像编码器负责提取视觉特征一个提示编码器把框、点、掩码转成条件向量最后掩码解码器根据条件和特征生成分割结果。它和分类网络有本质区别——分类网络输出一个类别标签而SAM2把分割任务拆成“给提示、出掩码”的交互过程。这点在医疗场景里非常关键医生本来就不会像做语义分割那样逐像素框选他们习惯在病灶中心点一下、或者在可疑区域画个框SAM2的交互方式正好对应这个工作流。比前一代更值得换的理由在于两点一是它统一了图像和视频分割的建模方式跨帧上下文让遮挡目标、边界模糊目标的掩码更稳定二是特征提取的层次设计针对细粒度边界做了优化。项目摘要里提到“泛化能力和分割表现提升”本质上就对应这两个改进。同一套权重能吸收多种成像模态的图像不必像传统方案那样针对每个器官训练一个专用分割网络这对样本量不大的医疗子集非常友好。选型还要有对比参照。如果直接上nnU-Net你会陷入另一套调参泥潭归一化策略、resample参数、loss结构都要按数据集反复试DeepLabV3作为经典语义分割基线在小目标边界上通常不如提示型模型稳。这份资源里带了与nnU-Net、DeepLabV3的对比素材想快速评估差异的话直接用仓库里的对比图就能看到结果走势。需要说明的是SAM2的image encoder输入固定为1024×1024的三通道图像这决定了后续所有预处理都要朝这个方向对齐。2.2 源码目录逐项拆解整个资源解压后是完整的工程而不是零散脚本。建议先按下面这张表把文件归一下类再决定从哪个入口切入分组关键文件用途SAM核心库segment_anything/predictor.py点/框/掩码提示的推理入口segment_anything/automatic_mask_generator.py无提示自动生成掩码segment_anything/modeling/图像编码器、提示编码器与掩码解码器网络定义segment_anything/utils/build_sam.py创建模型实例并加载权重训练与推理train_one_gpu.py单GPU微调主脚本train_multi_gpus.py / train_multi_gpus.sh多卡分布式训练脚本MedSAM_Inference.py医疗图推理入口demo.py / gui.py快速演示与GUI交互预处理与工具pre_grey_rgb.py单通道灰度转三通道pre_CT_MR.pyCT/MR数据读取、窗宽窗位处理format_convert.py标注格式统一为掩码split.py训练/验证集划分ckpt_convert.py分布式权重转单卡权重评估与扩展SurfaceDice.py表面Dice指标extensions/point_prompt、text_prompt点提示/文本提示示例seg_3dnii_sparse_marker3D NII稀疏标记相关处理文档与演示tutorial_quickstart.ipynb快速上手notebookMedSAM_supp.pdf方案补充说明文档assets/*.png, *.gif, *.mp4演示图与分割演示视频README.md项目使用说明注意一个细节目录里凡是带.zbak后缀的文件都是上一版被替换时留下的备份不是主文件。典型的是ckpt_convert.py.zbak、train_multi_gpus.py.zbak、setup.py.zbak。新手经常把这些备份改名后当作源文件去跑报错后一头雾水。真正的入口永远是不带.zbak的那个文件。2.3 build_sam与ckpt_convert模型实例化和权重的两个关键点build_sam.py解决的是“模型长什么样”的问题。它不只是new一个类而是把网络配置、预训练权重路径、模型结构三者绑定在一起。SAM2有不同规格的编码器配置不同规格对应不同宽度的特征层如果权重文件和网络配置不匹配加载时必然报size mismatch。所以训练和推理都建议统一走build_sam不要自己手工拼装模型。ckpt_convert.py解决的是“权重哪里来的”问题。用多卡分布式训练产出的权重状态字典里的key通常带module.前缀单卡推理时关键字对不上反过来单卡权重拿去做多卡续训也可能缺前缀。转换脚本就是处理这件事的python ckpt_convert.py --input ./work_dir/MedSAM/last.pt --output ./work_dir/MedSAM/last_convert.pt逻辑说明这个脚本本质是遍历state_dict的key把分布式训练加上的module.前缀去掉同时保持数值不变。参数说明--input给原始训练产物--output给转换后的干净权重转换后的文件再拿给MedSAM_Inference.py加载。我一般会用绝对路径执行避免分布式场景下相对路径找错文件。转换脚本看似不起眼但它在换卡、换部署环境时是刚需特别是从多卡训练切到单卡推理的那一步。3. 数据预处理三件套CT/MR灰度图、格式转换与数据集划分医疗分割项目里数据准备花费的时间往往比训练更长。很多队伍训练脚本没改一行把数据处理好就能涨几个点反过来数据一团糟时调模型结构全是徒劳。这个项目把预处理拆成了三个独立脚本正好对应三件必做的事把医学图像变成模型认识的格式、把标注统一成掩码、把数据切成训练和验证集。3.1 CT/MR与自然图像的第一层差异单通道灰度SAM2的image encoder预训练输入是RGB三通道自然图而CT/MR原生是单通道灰度。直接拿单通道图喂给模型形状都对不上。pre_grey_rgb.py做的事情就是把单通道扩展成三通道但核心不是复制而是复制前先做好数值处理。CT图像的单位是HU值范围通常从-1000到3000以上软组织可能只集中在-100到300之间。如果直接做min-max归一化大部分软组织会被压缩到极低的灰度区间训练出来边界是糊的。正确做法是先做窗宽窗位处理再归一化最后复制成三通道import numpy as np import SimpleITK as sitk # 以CT为例的真实处理顺序 img sitk.ReadImage(case_001.nii.gz) arr sitk.GetArrayFromImage(img) # (D, H, W) 灰度数组 arr arr.astype(np.float32) center, width 40, 400 # 窗位center40, 窗宽width400 low center - width / 2 # -160 high center width / 2 # 240 arr np.clip(arr, low, high) # 先按窗宽窗位截断 arr (arr - low) / (high - low) # 再线性映射到[0,1] arr (arr * 255).astype(np.uint8) # 转三通道 rgb np.stack([arr, arr, arr], axis-1) # 复制成 (H,W,3)逻辑说明先裁窗宽窗位、再归一化、最后复制三通道这个顺序不能反。若先做全局min-max再clip绝大多数体素会被压到极低对比度复制三通道也必须放在数值处理之后避免三通道各自归一化引入无意义的通道间差异。参数说明center40, width400是看软组织的常用窗胸腔、骨窗要按部位换例如骨窗常用center400, width1800MR没有固定的HU范围一般用百分位截断把1%到99%的像素区间映射到0到255效果比固定窗更稳。3.2 标注格式统一从多边形、RLE到单通道掩码医院拿到的原始标注通常不是统一格式有的来自ITK-SNAP存成nii标签有的来自LabelMe输出JSON里是多边形坐标还有数据集直接用COCO的RLE编码。format_convert.py解决的就是多格式归一。多边形转掩码是最常见的场景核心逻辑只有几步import numpy as np import cv2 # annotation[segmentation]里是多边形顶点坐标 polygon np.array(annotation[segmentation]).reshape(-1, 2) mask np.zeros((H, W), dtypenp.uint8) cv2.fillPoly(mask, [polygon.astype(np.int32)], 1)逻辑说明fillPoly是拿多边形点集在空掩码上填充填充值用1表示前景、0表示背景这样得到的是单通道整数掩码配合DiceLoss或BCELoss直接使用不需要再转one-hot。参数说明多边形坐标必须转成np.int32float型坐标在部分OpenCV版本里会直接抛异常H和W要和原图尺寸严格一致不一致时分割结果会整体偏移。如果是COCO RLE格式更省事的做法是用pycocotools的mask.decode直接解码成掩码不用自己写位运算。无论原始标注来自哪个工具最后都统一输出成png或npy的单通道文件这样训练脚本读取时路径就固定下来了。3.3 数据集划分随机split与可视化验收split.py负责把整理好的数据切成训练集和验证集常见用法python split.py --data_root ./work_dir/dataset --train_ratio 0.8 --seed 42逻辑说明脚本会把data_root下的图像和对应掩码按同一套随机序列打乱再按比例切分保证图像和标签不会错位配对。参数说明--train_ratio按数据量调数据量大可以设0.9标注质量一般时0.8更稳--seed务必固定否则每次划分结果不同实验就没法对比了。划分完成后不要直接进训练先做可视化验收python demo.py --image ./work_dir/dataset/sample.png它会输出一张叠加分割结果的可视化图和assets/seg_demo.gif、img_demo.png对照看能识别出标注偏移、灰度异常这类问题。我自己的习惯是每次改完预处理先随机抽10张图跑一遍可视化再进训练不然训练到一半才发现输入全黑浪费的时间和算力都不划算。4. 训练闭环与推理验证单卡、多卡和GUI一条线跑通数据准备好后进入训练环节。这套工程把训练入口拆成单卡和多卡两个版本还有一个GUI用于交互式验证基本覆盖了从实验到落地的完整闭环。4.1 单卡训练怎么设参数关键词是“用哪个脚本、什么参数起步”。train_one_gpu.py是单卡微调的固定入口它内部会先调build_sam创建模型再按提示型分割的范式做训练从标注里采样一个box框住目标把box编码成提示条件让mask decoder生成掩码和GT算Dice/loss后反传。我一般这样启动cd 医疗图像分割_基于SAM2实现的高精度医疗图像分割算法 export PYTHONPATH$(pwd) python train_one_gpu.py \ --data_root ./work_dir/dataset \ --ckpt ./work_dir/MedSAM/sam2_pretrain.pt \ --image_size 1024 \ --lr 1e-4 \ --batch_size 4 \ --epochs 60 \ --fp16参数说明--image_size是SAM2的输入分辨率保持1024和预训练权重配套不要随意改成512否则位置编码和特征图尺寸都要重新适配--lr从1e-4起步比自然图像任务的常见值略激进但医疗目标边界清晰时收敛更快--batch_size单卡用4在常见16GB显存下比较稳显存小就降到2--fp16开混合精度训练能在几乎不掉点的情况下省三分之一左右的显存。如果数据量只有几十到几百张建议把image encoder冻结只训练mask decoder。对应到脚本里一般是--freeze_encoder这类选项默认值以代码为准。冻结编码器后模型相当于只学习“怎么把通用特征翻译成医疗掩码”可以显著降低过拟合风险。数据量大、且目标器官和自然图像差异极大时再考虑解冻全部参数端到端微调。4.2 多卡训练脚本与脚本之间的区别train_multi_gpus.py是分布式训练主脚本train_multi_gpus.sh是把命令封装好的启动脚本。两者本质相同.sh里通常先export PYTHONPATH再用torchrun拉起多卡。直接跑bash train_multi_gpus.sh如果想自己掌控启动方式等效命令是export MASTER_ADDR127.0.0.1 export MASTER_PORT29500 torchrun --nproc_per_node4 --master_addr$MASTER_ADDR --master_port$MASTER_PORT \ train_multi_gpus.py --data_root ./work_dir/dataset --batch_size 4参数说明--nproc_per_node4表示用4张卡按机器实际显卡数量改--batch_size以每张卡为单位多卡总批量等于卡数乘以batch_size但学习率不要跟着线性放大微调场景下保持1e-4左右更安全放大学习率容易在第一个epoch就发散。MASTER_ADDR127.0.0.1适用于单机多卡跨机训练时要改成实际主节点IP否则从节点连不上。这里再提醒一次目录里的train_multi_gpus.sh.zbak是旧备份直接拿去bash会报语法错误因为它可能是上一个版本的遗留文件。多卡训练任务一旦启动先看每个进程的日志输出确认NCCL初始化成功再离开终端。4.3 推理测试MedSAM_Inference、demo与gui训练结束后的验证分成三档用MedSAM_Inference.py做纯脚本推理用demo.py直接可视化单图结果用gui.py手动交互式点选或拖框。推理命令python MedSAM_Inference.py \ --image ./work_dir/sample/case_001.png \ --box 120,80,240,220 \ --ckpt ./work_dir/MedSAM/sam2_best.pt \ --output ./work_dir/pred/case_001.png参数说明--box是字符串形式的左上右下四个坐标格式为x1,y1,x2,y2如果框来自目标检测器输出记得把坐标转成int并做边界clip防止超出图像尺寸导致解码失败。如果没有检测框可以尝试用automatic_mask_generator.py先全图生成候选掩码再挑选但医疗场景全图生成会有大量噪声我一般还是优先以医生给的框或点为准。gui.py是这份资源里最容易让合作医生产生信任感的部分打开界面后在影像上拖一个矩形模型立刻返回掩码。它内部复用predictor.py的set_image加predict接口不需要单独写服务端。如果团队没有前端开发经验直接用这个脚本做标注工具原型能省下两三周的界面开发时间。后续想部署到Web端可以关注SAM2量化模型相关的转换路径但训练阶段先别动量化精度优先。5. 避坑专项从源码到跑通必查的四个翻车点5.1 ModuleNotFoundErrorsegment_anything 导入失败现象第一次运行train_one_gpu.py还没来得及看参数就报ModuleNotFoundError: No module named segment_anything。原因资源包里的segment_anything是源码目录不是pip装好的第三方包。当前路径不在Python的模块搜索范围内命令运行时自然找不到。解决先完成包安装选一种方式export PYTHONPATH$PWD # 或 pip install -e .我一般用pip install -e .这条命令会把包入口固定到当前目录后续修改代码不用重复安装如果多个项目共用同一个Python环境、不想污染依赖就只用export PYTHONPATH。判断是否成功可以进Python后import segment_anything试一下不报错再跑训练脚本。5.2 加载权重时报size mismatch或key不存在现象模型实例化成功但到load_state_dict时报一堆size mismatch for xxx或者Ran out of keys / unexpected key module.xxx。原因训练产物来自多卡分布式状态字典的key带module.前缀和单卡模型的关键字对不上或者拿到的权重与当前网络配置不是同一规格。work_dir/MedSAM下同时放着原始权重、转换权重和备份文件路径一搞错就会加载到错误版本。解决先用ckpt_convert.py把权重大脑过一遍再进入推理python ckpt_convert.py --input ./work_dir/MedSAM/last.pt --output ./work_dir/MedSAM/last_convert.pt注意转换后的文件是给单卡推理用的如果转换后再拿去做多卡续训反而会因为缺少module.前缀引起新一轮报错。训练流程里用原始pt推理流程里用converted pt两个文件分开放不要混用。5.3 显存OOM单卡、小卡怎么收着跑现象image_size设为1024、batch_size设为8、不开fp16在消费级显卡上训练几步就CUDA out of memory。原因SAM2的image encoder比分类网络大得多1024分辨率的前向特征图占用的显存远超常规网络医疗2D切片本身分辨率又高512乘以1024再乘三通道显存自然不够用。解决优先开--fp16并把batch_size降到2或4还不够就把image encoder冻结只训练mask decoder再不行把训练输入降采样到512但推理时保持1024细节会更好。推理阶段OOM时用滑动窗口把切片分成带overlap的patch逐块预测再拼接。拼接时overlap区域要加权融合不能硬拼否则会出现明显的接缝这个问题在3D NII数据上尤其明显。5.4 图像全黑或对比度极低现象预处理后的png看起来像一张黑纸但直接查看nii.gz文件的数值范围又是正常的。原因CT的HU值或MR的信号强度范围很大没有做窗宽窗位或分位数截断直接min-max归一化后绝大部分软组织被压到接近0。这是医疗图像分割项目里最常见的翻车点也是最容易被忽略的一步。解决CT按部位设window/level胸腔看软组织用center40, width400这类经验值骨窗要换成center400, width1800MR用百分位截断把1%到99%的像素区间映射到0到255。关键点是先做窗宽窗位再复制三通道不要在RGB每个通道上各自归一化否则相当于给模型注入无意义的通道间噪声。6. 进阶技巧把框提示改成点提示小目标分割拿到不一样的效果如果只是复现流程用box提示就够了。但医疗场景里小病灶经常只有十几个像素框稍微画大一点就会把周围组织带进来分割结果随之膨胀。我建议在gui.py里把交互方式改成point prompt医生在病灶中心点一下SAM2把它作为前景点掩码通常比大框更收敛。改成点提示的代码量很小import numpy as np # 模拟gui里鼠标点击位置 point np.array([[cx, cy]]) # cx, cy为点击坐标 label np.array([1]) # 1前景, 0背景逻辑说明先调用predictor.set_image完成图像特征提取再调用predict接口时把point_coords和point_labels传进去其余逻辑和box一致。参数说明label0可以表示背景点如果模型对前景点有误判在背景区域补一个label0的点两个点一起给模型很多边缘粘连问题会被拉回来。项目里的extensions/point_prompt和extensions/text_prompt就是干这个用的点提示对应病灶中心点击文本提示则尝试把器官英文名编码进提示条件。文本提示对数据分布敏感建议只做探索性实验不要直接上生产。seg_3dnii_sparse_marker则适合3D体数据稀疏标记点本身就可以作为point prompt的种子把2D逐层分割串成3D结果。改成点提示后再用普通Dice评估就看不出明显差别因为两者掩码的差异集中在边界那几层像素。我改用项目自带的SurfaceDice.py做回归测试——表面Dice对边界偏移比体积重叠更敏感适合验证提示方式带来的真实收益python SurfaceDice.py --pred ./work_dir/pred/point --gt ./work_dir/label --smooth 1参数说明--smooth控制表面距离容差建议从1到2个像素看容差太小时标注噪声都会被放大为分数波动太大时又看不出边界改进。从那以后我每换一个数据集都强制先跑一次baseline推理并打SurfaceDice分再决定是调预处理还是换提示方式不再凭肉眼判断效果。这个“点提示加表面Dice”的组合帮我挡掉了至少三四次看起来很好、回调时边界翻车的情况希望帮到你。本文还有配套的精品资源点击获取