简介:这份资源是面向 AnyLabeling 用户的 Mobile SAM(Segment Anything 轻量版)模型文件包,适合需要在本地进行图像分割标注、又希望降低显存与算力门槛的开发者与标注人员使用。压缩包共 3 个文件,包含 2 个 onnx 模型文件与 1 个 yaml 配置文件,分别承担编码器与解码器的推理计算以及模型参数配置,整体约 34.96MB,体积小巧便于快速部署。下载解压后放入 anylabeling_data 下的 mobile_sam_20230629 目录即可直接调用,省去自行转换与配置的繁琐环节。目前已有 484 人学习下载,说明该模型在标注工作流中具备一定实用价值。对于希望以较低硬件成本体验 Segment Anything 分割能力、快速搭建自动标注环境的读者,这份资源提供了开箱即用的模型基础,可有效减少环境调试时间,把精力集中在标注与数据生产本身。
1. Mobile SAM 到底轻在哪:从 632M 到 40M 的取舍逻辑
如果你在边缘设备上跑过 Meta 的 Segment Anything(SAM),大概率经历过那种“模型下载半小时、推理一张图等三秒”的绝望。SAM 的 ViT-H 主干有 632M 参数,推理一张 1024×1024 的图在 CPU 上动辄十几秒,放到 Jetson 或者手机端基本没法用。Mobile SAM 就是冲着这个痛点来的——它把图像编码器换成了一个轻量级的 TinyViT,整体参数压到约 40M,单张图编码耗时从秒级降到几十毫秒级,而 mask 解码器部分几乎原封不动地复用了 SAM 的结构。标题里的mobile-sam-20230629.zip就是这套方案的权重与代码打包,核心文件通常包含 TinyViT 的图像编码器权重、prompt encoder 和解码器权重,以及一份精简后的推理脚本。它适合谁?做移动端分割、边缘 AI 部署、或者想在本地快速验证交互式分割效果的工程师。你不需要重新训练,拿过来就能做点选分割、框选分割,甚至批量自动掩码生成。但轻量化的代价是什么、哪些场景会翻车、参数怎么调,下面几章拆开讲。
2. 把 mobile-sam-20230629.zip 跑起来:环境、权重与最小推理链路
2.1 解压后先看什么:目录结构与权重对应关系
拿到mobile-sam-20230629.zip之后,别急着pip install。先解压,用tree或ls -R看一眼目录。常见的结构是:一个mobile_sam/包目录,里面放build_sam.py、predictor.py、automatic_mask_generator.py;一个weights/目录,里面是mobile_sam.pt或类似命名的权重文件;根目录下可能有setup.py、README.md和几个 demo 脚本。权重文件大小通常在 40MB 上下,如果解压出来发现权重只有几 MB,那大概率是下载不完整或者被压缩工具截断了。确认权重完整性的一个笨办法是看文件头:python -c "import torch; ckpt=torch.load('weights/mobile_sam.pt', map_location='cpu'); print(type(ckpt), list(ckpt.keys())[:5])",能正常打印出字典键名就说明文件没坏。这一步花两分钟,能省掉后面半小时的报错排查。
2.2 依赖安装:别直接 pip install -r,先锁版本
Mobile SAM 的依赖和原版 SAM 有重叠但不完全一样。原版 SAM 要求torch>=1.7、torchvision>=0.8,但 Mobile SAM 因为用了 TinyViT,对timm有依赖,而且某些版本对torch的算子兼容性有要求。我一般会先建一个干净的 conda 环境:
conda create -n mobilesam python=3.9 -y conda activate mobilesam pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python pillow matplotlib timm这里torch选 2.0.1 是因为它在 CUDA 11.8 和 CPU 模式下都比较稳,timm不锁死版本,但如果你遇到ImportError: cannot import name 'window_partition'这类报错,大概率是timm版本太新或太旧,可以试pip install timm==0.9.12。注意,如果你只有 CPU,把第一行--index-url换成 CPU 版的源,或者直接pip install torch==2.0.1 torchvision==0.15.2让它自己选。装完之后跑一句python -c "import torch; print(torch.cuda.is_available())"确认 GPU 是否可用,后面推理速度差十倍。
2.3 最小推理脚本:点选分割跑通第一张图
环境好了,写一个最小脚本验证链路。假设你已经把mobile_sam包和权重放好,下面这段代码可以直接抄:
import cv2 import numpy as np import torch from mobile_sam import sam_model_registry, SamPredictor # 加载模型,vit_t 是 Mobile SAM 的 TinyViT 标识 sam = sam_model_registry["vit_t"](checkpoint="weights/mobile_sam.pt") sam.to(device="cuda" if torch.cuda.is_available() else "cpu") sam.eval() predictor = SamPredictor(sam) # 读图并设置图像 image = cv2.imread("test.jpg") image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) predictor.set_image(image) # 给一个点提示,坐标格式是 (x, y),label=1 表示前景 input_point = np.array([[500, 375]]) input_label = np.array([1]) masks, scores, logits = predictor.predict( point_coords=input_point, point_labels=input_label, multimask_output=True, ) # 取分数最高的 mask 保存 best = masks[np.argmax(scores)] cv2.imwrite("mask_out.png", (best * 255).astype(np.uint8)) print("scores:", scores)逻辑说明:sam_model_registry["vit_t"]是 Mobile SAM 注册的模型类型,原版 SAM 用的是vit_h、vit_l、vit_b,这里必须用vit_t,否则会报 key 不存在。set_image会调用图像编码器把图片转成 embedding,这一步是耗时大头,Mobile SAM 在 GPU 上大约 20-40ms,CPU 上 200-500ms。predict里的multimask_output=True会返回三个候选 mask,对应不同粒度,scores是 IoU 预测值,通常选分数最高的那个。参数上,input_point的坐标是原图像素坐标,不是归一化的,如果你图片是 4K 的,点选位置要按实际像素给。跑通之后你会看到mask_out.png是一张黑白掩码图,白色区域就是分割结果。
2.4 自动掩码生成:批量出图的参数怎么设
点选只是验证,实际落地更多用自动掩码生成(Automatic Mask Generation)。Mobile SAM 同样支持,但参数比原版更敏感:
from mobile_sam import SamAutomaticMaskGenerator mask_generator = SamAutomaticMaskGenerator( model=sam, points_per_side=32, pred_iou_thresh=0.8, stability_score_thresh=0.9, crop_n_layers=1, min_mask_region_area=100, ) masks = mask_generator.generate(image) print("num masks:", len(masks))points_per_side=32表示在图像上均匀撒 32×32 个点,原版 SAM 默认是 32,但 Mobile SAM 因为编码器容量小,撒太多点反而会引入噪声,我一般会降到 16 或 24。pred_iou_thresh和stability_score_thresh是过滤低质量 mask 的阈值,调高会减少输出数量但提升精度。crop_n_layers=1表示对图像做一层裁剪再分别生成,能捕捉小物体,但耗时翻倍。min_mask_region_area=100过滤掉小于 100 像素的碎块。这些参数没有绝对最优,取决于你的图像分辨率和目标物体大小,建议先用一张典型图跑三组参数对比。
3. TinyViT 图像编码器:Mobile SAM 的轻量化核心与精度边界
3.1 从 ViT-H 到 TinyViT:蒸馏策略与结构差异
原版 SAM 的图像编码器是 ViT-H,24 层,宽度 1280,参数量 632M。Mobile SAM 换成了 TinyViT,一种混合了卷积和注意力机制的轻量骨干,参数量约 5M,加上解码器整体 40M 左右。它不是简单地把 ViT 砍小,而是用了蒸馏:让 TinyViT 去拟合 ViT-H 输出的图像 embedding。具体做法是,固定原版 SAM 的图像编码器,对同一张图分别取 ViT-H 和 TinyViT 的 embedding,算 MSE 损失,只更新 TinyViT。这样 TinyViT 学到的不是原始像素特征,而是“ViT-H 会怎么理解这张图”。好处是解码器不用重新训练,直接复用原版 SAM 的 prompt encoder 和 mask decoder。代价是 TinyViT 的表达能力有上限,遇到 ViT-H 没见过的域外数据,蒸馏信号本身就不准,TinyViT 更没法正确响应。
3.2 精度掉在哪:三类典型翻车场景
第一类,细小物体。ViT-H 对 10×10 像素的小目标还能给出合理 embedding,TinyViT 因为感受野和通道数限制,小目标在 embedding 里几乎被背景淹没。你点上去,mask 要么不出来,要么糊成一片。第二类,低对比度边界。比如透明玻璃杯、烟雾、阴影边缘,ViT-H 能靠大量参数记住这些模糊模式,TinyViT 容量不够,边界会明显抖动。第三类,密集同类物体。比如一堆重叠的树叶、人群,Mobile SAM 容易把多个物体合并成一个 mask,或者只分割出最前面那个。这三类场景不是调参能解决的,是模型容量决定的。如果你的业务正好落在这些场景,要么上原版 SAM,要么针对数据做微调。
3.3 什么时候该换回原版 SAM:一个决策表
| 场景 | Mobile SAM 表现 | 建议 |
|---|---|---|
| 单物体、边界清晰、占图比例 >10% | 接近原版 | 用 Mobile SAM |
| 小物体(<5% 图面积) | 明显掉点 | 原版 SAM 或裁剪后推理 |
| 低对比度/透明物体 | 边界抖动 | 原版 SAM |
| 密集同类物体 | 合并 mask | 原版 SAM + 后处理 |
| 实时交互(>10fps) | 优势明显 | Mobile SAM |
| 批量离线处理 | 精度优先 | 原版 SAM |
这张表是我自己在几个项目里踩出来的,不是绝对标准,但能帮你快速判断要不要在这个方向上投入。Mobile SAM 的定位是“够用且快”,不是“替代原版”。
4. 避坑与排查:mobile-sam 落地时最容易翻车的 5 个点
4.1 现象:加载权重报KeyError: 'vit_t'→ 原因:包版本不对 → 解决:确认sam_model_registry里有vit_t
这个报错通常是因为你用的mobile_sam包是旧版,或者误装了原版segment_anything。原版的sam_model_registry只有vit_h、vit_l、vit_b,没有vit_t。解决方法是检查mobile_sam/build_sam.py里有没有vit_t的注册项,没有就说明包不对,重新从 zip 里解压覆盖。另一个可能是你import的时候路径写成了from segment_anything import ...,改成from mobile_sam import ...即可。
4.2 现象:推理结果全黑或全白 → 原因:图像通道顺序或归一化问题 → 解决:确认 RGB 和 uint8
Mobile SAM 的set_image内部会做归一化和 resize,但它期望输入是 RGB 的 uint8 数组,形状(H, W, 3)。如果你用 OpenCV 读图,默认是 BGR,必须cv2.cvtColor转 RGB。如果你用 PIL 读图,转成 numpy 后是 RGB,但要注意 PIL 读出来可能是uint8,没问题。全黑或全白通常是通道反了,或者你传了 float 类型的 0-1 数组,模型内部又除了一次 255,导致数值全接近 0。检查方法:print(image.shape, image.dtype, image.max()),应该是(H, W, 3) uint8 255。
4.3 现象:GPU 显存爆了 → 原因:自动掩码生成的点数太多 → 解决:降points_per_side和crop_n_layers
自动掩码生成会在一次前向里处理大量 prompt,显存占用和points_per_side的平方成正比。points_per_side=32就是 1024 个点,加上crop_n_layers=1会变成 4 倍,显存不够很正常。解决办法:先把points_per_side降到 16,crop_n_layers设为 0,跑通再逐步加。另外,SamAutomaticMaskGenerator支持output_mode="coco_rle",比默认的binary_mask省显存,但后处理麻烦一点。
4.4 现象:CPU 推理慢到无法接受 → 原因:没做图像预缩放 → 解决:限制长边到 1024
Mobile SAM 的图像编码器输入是 1024×1024,如果你原图是 4000×3000,set_image会先 resize 到 1024,这一步在 CPU 上也很耗时。更关键的是,如果你反复对同一张大图调set_image,每次都要重新编码。正确做法是:先把原图缩放到长边 1024 以内,再传给set_image,然后把你的点坐标按缩放比例映射回去。这样编码一次只要几十毫秒,点选响应就快了。缩放比例用scale = 1024 / max(H, W),点坐标乘scale即可。
4.5 现象:mask 边缘有锯齿或空洞 → 原因:解码器输出分辨率限制 → 解决:后处理 + 原图引导
Mobile SAM 的 mask 输出是 256×256 的 logits,上采样到原图尺寸后边缘会有锯齿。这是结构决定的,不是 bug。缓解办法有两个:一是用cv2.morphologyEx做闭运算填小洞,二是用原图的边缘信息做引导滤波(guided filter),把 mask 边缘对齐到原图边缘。后者效果更好但需要额外依赖cv2.ximgproc,不是所有 OpenCV 包都带。如果只是演示,闭运算就够了;如果做产品,建议上引导滤波或者直接换原版 SAM。
5. 进阶技巧:用 Mobile SAM 做视频交互分割的工程化收尾
视频场景下,Mobile SAM 的价值比单图更大,因为它的编码速度允许你做近实时交互。但直接逐帧跑set_image会浪费算力,因为相邻帧的 embedding 高度相似。我一般会做一个简单的缓存策略:每 N 帧重新编码一次,中间帧复用上一帧的 embedding,只更新 prompt。N 取 5 到 10 之间,取决于物体运动速度。运动快就取小,运动慢就取大。这个策略能把视频处理速度再提 3 到 5 倍,代价是快速运动时 mask 会滞后一两帧。
另一个技巧是 prompt 传播。第一帧你手动点一个点,得到 mask 之后,用这个 mask 的质心作为下一帧的输入点,再结合上一帧的 mask 做 bounding box 提示,这样即使物体移动,也能自动跟上。代码上就是在循环里维护prev_mask和prev_point,每帧调predict时把mask_input传进去。mask_input是上一帧的低分辨率 logits,能显著提升时序稳定性。
验证方法上,我习惯用 IoU 和边界 F1 两个指标。IoU 看区域重叠,边界 F1 看边缘贴合度。Mobile SAM 在清晰物体上 IoU 能到 0.85 以上,边界 F1 大概 0.7 左右,比原版低 5 到 10 个点。如果你的业务能接受这个差距,那 Mobile SAM 的 speed/accuracy trade-off 就是划算的。
最后说一个我自己的习惯:每次拿到新的轻量模型,先别急着集成到产品里,花半天时间在验证集上跑一遍,把失败案例截图存下来,按场景分类。Mobile SAM 的失败模式很集中,基本就是小物体、低对比度、密集物体这三类。提前知道边界在哪,比事后救火强。希望帮到你。
本文还有配套的精品资源,点击获取