40MB 编码器 + 16MB 解码器,5 种 prompt 全支持,M1 笔记本 CPU 模式 180ms 出图。
SAM 3 走的是"更大、更全、能认概念"的路子,权重动辄几 GB,本地部署要 24G 显存的卡。对个人开发者来说,这东西和「能跑」之间隔着一条银河。
我手头有个工业质检的小活,需要从传送带图片里把缺陷抠出来,等不起云端推理。于是那天晚上熬到两点,用 MobileSAMv2 撸了个 Web Demo:
localhost:8000 就能玩,不挑显卡




一句话解释:把 SAM 那个 632M 参数的 ViT-H 编码器换成 5M 参数的 TinyViT,剩下的交给一个 prompt-guided mask decoder 精修。
原版 SAM | SAM 3 | MobileSAMv2 | |
|---|---|---|---|
编码器 | ViT-H (632M) | 更大 | TinyViT (5M) |
总体积 | ~2.4 GB | GB 级 | ~56 MB |
CPU 推理 | 跑不动 | 跑不动 | 毫秒级 |
本地部署 | 要 A100 | 要 A100/H100 | 笔记本就行 |
代价是 mask 质量掉 1-2 个 IoU 点,但作为 prompt-driven 工具完全够用。
这是这个 Demo 跟其他 SAM 项目最不一样的地方——不是一个 prompt 一种模型,而是同一个 SAM 编码器 + 5 种 prompt 入口。

核心 trick:图像编码只跑一次,特征缓存住;之后任意 prompt 都直接走 decoder,毫秒级返回。这是 SAM 系模型比传统分割网络快一个数量级的根本原因。
这一步最反直觉,但思路很简单。
MobileSAMv2 的权重文件里,编码器是 TinyViT、解码器还是原本 SAM 的结构。state_dict 的 key 按 image_encoder.* 前缀区分两边。加载时分两段:
image_encoder. 前缀的 → 去掉前缀后塞进 TinyViT加上 strict=False 容忍缺失/多余 key,两边都能装得上。
SamPredictor 负责点/框/模板,SamAutomaticMaskGenerator 负责 everything。两者共用同一个 encoder 实例,避免重复加载和重复前向。
SAM 的输入永远是 1024×1024,任意分辨率的图进来都要做一次 resize + pad:
关键点:scale 这个值一定要存下来。用户在 800×600 显示的图上点 (x=400, y=300),喂给模型前必须乘 scale,否则点偏。返回 mask 时也要按 scale 还原。这是个非常容易出错的地方。
五种接口后端加起来不到 400 行,挨个说思路。
最基础。在图上点一个点,给前景/背景标签,SAM 吐 3 个候选 mask。
两个关键:
multimask_output=True 必须开,3 个候选按粒度从粗到细排列实测 180ms(M1 CPU)。
画一个矩形框把目标"框"住。multimask_output=False,只出一个 mask。框 prompt 收敛性比点好,所以只出一个就够了。
实测 180ms。
这个模式不传任何 prompt,模型自己把图里所有"东西"切出来。
SAM 内部是这么做的:在图上铺 32×32 的 grid point,每个点都预测一次,最后 NMS 合并。所以一个 1024×1024 的图要跑 1024 次 SAM decode。
优化思路:先把图缩放到 1024 再 everything;按面积排序后再返回前端,太小的 mask 过滤掉。
实测 4.2s 切 50 个 mask(M1 CPU)。
这个最值得说——**"用一张小图找出原图里所有同类物体"**。
思路分两段:先用传统 CV 找候选框,再用 SAM 在每个框中心点精修。
候选框的找法有两种:
AKAZE 方案的精髓是:模板图和原图分别抽特征点 → KNN 匹配 → Lowe's ratio test 过滤(m.distance < 0.7 * n.distance,0.7 是经验值) → RANSAC 算单应性矩阵 → 把模板的四个角投影到原图得到候选框。
几个我反复试出来的数值:
找到候选框后再用 SAM 在框中心点做一次 point prompt,匹配 + 精修一条龙。
最"魔法"的一个——用自然语言告诉模型你要什么。
单靠 SAM 本身吃不下文本 prompt(SAM 只认点和框),所以要接一个 CLIPSeg 做"文本→粗 mask"。
两阶段 Pipeline:

几个让我 debug 到凌晨两点的点:
实测 2.1s 找 10 个目标(含 CLIPSeg 推理)。
导出一开始失败,错误是 RuntimeError: Only tensors, lists, tuples of tensors, or dictionary of tensors can be traced。原因是 SAM 内部有个地方用了 int(x.item()),trace 模式下会被当成常量。
思路:把图像尺寸相关的 crop/resize 全部挪到模型外,模型只做纯前向,裁剪/缩放由调用方 Python 端处理。
第二个坑是 dynamic shape。point_coords 的 N 必须声明成动态轴,否则换一个点数就 shape mismatch。ONNX 导出时用 dynamic_axes 显式声明。
加速效果:ONNX 推理比 PyTorch 快 30%-50%,主要是消除了 Python 端 dispatch 开销。
模式 | 1024×1024 | 1920×1080 | 4K |
|---|---|---|---|
Point | 180ms | 220ms | 380ms |
Box | 180ms | 220ms | 380ms |
Everything (50 mask) | 4.2s | 5.1s | 8.7s |
Text (top_k=10) | 2.1s | 2.6s | 4.5s |
Point/Box 都在 200ms 以内,人眼基本无感。
/health 接口multimask_output=True 时的 3 个候选都画出来,让用户挑分数最高的devicePixelRatio 缩放 canvas 尺寸和绘图比例.replace(/\s/g, '')如果想照着抄,按这个顺序:
pip install fastapi uvicorn torch torchvision opencv-python-headless Pillow transformers onnxruntimepip install -e .mobile_sam.pt(40MB)+ Prompt_guided_Mask_Decoder.pt(16MB),总 56MB/segment/* 接口,前端用原生 HTML + Canvas 单文件python3 backend/app.py,浏览器开 http://localhost:8000MobileSAMv2 给我的最大启发是:大模型不必从头训,把重的那部分换掉,留住精修模块,能力可以保留 95%。这思路套到很多场景都成立。
SAM 3 那种"全员大模型"路线当然是未来,但对个人开发者、对边缘设备、对今天就要跑起来的需求,56MB + 毫秒级 + 5 种 prompt 仍然是更务实的选择。
后续想试: