基于SAM的半自动标注工具:从环境搭建到COCO导出的完整指南

发布时间:2026/10/11 12:19:32
基于SAM的半自动标注工具:从环境搭建到COCO导出的完整指南 简介这是一套面向计算机视觉开发者与数据标注人员的半自动图像标注工具源码基于 Segment Anything Model 实现只需鼠标左键点击一次即可完成目标分割与标注并支持多目标、多类别批量处理及 YOLO 数据格式转换适合需要快速构建检测数据集的中高级用户。压缩包共 31 个文件约 44KB以 22 个 Python 脚本为核心涵盖 SAM 模型调用、掩码转 YOLO、VOC 与 YOLO 格式互转、图像形态学后处理等模块另含 5 个 XML 配置、说明文档与依赖清单结构紧凑便于二次开发。目前已有 493 人学习下载。资源提供完整使用教程读者可掌握点位选取、右键撤回、按 S 保存等交互流程理解按类别逐轮标注直至完成的策略并能通过调整形态学操作的 kernel_size 与 iterations 去除误分割噪点快速搭建属于自己的半自动标注流水线。1. 半自动标注到底省了哪段力气从一张图到一批掩码的真实链路做过检测和分割项目的人都清楚模型精度卡住的时候八成不是网络结构的问题而是标注数据不够、不细、不一致。传统多边形标注一张图少则几十秒多则几分钟遇到密集遮挡场景直接劝退。Segment Anything ModelSAM出来之后很多人第一反应是「这玩意儿能不能替我把标注干了」。答案是能替你把最耗时的「勾轮廓」这一步干掉但「告诉它勾哪个、勾成什么类别」还得人来点一下。这就是半自动数据标注工具的核心逻辑——人负责语义决策SAM 负责像素级边界。这个方案适合谁如果你手头有几百到几千张图需要做分割标注预算请不起标注团队又不想纯手工点像素那这套基于 SAM 的半自动标注工具就是为你准备的。它不要求你训练模型不要求你写推理服务只需要你会装 Python 环境、能跑命令行、知道怎么把点或者框喂给 SAM。源码加使用教程的组合本质上是把「SAM 推理 交互式标注界面 结果导出」这条链路打包好让你改改配置就能用。接下来我会把这条链路拆开从环境搭建到批量导出再到踩过的坑一步步讲清楚。2. 把 SAM 跑起来之前环境、权重与推理后端怎么选2.1 为什么不是所有场景都无脑上 SAMSAM 有三个版本ViT-B、ViT-L、ViT-H。参数量分别是 91M、308M、636M。很多人一上来就下最大的 ViT-H觉得效果一定最好。实际用下来ViT-H 在边缘细节上确实更稳但推理速度在单张 1024×1024 图像上CPU 要跑到十几秒GPU比如 RTX 3060也要 1 到 2 秒。如果你要标几千张图这个延迟累积起来非常可观。ViT-B 在 GPU 上能压到 0.1 秒以内边缘稍微毛糙一点但配合人工微调完全够用。我的建议是先拿 ViT-B 跑通全流程确认标注界面和导出格式没问题再根据实际边缘质量决定要不要换大模型。另外SAM 的推理后端有两种常见选择一种是官方segment-anything库直接加载.pth权重另一种是 ONNX Runtime 或者 TensorRT 加速。官方库最省事兼容性好ONNX 适合部署到没有 PyTorch 的环境但导出和量化会引入精度损失。半自动标注工具源码里通常默认走官方库因为标注阶段对延迟不敏感稳定优先。2.2 从零搭一个能跑 SAM 的 Python 环境下面这套步骤我在 Ubuntu 22.04 和 Windows 11 上都验证过Python 版本锁定在 3.10因为 3.11 之后有些依赖轮子还没跟上。# 创建独立环境避免和系统里的 torch 冲突 conda create -n sam-label python3.10 -y conda activate sam-label # 安装 PyTorch根据你的 CUDA 版本选对应命令 # CUDA 11.8 的情况 pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 安装 SAM 官方库和标注工具常用依赖 pip install segment-anything opencv-python pillow numpy matplotlib pip install gradio # 如果工具带 Web 界面这段命令的关键点有三个第一conda create指定 Python 3.10避免版本漂移第二PyTorch 的 index-url 必须和你的 CUDA 驱动匹配装错了会回退到 CPU 版本推理慢十倍第三segment-anything只提供模型定义和推理接口不包含权重文件权重需要单独下载。权重文件一般放在项目根目录的weights/文件夹下命名通常是sam_vit_b_01ec64.pth、sam_vit_l_0b3195.pth、sam_vit_h_4b8939.pth。文件大小分别是 375MB、1.25GB、2.56GB。下载完之后用md5sum校验一下避免传输损坏导致加载时报unexpected EOF。2.3 加载模型与单张图推理的最小代码环境好了之后先用一段最小代码验证 SAM 能不能正常出掩码。这段代码不涉及标注界面只做「给一个点返回一个掩码」。import cv2 import numpy as np import torch from segment_anything import sam_model_registry, SamPredictor # 选择模型类型b/l/h 对应 ViT-B/L/H sam_checkpoint weights/sam_vit_b_01ec64.pth model_type vit_b device cuda if torch.cuda.is_available() else cpu # 加载模型并推到设备 sam sam_model_registry[model_type](checkpointsam_checkpoint) sam.to(devicedevice) # 创建预测器内部会做图像编码 predictor SamPredictor(sam) # 读取图像并转 RGB image cv2.imread(test.jpg) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 设置图像这一步会计算 image embedding比较耗时 predictor.set_image(image) # 给一个前景点坐标格式是 (x, y) input_point np.array([[500, 375]]) input_label np.array([1]) # 1 表示前景0 表示背景 # 预测掩码multimask_outputTrue 会返回三个候选 masks, scores, logits predictor.predict( point_coordsinput_point, point_labelsinput_label, multimask_outputTrue, ) # 取分数最高的掩码保存 best_idx np.argmax(scores) best_mask masks[best_idx].astype(np.uint8) * 255 cv2.imwrite(mask_result.png, best_mask)逻辑说明set_image是整条链路里最重的一步它把图像编码成 embedding后续所有点、框、掩码提示都复用这个 embedding。所以交互式标注工具会把set_image放在用户打开一张图的时候执行一次之后每次点击都只跑轻量的掩码解码器。multimask_outputTrue返回三个候选掩码分别对应不同粒度标注工具通常会让用户在这三个里选一个或者用分数自动选。参数说明input_point的坐标是原图像素坐标不是归一化坐标。input_label里 1 代表前景点0 代表背景点。如果你给多个点前景和背景可以混着给SAM 会根据这些提示分割出目标。predict返回的scores是 IoU 预测分数不是概率但可以用来排序。3. 半自动标注工具源码拆解交互、缓存与导出三块怎么改3.1 交互层点、框、掩码三种提示怎么组合SAM 支持三种提示点、框、掩码。点提示最直观适合孤立目标框提示适合目标边界比较规整的场景比如车辆、屏幕掩码提示适合迭代修正比如第一次分割多了把多余区域涂掉再喂回去。半自动标注工具源码里交互层通常用 OpenCV 的setMouseCallback或者 Gradio 的Image组件来捕获用户点击。一个常见的翻车点是用户点了一个点SAM 返回的掩码把整个背景都包进去了。原因通常是这个点落在了低对比度区域或者图像本身纹理太复杂。解决办法是让用户补一个背景点或者切换到框提示。源码里如果只支持单点建议自己加一个「添加背景点」的按钮把input_label里对应的值改成 0。3.2 缓存层embedding 复用与显存管理set_image算一次 embeddingViT-B 在 GPU 上大约占 1GB 显存ViT-H 要 3GB 以上。如果标注工具同时打开多张图或者用户频繁切换图片显存很容易爆。源码里一般会做一个 LRU 缓存只保留最近 N 张图的 embedding。N 取 1 到 3 比较合理再多收益不大。另一个细节是SamPredictor对象本身是有状态的set_image会覆盖上一次的 embedding。如果你在多线程环境里用同一个 predictor必须加锁否则会出现「A 图的点打到 B 图的 embedding 上」这种玄学 bug。我一般会在源码里把 predictor 封装成一个类每次set_image前检查当前图像 ID不同 ID 才重新计算。3.3 导出层从掩码到 COCO 格式的转换脚本标注完了要导出成训练框架能吃的格式。分割任务最通用的是 COCO 格式每个目标一个segmentation多边形和bbox。SAM 返回的是二值掩码需要转成多边形。下面这个脚本把掩码转成 COCO 的 annotation 列表。import cv2 import numpy as np from pycocotools import mask as mask_utils def mask_to_coco_annotation(mask, image_id, category_id, ann_id): mask: 二值掩码H x W值为 0 或 1 返回 COCO 格式的 annotation 字典 # 确保掩码是 uint8 且 Fortran 顺序pycocotools 要求 mask np.asfortranarray(mask.astype(np.uint8)) # 编码成 RLE rle mask_utils.encode(mask) rle[counts] rle[counts].decode(utf-8) # 计算面积和 bbox area float(mask_utils.area(rle)) bbox mask_utils.toBbox(rle).tolist() return { id: ann_id, image_id: image_id, category_id: category_id, segmentation: rle, area: area, bbox: bbox, iscrowd: 0, }逻辑说明pycocotools的encode要求掩码是 Fortran 顺序列优先直接传 C 顺序的数组会报ValueError。counts字段在 Python 3 里是 bytes存 JSON 前要 decode 成 str。area和bbox都可以从 RLE 直接算不用再遍历像素。参数说明image_id和category_id要和你的 COCO 数据集 JSON 里的images和categories对应。ann_id全局唯一建议用递增整数。如果你的标注工具支持多个类别每个类别一个category_id导出时按类别分组。提示如果后续要转成 YOLO 格式多边形点可以直接从 RLE 解码后取轮廓但要注意 YOLO 的坐标是归一化的且多边形点顺序要一致。4. 避坑与排查标注到一半崩了、掩码偏移、类别错乱怎么救4.1 掩码整体偏移几十个像素现象点选目标后返回的掩码位置和实际目标差了一截像是被平移过。原因通常是图像在预处理时被 resize 了但点坐标没有同步缩放。SAM 官方实现里set_image内部会把图像长边缩到 1024短边按比例缩放。如果你传给predict的点还是原图坐标就会偏移。解决在set_image之前记录缩放比例把用户点击的坐标乘以比例再传给predict。或者直接用SamPredictor的transform.apply_coords方法它内部会处理坐标变换。源码里如果没做这一步自己补上。4.2 显存不足导致进程被 kill现象标注了十几张图之后程序突然退出终端显示Killed或CUDA out of memory。原因是 embedding 缓存没有释放或者SamPredictor对象一直持有旧图的 tensor。解决每次set_image前调用torch.cuda.empty_cache()并把 predictor 的features、original_size等属性显式置空。如果用的是 ViT-H把缓存数量降到 1。另外标注工具如果开了多个进程每个进程都会加载一份模型显存占用翻倍建议用单进程加队列。4.3 同一张图多次标注结果不一致现象同一张图同样的点两次运行返回的掩码不一样。原因通常是multimask_output为 True 时三个候选掩码的排序不稳定或者模型没有设成 eval 模式。解决在加载模型后调用sam.eval()并设置torch.no_grad()。如果还是不稳定把multimask_output设为 False只返回一个掩码。另外随机种子也会影响某些后处理固定torch.manual_seed(42)能减少玄学。4.4 导出的 COCO JSON 在训练时读不出来现象用pycocotools加载导出的 JSON 时报KeyError: segmentation或者TypeError: Object of type bytes is not JSON serializable。原因是 RLE 的counts没 decode或者segmentation字段写成了多边形列表但格式不对。解决导出前统一走一遍mask_utils.encode确保counts是 str。如果要用多边形格式用cv2.findContours提取轮廓后转成[[x1, y1, x2, y2, ...]]的列表注意点数不能少于 3 个且要按顺时针或逆时针排列。4.5 类别 ID 和图像 ID 冲突现象标注工具里给目标选了类别导出后训练时所有目标都变成同一个类。原因是category_id在导出时被硬编码成 1或者多个类别的 ID 重复。解决在标注工具的配置里维护一个category_map比如{person: 1, car: 2}导出时从 map 里取。图像 ID 同理用文件名哈希或者数据库自增 ID不要用列表索引否则增删图片后会错位。5. 把半自动标注接进真实流水线批量预标注与人工复核的节奏控制单张交互标注跑通之后真正省时间的是批量预标注。思路是先用一个粗糙的检测模型或者简单的网格点给每张图生成一批候选掩码然后人工只做「保留、删除、改类别」三个动作。SAM 的SamAutomaticMaskGenerator就是干这个的它会在全图撒点生成大量掩码再用 NMS 去重。from segment_anything import SamAutomaticMaskGenerator # 配置自动掩码生成器 mask_generator SamAutomaticMaskGenerator( modelsam, points_per_side32, # 每边撒 32 个点总共 1024 个提示 pred_iou_thresh0.88, # IoU 预测阈值低于这个丢弃 stability_score_thresh0.92, # 稳定性分数阈值 crop_n_layers1, # 多尺度裁剪层数 min_mask_region_area100, # 小于这个面积的掩码丢弃 ) # 对一张图生成所有掩码 masks mask_generator.generate(image) # masks 是一个列表每个元素包含 segmentation、bbox、area、predicted_iou 等参数说明points_per_side越大掩码越细但耗时线性增长。32 在 ViT-B 上单张图大约 10 到 20 秒64 要一分钟以上。pred_iou_thresh和stability_score_thresh是过滤低质量掩码的关键调高会减少误检但可能漏掉小目标。crop_n_layers设为 1 会做一次 2×2 裁剪对小目标更友好但耗时翻倍。批量预标注的节奏控制我一般会把points_per_side设为 16 先跑一遍生成粗掩码人工快速过一遍把明显不对的删掉。然后对保留的区域再用交互式单点精修。这样比一上来就 64 个点快得多因为人工复核的时间远大于推理时间。验证方法拿 50 张图做人工全标注再用半自动流程跑一遍计算两者的 IoU。如果平均 IoU 低于 0.85说明预标注质量不够需要调pred_iou_thresh或者换 ViT-L。如果高于 0.95说明人工复核可以只做抽查。最后说一个我自己的习惯每次改完标注工具的配置先拿 5 张图跑一遍完整流程从加载模型到导出 JSON确认没有报错再上批量。这个习惯帮我省了很多次「跑了一晚上发现导出格式错了」的后悔药。半自动标注不是全自动人的判断始终在环里工具只是把重复劳动压缩掉。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询