简介面向计算机视觉、深度学习与图像分割开发者的SAM2模型资源包基于Ultralytics框架整合可用于任意目标的分割实验。压缩包共7个文件其中4个pt权重文件覆盖tiny、base、small、large不同模型规模2个Python脚本分别实现带提示词分割与全图分割1张jpg样例图便于快速验证效果包体约690.55MB。已有1265人学习下载。借助点、框等提示词可精准分割任意目标也可一键完成全图分割。资源内含可直接运行的测试代码完整呈现从权重加载、输入图像处理到掩码输出的过程方便对比不同尺寸模型的精度与速度差异并帮助理解Ultralytics与SAM2的集成方式节省二次开发中的配置与调试时间。1. SAM2图像分割为什么大家开始把老模型换掉做图像分割的工程师这两年应该有一个明显感受Mask R-CNN、U-Net这些老将还在用但一到需要交互式分割、需要点一下就能把目标抠出来的场景传统模型就有点吃力了。SAM2Segment Anything Model 2是Meta推出的第二代分割基础模型和第一代相比最大的变化是支持视频分割、对提示更敏感、掩码质量更高。而Ultralytics框架把SAM2封装成了和YOLO一样的predict接口你不用去读官方repo那一大堆配置三行代码就能在本地把单张图片、摄像头流、视频帧跑出掩码来。这篇笔记就是讲我怎么用Ultralytics跑SAM2、参数怎么调、踩过哪些坑。适合正在做广告牌图像分割系统、医学图像分割实验或者想把SAM2接进现有检测管线的工程师——新手可以照着跑通熟手可以跳过前两章直接看坑。2. Ultralytics框架下的SAM2环境安装、权重与最小跑通2.1 为什么选Ultralytics而不是官方Segment Anything仓库Meta官方发布SAM2时给的是研究向代码推理脚本是Python入口模型配置用YAML描述训练和评估的代码耦合很深。如果你只想要一个能出掩码的接口官方仓库带来的不是功能是配置成本。Ultralytics的做法是把SAM2包装成和YOLO同一套APImodel SAM(sam2_b.pt)然后model.predict(source)输出的results对象里直接挂着掩码、坐标、置信度。这套封装的好处有三点第一模型文件是统一的.pt格式换模型不用改代码第二predict参数和YOLO共用一套语义conf、device、imgsz这些你已经熟悉了第三Results对象有plot()方法调试可视化不用自己写OpenCV拼接。研究向的人可能会反驳官方仓库能训练、能评估Ultralytics只能推理。这话对一半。Ultralytics的SAM2接口从0.3.0版本之后确实加入了训练入口但实际用下来训练脚本主要覆盖点提示、框提示的微调视频分割的时序训练支持还比较薄。所以我的建议是如果你的目标是快速拿掩码、接业务、做验证Ultralytics足够如果你要做视频分割方向的研究实验再去看官方仓库也不迟。工程和科研的侧重点不一样别指望一个框架把两件事都做到极致。2.2 ultralytics安装与依赖检查安装本身不复杂pip install ultralytics一句就能装好。但SAM2的推理对PyTorch和CUDA版本有隐性要求我见过太多人在这里翻车ultralytics装好了import正常一加载SAM2权重就报CUDA error: no kernel image is available for execution on the device。这个报错的意思是你的PyTorch编译时用的CUDA架构和你显卡的算力不匹配最常见的就是老显卡GTX 10系列、20系列装了默认的PyTorch CPU版或者太高版本的CUDA。我的建议是先建一个干净的虚拟环境再按顺序装conda create -n sam2 python3.10 -y conda activate sam2 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics python -c import ultralytics, torch; print(ultralytics.__version__); print(torch.cuda.is_available())这里python3.10是因为SAM2官方权重和Ultralytics的依赖在3.9和3.11下都有过兼容性反馈3.10是一个比较稳的中间值。torch和torchvision单独装是为了让CUDA版本可控后面那行index-url指定的是CUDA 11.8版本——如果你的显卡是30系或更新可以改成cu121或cu124但注意别用默认的PyPI源默认装的是CPU版torch.cuda.is_available()会打印出False。最后一行是验证能看到ultralytics版本号且cuda返回True环境就算通了。如果输出False不要继续往下走先回头检查显卡驱动和PyTorch版本。2.3 三行命令跑通SAM2图像分割环境没问题之后最小推理代码比我预想中还短。我一般会写到四行因为要把device和conf显式写出来防止后续忘了改from ultralytics import SAM model SAM(sam2_b.pt) results model.predict( bus.jpg, devicecuda:0, conf0.4, retina_masksTrue, saveTrue, projectsam2_output, namebus_demo ) print(results[0].masks.shape)这个代码块里SAM(sam2_b.pt)会自动下载模型权重如果你有代理或者内网镜像第一次加载可能会卡在下载阶段后面会专门讲这个坑。predict里的device指定显卡conf控制的是提示点和提示框的置信度阈值SAM2的机制和YOLO的置信度不是一回事但直觉上相同太小会冒出很多碎掩码太大会把目标整体丢掉我一般从0.35到0.45之间试。retina_masksTrue很关键它让模型输出的是高分辨率掩码而不是压缩到输入尺寸后再放大的低质量掩码False的话边缘会明显锯齿化。saveTrue会把可视化结果写到sam2_output/bus_demo目录下第一次跑建议开着能直接看到模型表现再决定要不要调参数。results[0].masks是一个Masks对象masks.shape返回的是(N, H, W)N是检测到的实例数H和W是掩码分辨率。如果你只想拿数据不想看图可以把save去掉直接在内存里处理。还有一个细节model.predict()返回的是列表每个元素对应输入图片列表中的一张所以单图也要取results[0]而不是results。3. 从跑通到能用SAM2的提示方式、掩码后处理与批量推理3.1 点提示、框提示与全图分割三种模式的取舍跑通全图分割只算热身实际业务里很少直接拿整张图生成掩码——成本高、噪声多。广告牌图像分割系统里通常先用一个轻量检测器把广告牌区域框出来再把框交给SAM2做精细分割医学图像分割里则是先在低分辨率图上标一个病灶中心点再用点提示让SAM2把病灶区域提取出来。Ultralytics对这两种提示都做了封装。框提示通过boxes参数传入点提示通过points参数传入from ultralytics import SAM model SAM(sam2_b.pt) # 框提示传入xyxy格式 results model.predict( medical_slice.png, boxes[120, 80, 260, 230], devicecuda:0 ) # 点提示传入xy坐标和标签(1为前景0为背景) results model.predict( medical_slice.png, points[[180, 150]], labels[1], devicecuda:0 )框提示的坐标是像素坐标顺序是x1, y1, x2, y2左上和右下。注意这里不是归一化坐标如果你从检测模型拿到的是归一化框要记得乘回原图宽高这个转换最容易写错。点提示的labels参数我一开始漏传了结果SAM2把所有点都当成了背景掩码输出全是空的。labels为1代表该点是目标内部0代表外部多个点可以混合传比如框一个目标时可以在目标上点几个1在背景上点一个0约束效果比纯框好很多。全图分割和提示分割怎么选我的经验是如果目标边界清晰、形状规整框提示就够如果目标纹理复杂、边界模糊比如医学图像里的肿瘤区域点提示加背景约束会更稳如果目标是视频里运动的小物体那得靠全图分割加时序后处理。全图分割也不是不能用但要把conf调高并且做好后处理过滤否则碎掩码会淹没真实目标。3.2 掩码数据怎么取出从Results到RLE与多边形拿到results之后下一步是把掩码转成业务能用的格式。常见需求是转成COCO的RLE格式用于评估或者是转成多边形用于标注平台回显。我踩过的一个坑是直接对results[0].masks.data做numpy()结果发现掩码是浮点型值域在0到1之间直接当整型用会出错。正确的取出逻辑是这样import numpy as np from pycocotools import mask as mask_utils masks results[0].masks.data.cpu().numpy() # (N, H, W) float32 for i in range(masks.shape[0]): mask (masks[i] 0.5).astype(np.uint8) # 阈值化 rle mask_utils.encode(np.asfortranarray(mask)) rle[counts] rle[counts].decode(ascii) # 此时rle可直接用于COCO的segmentation字段这里masks.data是PyTorch张量如果推理时用了GPU直接.cpu().numpy()拷贝到内存。(masks[i] 0.5)做二值化很关键因为SAM2输出的掩码是概率图不是硬掩码不阈值化的话转RLE时pycocotools会报数据类型错误。np.asfortranarray是为了满足pycocotools对数组内存连续性的要求漏了它会在RLE编码时报内存布局错误。转多边形稍微麻烦一点。masks.xy返回的是每个实例的多边形点集格式是(N, points, 2)但并不是所有掩码都适合转多边形——当掩码有孔洞时masks.xy只给外轮廓孔洞信息丢失。如果你的业务对孔洞敏感比如医学图像分割中病灶内部可能有正常组织那就得保留概率掩码或者用cv2.findContours自己提取内外轮廓。3.3 批量推理的显存与缓存把SAM2接进现有分割管线单张推理没问题后批量推理的坑集中在显存和重复计算上。SAM2的骨干网络是Hiera参数量不小和YOLO那种轻量模型不是一个量级。我做过一个广告牌图像分割系统的压力测试单张1080p图显存占用约4到6GBbatch size开到4时12GB显卡已经快满了而且推理速度并不是线性提升——因为SAM2的处理逻辑里有一部分是对每张图独立计算的batch带来的加速主要在编码器部分。批量推理建议这样写from ultralytics import SAM model SAM(sam2_b.pt) image_paths [frame_001.jpg, frame_002.jpg, frame_003.jpg] results model.predict( sourceimage_paths, devicecuda:0, streamTrue, # 生成器模式降低峰值显存 batch2, conf0.4, retina_masksTrue )streamTrue是关键参数。不开stream时Ultralytics会一次性把所有图片的掩码全部放进内存四张1080p图就可能吃掉好几个G内存。开了stream后返回的是一个生成器处理完一张才取一张显存峰值和内存占用都会降下来。代价是结果的顺序和输入顺序不一定严格一致所以如果你要按文件名匹配结果最好自己维护一个映射。另外如果同一个模型要对同一个视频的多帧做分割建议把模型实例保持在内存里不要反复SAM(...)加载权重。我第一次写视频分割脚本时每帧都加载一次模型结果加载时间比推理时间还长。正确做法是循环外加载一次循环内只调predict。还有一个小技巧如果显存不够可以把imgsz参数从默认的640降到512代价是掩码边缘会损失细节但很多时候512足以满足业务需求。对于医学图像分割这种超大图的场景另一个常见做法是滑窗推理把大图切成重叠的tile每个tile单独分割最后用权重融合拼回原图。这里缓存策略更重要——重叠区域的结果缓存到内存里等所有tile都处理完再取平均能有效避免拼接缝。在做不确定性估计时还可以用蒙特卡罗方法对同一张图多次推理但那是更高级的用法先把基础的滑窗搞定再谈采样。4. SAM2在Ultralytics框架下的常见坑现象、原因、解决4.1 权重加载失败KeyError和模型结构不匹配现象运行SAM(sam2_b.pt)时报错提示KeyError: xxx或者mismatched keys。原因Ultralytics的版本和权重文件的打包格式不匹配。SAM2权重有官方原版和Ultralytics重打包版两种后者在state_dict里加了前缀或改了字段名。你下载了一个官方原版权重而ultralytics版本期望的是自己的格式加载时自然对不上。解决确认ultralytics版本大于等于0.3.1直接删掉本地已下载的权重文件重新让Ultralytics自动下载。如果网络受限去Ultralytics官方GitHub的release页面下载对应版本的.pt文件不要从第三方博客的网盘链接拿权重。我遇到过同事从某论坛下载了所谓SAM2权重结果是个破损的YOLO权重加载报错内容完全不是KeyError而是AttributeError: Detect object has no attribute sam。4.2 显存明明够一跑就OOM图像尺寸和batch的隐性放大现象nvidia-smi看显存还有6GB空闲跑单张1080p图却报CUDA out of memory。原因SAM2在Ultralytics内部做了分辨率归一化默认imgsz1024而且对长宽比不是4:2的图会做paddingpadding后的实际张量尺寸可能比原图大很多。比如一张2000x1000的图padding到1024x1024后显存占用不是线性增长而是成平方增长。解决显式设置imgsz。先看原图分辨率再决定目标尺寸。我一般这样调results model.predict( wide_banner.png, imgsz768, devicecuda:0, retina_masksTrue )从1024降到768显存占用能降一半以上边缘质量损失在视觉上几乎看不出来。如果降到512仍然OOM说明显卡确实带不动这时候考虑换sam2_t.pt——这是SAM2的tiny版本速度和显存都好很多代价是掩码边缘精度下降。工程上不要死磕一个模型先跑通再优化精度。4.3 掩码全黑或全白输出归一化与可视化习惯现象results[0].plot()保存的图正常但自己用cv2.imwrite保存masks.data转出的掩码时图片全黑或者全白。原因masks.data的值域是浮点0到1而cv2.imwrite期望的是uint8的0到255。直接拿浮点数组去保存OpenCV会截断小于1的值全部变成0所以全黑。全白的情况是你在别处乘了255但没有做类型转换溢出或者取整出了问题。解决保存前做标准化mask results[0].masks.data[0].cpu().numpy() # (H, W) float32 mask_uint8 (mask * 255).astype(np.uint8) cv2.imwrite(mask.png, mask_uint8)这里* 255之后必须.astype(np.uint8)否则OpenCV还是会按浮点处理并且报错或乱码。如果掩码是透明的要叠加到原图上用cv2.addWeighted透明度可以按业务调一般0.5左右能看出边界。4.4 掩码错位图像预处理导致的坐标偏差现象框提示传入的坐标明明标在目标中心但生成的掩码却偏移到旁边的物体上尤其发生在图像被自动resize之后。原因Ultralytics在推理前会把图像resize到imgsz指定尺寸推理完成后把掩码坐标再映射回原图。这个映射过程依赖ratio和pad参数当你手动修改了predict的输入比如传入了已经是tensor的图像或者做了letterbox但没有同步更新坐标映射就会错位。解决不要直接传tensor始终传文件路径或numpy数组BGR格式。如果必须传tensor要自己完成坐标变换。最稳妥的做法是让Ultralytics处理所有预处理你只提供原始坐标和原始图像路径。另一个相关场景是旋转图像后的分割。广告牌图像分割系统里经常要处理倾斜拍照的广告牌如果你先cv2.rotate再传给SAM2那么提示点坐标也要跟着旋转否则掩码必然偏移。我在这上面花过一个下午最后写了个统一的变换函数把旋转、缩放、平移都包进去才彻底解决。4.5 版本升级后的结果突变现象ultralytics从某个旧版本升级到新版本后同样的权重、同样的图片分割结果完全不一样掩码数量翻倍或减半。原因Ultralytics对SAM2的推理逻辑一直在调整尤其是置信度阈值和NMS策略的默认值在不同版本不一样。升级后conf的默认行为变化导致输出特征改变。解决固定版本或者升级后先用一批标准测试图回归确认结果没有漂移。我的习惯是pip install ultralytics8.3.40这类版本号建议锁在你验证过的版本上不要习惯性pip install --upgrade ultralytics。做工程不是追新稳定优先。如果你已经升级了且无法回退就把conf调低或调高0.05到0.1看看能不能找回接近的结果。这个现象很玄学没有绝对规律回归测试是唯一有效的办法。5. 验证SAM2的分割效果用指标说话别用肉眼验收业务方问你SAM2到底行不行你不能说“看着还行”。图像分割的验证有几个层次我按自己的使用频率排一下像素级指标、实例级指标、业务级指标。像素级指标最常用的是mIoU和Dice。评测时把提示方式固定住——用框提示就把所有目标的标注框统一喂进去用点提示就把标注中心点统一喂进去不要手动一张张调。评测代码import numpy as np from ultralytics import SAM model SAM(sam2_b.pt) iou_scores [] for img_path, gt_mask in zip(img_list, gt_list): results model.predict(img_path, boxesgt_boxes[i], retina_masksTrue) pred_mask results[0].masks.data[0].cpu().numpy() pred_bin pred_mask 0.5 gt_bin gt_mask 0.5 intersection np.logical_and(pred_bin, gt_bin).sum() union np.logical_or(pred_bin, gt_bin).sum() iou_scores.append(intersection / (union 1e-6)) print(fmean IoU: {np.mean(iou_scores):.3f})注意gt_boxes[i]不是GT掩码生成的框而是你的提示框来源如果提示框本身就是GT的外接矩形那么测得的是SAM2的标注上限如果提示框来自一个检测模型测的是整个管线的效果。这两者要分开报告合在一起说不清楚是检测的问题还是分割的问题。实例级指标要看掩码是不是连成一片。SAM2有时会把一个目标切成两块导致实例数翻倍。这时候可以用连通域分析检查对每个目标的掩码做cv2.connectedComponents如果组件数大于1说明发生了实例分裂需要后处理合并或调整conf。业务级指标就要结合场景了。医学图像分割里还要算边界距离比如Hausdorff距离因为肿瘤边界是否平滑直接影响后续治疗计划。广告牌图像分割系统里更关心的是掩码面积覆盖率和广告文字区域是否被完整包含指标是像素级指标解决不了的。最后说一个建议如果要上生产不要把所有提示都压在SAM2上。SAM2是分割模型不是检测模型——它擅长把已有目标抠精细不擅长从零开始找目标。最理想的架构是“检测器生成框SAM2细化掩码”检测器可以用YOLO系列或者RT-DETR两者在Ultralytics里都能直接接到SAM2后面。这个组合我在多个项目里验证过比单独用SAM2做全图分割稳定得多也更省算力。验证完效果记得保留几组固定图片作为回归集。模型升级、参数微调、代码重构都要在回归集上重跑一遍别等到上线前才发现掩码质量悄悄下降了。这是我自己吃过亏的地方——有一版升级后广告牌边缘的锯齿明显变多靠肉眼没及时发现直到评测脚本跑出来MSSSIM下降才回头排查。希望你不用踩同样的坑把验证脚本沉淀下来比什么都管用。希望帮到你。本文还有配套的精品资源点击获取
