资讯动态

SAM2实战指南:用Ultralytics实现自动/框选/点选分割

发布时间:2026/9/25 22:45:53 来源:尧图企业网站定制
简介面向需要快速上手SAM2的计算机视觉开发者这套资源基于Ultralytics框架封装了Segment Anything Model 2的完整测试代码与预训练权重。压缩包共7个文件包含4个pt格式的模型权重覆盖tiny、base、small、large等不同规格、2个Python脚本以及1张jpg测试图片包体约690.55MB。脚本分别对应基于提示词点、框的交互式分割与全图自动分割两种典型用法并配有可直接运行的样例图片便于理解SAM2在图像分割任务中的调用方式与输出效果测试代码也给出了不同交互条件下的提示示例方便对比分割结果。不同规格的pt权重可满足速度与精度兼顾的需求Python脚本结构清晰便于在此基础上替换输入图片或调整参数做进一步实验。目前已有1265人浏览学习适合正在研究视觉分割模型或需要基于Ultralytics进行二次开发的开发者参考。1. 为什么SAM2值得动手跑一遍提示词分割不是噱头去年我在处理一套街景数据时最费时的不是算法选型而是给广告牌做手工标注。后来换成SAM2配Ultralytics框架才发现这个模型根本不用微调——给个框、点几下就能把目标从背景里干净地切出来。它属于图像分割领域里“分割一切”的思路在医学图像分割、遥感目标切分这类场景里同样能直接复用输出的是实例级别的mask不是分类概率。适合两类人一是手上没标注数据想快速出mask的算法工程师二是想用大模型生成训练集再蒸馏给轻量模型的开发。下面的笔记直接把可跑代码、参数调法和踩坑记录给你照着就能复现。2. 模型骨架与Ultralytics封装先弄清楚SAM2在切什么2.1 三段式结构图像编码器、记忆注意力、mask解码器SAM2延续了“prompt-driven”的设计但内部结构比第一代SAM复杂不少。整条链路分为三块Hiera图像编码器负责把输入图变成多尺度特征记忆注意力模块让模型可以跨帧引用信息mask解码器把特征和用户的点/框提示融合输出分割结果。理解这三块是后面调参数的前提。Hiera编码器的核心是保留归纳偏置的层次化Transformer。它不像ViT那样一上来就把图切成均匀patch而是模仿卷积网络做逐步下采样浅层处理局部纹理深层处理语义。这样设计的好处是计算量比纯ViT小同时还能拿到多尺度特征。对于图像分割来说多尺度特征很重要——广告牌这种大目标需要深层语义而医学图像里细小的血管则需要浅层纹理。记忆模块和mask解码器是SAM2区别于传统分割模型的关键。记忆模块维护一个特征缓存把当前帧的视觉特征写入后续推理时再读出形成上下文。即使在单张图像推理时这个模块也在工作只是没有跨帧记忆而已。解码器输出时如果开启多mask模式会一次给3个候选mask分别对应整体轮廓、局部细节和边缘锐化三个不同“理解”然后用IoU预测头给每个候选打分。这个设计解决了一个经典问题同一个提示词可能对应多个合理分割结果。2.2 检查点怎么选sam2_t、sam2_s、sam2_b、sam2_l的取舍Ultralytics把官方权重转成了统一的.pt格式命名规则很直接t是tinys是smallb是basel是large。参数量从几千万到两亿多不等。我自己的经验是如果只是做数据标注或快速验证b就够了速度和精度都比较均衡如果对边缘质量要求高、有GPU且不赶时间选lt和s适合在CPU或老显卡上做demo精度差距在精细边缘上体现得最明显。模型加载通过统一入口完成from ultralytics.models.sam.build import build_sam model build_sam(sam2_b.pt)build_sam会自动处理两件事一是从指定路径加载权重二是把模型切换到推理模式。第一次运行时如果本地没有对应文件会尝试从远端下载权重。这里有个值得注意的点文件名里的b代表base不是batch别和训练时的batch size混淆。加载完之后建议显式指定推理设备model.to(cuda if torch.cuda.is_available() else cpu)很多人在这一步翻车后面避坑章节会展开讲。现在只要记住模型加载后立即指定设备后面所有操作都在这个设备上进行不要中途切换。3. 用Ultralytics跑通三类分割自动、框选、点选3.1 ultralytics安装与环境检查先装环境。Ultralytics的SAM2支持从8.3.x版本开始安装时用pip拉最新版就好pip install -U ultralytics python -c import ultralytics; print(ultralytics.__version__)如果是在国内网络环境可以加镜像源加速。安装完成后建议顺手验证一下依赖的torch和opencv版本SAM2对torch版本有一定要求太老的版本会出莫名其妙的算子错误。另外提醒一句如果你的项目里已经装过旧版ultralytics直接用-U覆盖可能出现依赖冲突稳妥做法是先建一个独立虚拟环境再装。3.2 自动分割全图直接出所有mask自动分割适合“我不知道图里有什么先把所有区域切出来”的场景。它会在图上铺一个密集点阵以每个点为中心做推理再把结果合并去重。代码非常短import cv2 from ultralytics.models.sam import SamAutomaticMaskGenerator from ultralytics.models.sam.build import build_sam model build_sam(sam2_b.pt) model.to(cuda) mask_generator SamAutomaticMaskGenerator( model, points_per_side16, # 每边采样点数16×16256个起始点 pred_iou_thresh0.80, # 模型自估IoU阈值低于此值直接丢弃 stability_score_thresh0.85, # 稳定性分数阈值过滤边缘抖动大的mask min_mask_region_area100, # 小于100像素的碎片直接清除 ) img cv2.imread(street.jpg) # OpenCV读入的是BGRUltralytics内部处理 result mask_generator.generate(img) for i, r in enumerate(result): print(fmask {i}: bbox{r[bbox]}, iou{r[predicted_iou]:.2f}, fstability{r[stability_score]:.2f}, area{r[area]})这段代码的关键在SamAutomaticMaskGenerator的参数。points_per_side16意味着生成16×16共256个起始点点数越多小目标越容易被发现但推理时间成倍增加。pred_iou_thresh是模型对自己输出质量的预估分低于0.8的候选直接丢弃stability_score_thresh更微妙——它通过对mask做微小扰动来测试稳定性边缘模糊或者和背景纠缠不清的区域这个分数会很低。min_mask_region_area是纯后处理把碎片清理掉避免后续标注时被小噪点干扰。实测下来这套默认参数对中等尺寸目标效果不错但对小目标或细长结构不太友好第4章会专门讲怎么调。3.3 框选分割给一个bbox切出目标自动分割拿到的是全图所有目标但如果我只想切某一个具体物体框选更直接。所谓框选就是给定目标的左上和右下坐标让模型只在这个范围内推理import cv2 from ultralytics.models.sam import SamPredictor from ultralytics.models.sam.build import build_sam model build_sam(sam2_b.pt) model.to(cuda) predictor SamPredictor(model) img cv2.imread(billboard.jpg) predictor.set_image(img) # 编码特征会缓存后续predict直接复用 masks, scores, logits predictor.predict( boxes[[100, 80, 500, 360]], # xyxy绝对坐标 multimask_outputFalse, # 只返回最优的一个mask ) print(masks.shape) # (1, 高, 宽) print(fmask score: {scores[0].item():.3f})这里最需要注意的是boxes的格式。它接收的是xyxy绝对像素坐标不是YOLO习惯用的xywh也不接受归一化坐标。很多从目标检测转过来的人在这里栽跟头——拿YOLO的标注格式直接喂进去结果mask和位置完全对不上。multimask_outputFalse让模型挑自认为最好的一个mask返回如果设为True则会返回3个候选需要自己看哪个更合适。3.4 点选分割正负点交互细化边缘框选适合目标边界比较规整的情况但如果两个物体靠得很近或者目标边缘特别复杂点选更精确。点选的核心是给一个前景点模型往外生长出mask如果生长过头了再给一个背景点把多余部分“按回去”import cv2 from ultralytics.models.sam import SamPredictor from ultralytics.models.sam.build import build_sam predictor SamPredictor(build_sam(sam2_b.pt)) predictor.set_image(cv2.imread(cell.jpg)) masks, scores, logits predictor.predict( points[[120, 150], [185, 220]], # 第一个是前景点第二个是背景点 labels[1, 0], # label1表示前景, 0表示背景 multimask_outputFalse, )labels数组和points一一对应1代表“这里是我要的目标”0代表“这里不是”。这个机制在处理粘连目标时特别有用——比如医学图像分割里两个细胞挤在一起第一个点落在目标细胞上第二个点按在相邻细胞上模型就会在两个点的博弈中把边界收敛到合适的区域。点选也是SAM2系列模型交互性能最好的方式因为它最接近训练时的prompt分布。4. 避坑SAM2实战中的5个高频翻车点4.1 device参数导致推理全部落在CPU上现象模型明明to(cuda)了但推理时显存占用为0torch.cuda.is_available()也是True速度慢得离谱。原因Ultralytics的SAM2实现在内部有多处torch.device的隐式推断。如果你在set_image时传入的设备与模型当前设备不一致某些中间结果会被自动搬回CPU。常见的触发场景是先用CPU加载模型做了一次推理之后又model.to(cuda)但SamPredictor内部的某些缓存张量还留在CPU上。解决构建模型后立即指定设备set_image和predict之间不要切换设备。如果已经出了这个问题最简单的办法是销毁SamPredictor重新构建不要试图手动搬缓存张量因为内部结构复杂容易漏掉某个变量。4.2 小目标整体消失只剩背景碎片现象对一张包含多个小广告牌、小目标的街景图做自动分割结果小目标一个都没出来反而把墙面裂缝、树叶阴影切出一堆碎片。原因默认的pred_iou_thresh0.80和stability_score_thresh0.85偏向保守。小目标的区域小边缘像素比例高模型自估IoU和稳定性分数天然偏低阈值一卡就直接被过滤掉了。解决把两个阈值降下来mask_generator SamAutomaticMaskGenerator( model, points_per_side32, # 提高采样密度 pred_iou_thresh0.60, # 降低自估IoU门槛 stability_score_thresh0.75, # 降低稳定性门槛 min_mask_region_area50, # 只清理小于50像素的碎片 )调低阈值后碎片会增加这是正常现象后续通过面积过滤和人工筛选来解决。小目标场景的核心原则是先让模型“多切”再用后处理“精选”而不是一上来就用高阈值把候选全杀掉。4.3 boxes格式混淆把xywh和归一化坐标喂进去现象框选分割时mask位置完全错乱有时mask甚至跑到了图像外面。原因Ultralytics的predict(boxes...)接收的是绝对像素坐标xyxy而YOLO标注、标注平台导出数据通常是xywh归一化格式。直接混用必然翻车。解决写一个统一的转换函数def yolo_to_sam_boxes(boxes_norm, img_w, img_h): 把归一化的xywh转成SAM2需要的xyxy绝对坐标 boxes_xyxy [] for x_c, y_c, w, h in boxes_norm: x1 (x_c - w / 2) * img_w y1 (y_c - h / 2) * img_h x2 (x_c w / 2) * img_w y2 (y_c h / 2) * img_h boxes_xyxy.append([int(x1), int(y1), int(x2), int(y2)]) return boxes_xyxy从那以后我每次喂box给SAM2之前都会打印一下坐标的最大最小值确认没超出图像尺寸再做后续处理。4.4 循环推理时显存一路暴涨最后OOM现象写了个循环遍历一批图片每张图都做一次框选分割。跑了几十张后显存占用从3GB涨到8GB最终OOM。原因每张图都重新调用build_sam构建了一次模型旧模型没有被释放。更隐蔽的情况是set_image本身会缓存图像特征如果每次循环都新建predictor旧缓存全部泄漏。解决模型和predictor都只构建一次循环里只更新图像和promptmodel build_sam(sam2_b.pt).to(cuda) predictor SamPredictor(model) for img_path in image_list: img cv2.imread(img_path) predictor.set_image(img) # 只更新缓存不重建模型 masks, scores, _ predictor.predict( boxescurrent_boxes, multimask_outputFalse ) # 处理masks...这看起来是小事但在批量处理数据集时代码写得不谨慎轻则显存溢出重则拖垮整台机器。4.5 结果直接商用license没理清现象项目上线前法务审核发现代码库的license不兼容差点重写推理部分。原因SAM2官方权重是Apache 2.0但Ultralytics的代码库是AGPL-3.0。这意味着如果你在商业产品里直接调用Ultralytics的build_sam和SamPredictor整个项目的开源义务可能被触发。解决商用场景两条路——一是只使用官方发布的权重和官方的sam2推理代码保持Apache 2.0的兼容性二是接受AGPL约束把周边代码开源。技术方案本身没有好坏但license必须在动手前确认否则后面全是坑。5. 把mask变成可训练数据RLE转COCO标注并过滤低分样本5.1 自动分割输出的数据结构用SAM2做数据标注管线的核心目标是生成可以喂给YOLO等目标检测/分割模型的训练集。SamAutomaticMaskGenerator.generate返回的是一个字典列表每个字典里segmentation字段比较特殊——当启用了crop策略时它是pycocotools格式的uncompressed RLE没启用时则是二值数组。RLE是一种压缩编码不能直接当数组用必须先解码。5.2 RLE解码、polygon提取与样本过滤下面这段代码把自动分割结果转成COCO格式的annotation同时按分数和质量过滤import cv2 import numpy as np from pycocotools import mask as mask_utils def rle_to_polygon(rle, image_shape): RLE转polygon返回COCO segmentation字段需要的扁平坐标 mask mask_utils.decode(rle) # 解码为二值mask mask (mask 0).astype(np.uint8) contours, _ cv2.findContours( mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) polygons [] for contour in contours: contour contour.flatten().tolist() if len(contour) 6: # 至少3个点过滤几乎空的轮廓 polygons.append(contour) return polygons def masks_to_coco(result, image_id, iou_thresh0.75, min_area100): 把SAM2自动分割结果转成COCO标注 coco_annotations [] for i, r in enumerate(result): # 核心过滤低IoU和碎片不进入训练集 if r[predicted_iou] iou_thresh: continue if r[area] min_area: continue polygons rle_to_polygon(r[segmentation], r.get(original_size)) if not polygons: continue coco_annotations.append({ id: i, image_id: image_id, category_id: 1, bbox: [float(v) for v in r[bbox]], # SAM输出的是xyxy segmentation: polygons, area: float(r[area]), # 用SAM算好的面积更精确 iscrowd: 0, }) return coco_annotations转换代码本身的坑不多最值得留意的是过滤策略。我之前直接拿全量结果导入标注平台结果模型把墙面、车窗都切了出来标注师花了大量时间去删无效样本。加上iou_thresh和min_area过滤后有效样本比例从六成提升到九成以上。还要注意一点r[bbox]是SAM2输出的绝对坐标格式是[x1, y1, x2, y2]而COCO要求的bbox是[x, y, width, height]。我在第一版代码里直接沿用导致后续训练指标全乱了。要么转换要么在训练时用能接受xyxy的dataset类。强烈建议在写入JSON之前统一为COCO标准格式x1, y1, x2, y2 r[bbox] coco_bbox [x1, y1, x2 - x1, y2 - y1]6. 让分割更快更准的三个习惯和一次教训最后一个章节分享三个能让SAM2落地更顺的技巧。第一个习惯输入图先压长边到1024再做推理。SAM2内部虽然会做缩放但外部先缩放可以让自动分割的crop策略更稳定还能明显提速。比如一张4000×3000的街景图直接推理可能要十几秒缩到1024后基本在1秒内出结果。压缩时用OpenCV的INTER_AREA插值质量损失最小。第二个习惯阈值调整不是拍脑袋而是看分数分布。每跑完一次自动分割先打印所有mask的predicted_iou和stability_score分布再决定阈值卡在哪里。下面的参数表是我在几个项目里试出来的起点具体场景在此基础上浮动场景pred_iou_threshstability_score_threshpoints_per_side大块目标广告牌、楼栋0.72 - 0.800.85 - 0.908 - 16小目标/密集目标0.55 - 0.700.75 - 0.8532 - 64医学图像细结构0.50 - 0.650.75 - 0.8532第三个习惯连续帧场景复用上一帧的mask中心点作为下一帧的提示词。逐帧全图自动分割不仅慢而且帧间mask抖动明显。取上一帧目标mask的中心坐标作为下一帧predict的points输入配合labels[1]速度和稳定性都会好很多。这是SAM2官方在视频场景的推荐用法之一在Ultralytics里同样适用。最后说一次真实教训。给一个合同项目做户外广告牌分割数据时我图省事把SAM2自动分割的输出全量导入标注平台。结果模型把墙面纹理、电线阴影、车窗反光全切了出来标注师清理了两天还漏了不少垃圾样本混进了训练集。后来我花了半小时统计分数分布发现stability_score低于0.8的样本几乎都是边缘模糊或背景纠缠的无效mask。从那以后每次做分割数据集都强制走一遍“先看分数分布、再定阈值、再导入标注”的流程再也没有翻过车。希望这篇笔记能帮你少踩几个我踩过的坑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑