ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

自动标注流水线实战:Grounded-SAM、X-AnyLabeling与autodistill组合方案

2026/10/2 10:44:06 拓冰建站 浏览量
自动标注流水线实战:Grounded-SAM、X-AnyLabeling与autodistill组合方案 1. 为什么我最终搭了一套“自动标注”流水线做计算机视觉项目的人早晚都会撞上同一个坎数据标注。我最早做检测项目的时候标注一两千张图还能靠人工硬扛用标注软件一张张框、一张张打标签一天下来眼睛都快花了。等到项目从几千张图扩展到几万张图的时候人工这条路基本就堵死了——不是画不动是成本和时间真的耗不起。后来我开始尝试自动化方案陆陆续续接触了 X-AnyLabeling、autodistill、Grounded-SAM 这几个工具最终把它们串成了一条完整的流水线。这条流程解决的核心问题有三个第一用文本提示词自动生成目标框和分割掩码把标注的起点从“手动逐张画框”变成“AI 先标、人工修”第二用 GUI 工具做人工修正和质检保证最终交给训练集的数据不是一堆脏数据第三用蒸馏框架把大模型的标注能力迁移到小模型上后续项目可以低成本复用。这套流程适合谁我个人的判断是正在做检测、分割类项目的算法工程师或者自己搞落地项目、需要频繁生产训练数据的独立开发者甚至是刚入门想理解“自动化标注到底怎么运转”的学生都可以参考。接下来我会把整套流程的选型逻辑、实操步骤、常见坑逐一写清楚。看完之后你应该能直接在自己的机器上把这条流水线跑起来省下大量重复劳动。2. 三个工具各自解决什么问题先想明白再动手在写安装命令和运行参数之前必须先弄清楚这三个工具的定位——很多人一上来就把它们混在一起用结果流程混乱、数据也对不上号。我的经验是它们分别处于“标注链路”的不同环节分工非常明确。2.1 X-AnyLabeling人工质检与修正的主阵地X-AnyLabeling 是一个开源交互式标注工具界面和操作逻辑基于 LabelMe 那套思路但内置了非常多的 AI 辅助能力。比如它可以直接加载 YOLO 系列模型做预标注也可以用 SAM 系列做点击分割还可以调用不同的分类模型辅助判断。在整条流水线里它的定位是“人工修正层”——自动标注出来的结果不可能 100% 完美总会有漏检、误检、边界不准的情况这些都要靠人工在 X-AnyLabeling 里快速过一遍。为什么选它而不是别的标注工具我有两个理由。第一它对格式的支持非常全导出 COCO、YOLO、VOC 格式都是界面操作不需要自己写转换脚本这对后面训练环节是巨大的便利。第二它内置模型的预标注能力可以叠加使用——你可以先用 Grounded-SAM 自动标注一遍然后把结果导进去再配合它内置的模型做二次预检整个人工修正的效率会高很多。我在实际使用中比较看重的一个细节是X-AnyLabeling 支持在标注过程中对检测框做“锁定和隐藏标签”的操作修正遮挡严重的目标时非常管用。很多工具一改框就得重新选标签效率很差这个工具的交互设计在这一块明显是下了功夫的。2.2 Grounded-SAM从“文字描述”到“分割掩码”的自动标注核心Grounded-SAM 其实不是一个单一模型而是一条组合链路Grounding DINO 负责开放词汇检测SAM 负责精确分割。前者的核心能力是你给它一句文本描述比如“a red car”或者“a person on a bicycle”它能在图像里找出对应目标的检测框不需要预先训练固定的类别。后者是“分割一切”模型给一个框或者点它就能生成精确到像素级的目标掩码。这两个模型组合在一起效果就是你输入一段自然语言描述系统直接输出对应目标的掩码和类别标签。这正好就是自动标注最关键的能力——省掉了人工画框、画分割轮廓的环节。我曾经的痛点是以前做分割项目需要手动标注像素级轮廓一张复杂一点的图要画十几分钟而 Grounded-SAM 出来之后同样的图几秒钟就能给出一个还不错的初始掩码人工只需要在边界明显不准的地方微调。这里有一个必须强调的点Grounded-SAM 是“开集”的也就是说它不依赖你预先定义类别的数量。这对早期探索性项目的价值尤其大——你还没想好最终要分哪几类先用自然语言跑一批数据看看模型能检出什么再回头定义类别思路会清晰很多。2.3 autodistill把“教师模型”的标注能力蒸馏给“学生模型”autodistill 是 Roboflow 开源的一套框架核心思路非常直白用能力更强的大模型教师模型给数据打标签然后用这些标签去训练一个小模型学生模型训练完之后这个学生模型又可以在新的数据上做自动标注。这个流程在学术界叫“知识蒸馏”但在标注场景下它更像是一个“标注能力复制”的过程。比如说我用 GroundingDINO 当教师模型给一批农业场景图片标注“成熟番茄”和“未成熟番茄”虽然 GroundingDINO 能识别但推理速度慢、显存占用大不适合部署。那我用标注好的数据去训练一个 YOLOv8 的小模型训练完成后这个小模型在新图片上的检测精度可能接近大模型但推理速度快了几十倍。以后再做同场景的标注直接用这个小模型预标注成本就低很多了。我之前踩过的坑是把 autodistill 当成一个“标注工具”来用。它其实更像一个“流程框架”内部把“标注”和“训练”绑定在一起你需要按照它规定的目录结构和配置文件去组织数据如果不理解它的底层层级关系很容易在数据路径和标签格式上报错。所以后文我会专门拆开它的目录逻辑和实验流程。2.4 三者组合起来的完整链路是怎么流转的把三个工具连起来我的日常流程大概是这样的整理好原始图片集按场景分组放进目录。对每个场景写对应的文本提示词调用 Grounded-SAM 跑第一轮自动标注输出检测框和分割掩码。把自动标注结果导入 X-AnyLabeling人工逐张检查修正漏检、误检、边界不准的掩码同时删除低质量图像。导出为 YOLO 或 COCO 格式用这份“人工修正后的数据”训练一个小型检测模型。把这个小模型接入 autodistill 流程在更大规模的新数据上做自动预标注人工抽检后继续迭代训练。这套流程最核心的价值在于人工只会在第一轮和抽检阶段出现其他环节全部自动化。而且每一轮迭代产生的模型都会成为下一轮标注的“教师模型”标注成本是逐轮下降的。下一章我会一步步把这个流程落到具体操作上。3. 完整实操从环境搭建到跑通自动标注这一章是全文的重头戏。我会按照实际操作的顺序把每个环节的命令、参数选择、注意事项都写清楚。我的环境说明一下方便你对照操作系统是 Ubuntu 22.04显卡是 RTX 309024GB 显存驱动版本 535CUDA 12.2Python 3.10。如果你是 Windows 或者显存更小的卡我会在对应位置单独标注差异点。3.1 环境准备先装好三套依赖别互相污染这三个工具链的依赖并不完全一致我强烈建议新建独立的 Python 虚拟环境来管理。# 创建虚拟环境 conda create -n auto_label python3.10 -y conda activate auto_label # 安装 GPU 版本 PyTorch根据自己的 CUDA 版本调整命令 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 # 1. X-AnyLabeling 建议直接 clone 源码安装 git clone https://github.com/jianchang512/X-AnyLabeling.git cd X-AnyLabeling pip install -r requirements.txt # 2. autodistill 用 pip 直接安装基础框架和目标模型模块 pip install autodistill autodistill-grounded-dino autodistill-yolov8 # 3. Grounded-SAM 官方仓库是独立环境强烈建议单独 clone git clone https://github.com/IDEA-Research/Grounded-SAM.git cd Grounded-SAM pip install -r requirements.txt python setup.py build develop这里有几个我在实操中遇到的坑提前提醒你PyTorch 版本必须和你的 CUDA 驱动匹配。Grounded-SAM 官方对 torch 版本没有严格限制但实测下来 torch 2.1 以上比较稳太低版本会和后续装的一些依赖冲突。如果你用的是 20 系或更老的显卡建议装 cu118 版本的 torch别硬上 cu121。X-AnyLabeling 仓库里的依赖比较重里面包含 opencv、pyqt 等安装时间会比较长。如果在 Windows 上编译某些 native 依赖失败可以直接用预编译的 wheel 包。Grounded-SAM 的 setup.py build develop 这一步很关键它会把一些 C 扩展编译好这个过程需要 gcc、gLinux 上记得提前sudo apt install build-essential。Windows 上如果编译失败我建议直接放弃源码安装改用官方提供的预编译权重文件和纯 Python 推理脚本后面我会给替代方案。3.2 用 Grounded-SAM 跑第一轮自动标注提示词是关键环境装好之后第一件事是下载权重文件。Grounded-SAM 需要两个模型的权重Grounding DINO 的权重可以选 swin-tiny 或 swin-base前者快后者准和 SAM 的权重分 vit_b、vit_l、vit_h 三个规格我建议先用 vit_b速度比较理想精度在大多数场景下够用。权重的下载地址官方 README 里都有下载完放进项目目录下的weights文件夹。接下来是推理脚本。官方仓库里提供了inference脚本和命令行工具但直接用命令行写提示词有时候不够灵活我会写一个简短的 Python 脚本方便批量处理。核心逻辑如下import torch from groundingdino.util.inference import load_model, load_image, predict from segment_anything import SamPredictor, sam_model_registry import cv2 import os # 加载模型 grounding_model load_model( groundingdino/config/GroundingDINO_SwinT_OGC.py, weights/groundingdino_swint_ogc.pth ) sam_model sam_model_registry[vit_b](checkpointweights/sam_vit_b_01ec64.pth) sam_model.to(cuda) sam_predictor SamPredictor(sam_model) img_path imgs/example.jpg image, boxes, _ load_image(img_path) # boxes 为 xyxy 格式 text_prompt a ripe tomato box_threshold 0.35 text_threshold 0.25 # 先用 Grounding DINO 检测目标框 boxes, logits, phrases predict( modelgrounding_model, imageimage, captiontext_prompt.lower(), box_thresholdbox_threshold, text_thresholdtext_threshold ) # 再把检测框交给 SAM 生成掩码 sam_predictor.set_image(cv2.imread(img_path)) masks, scores, _ sam_predictor.predict( point_coordsNone, box_coordsboxes, multimask_outputFalse ) # 保存结果 import json results [] for box, mask, phrase, score in zip(boxes, masks, phrases, scores): results.append({ box: box.tolist(), mask_path: fmasks/{os.path.basename(img_path)}, phrase: phrase, score: float(score) }) with open(output.json, w) as f: json.dump(results, f, indent2)这段脚本只是搭建了一个最小可用的骨架你需要根据自己的数据格式改写入逻辑。重点说三个参数box_threshold框阈值Grounding DINO 对检测框的置信度阈值。我默认从 0.35 开始调。如果目标小、遮挡多可以降到 0.25 左右如果背景噪音多、误检频繁就往上调到 0.4 甚至 0.5。这个参数直接影响漏检率和误检率是整条链路里最值得花时间调的参数。text_threshold文本阈值文本和类别标签的匹配阈值。这个参数调太高会导致部分目标检不出来调太低又会把语义相近但错误的目标检出来。我的经验是 0.2-0.3 之间比较安全。multimask_outputSAM 会同时生成多个可能的掩码你只需要一个时设置成 False。如果你的掩码边界总是偏大或偏小可以试一下改成 True挑选 score 最高的那个使用。批量处理的时候建议把每张图的结果单独保存成 JSON 文件别把所有结果堆在一个文件里——后面导入 X-AnyLabeling 质检时按图索引会方便得多。3.3 数据落盘与格式转换别忽略这一步的坑Grounded-SAM 的输出是检测框、掩码数组和文本标签但 X-AnyLabeling 和 autodistill 需要的是特定格式的项目文件。这一步直接决定后面的流程顺不顺畅。我的做法是先不急着转最终格式先把掩码数组保存成独立的 PNG 文件灰度图每个像素值对应类别 ID检测框和解码文本保存为对应的 JSON。然后写一个小脚本把这些中间结果转换成 LabelMe 的 JSON 格式因为X-AnyLabeling 原生支持 LabelMe 格式的导入导出。LabelMe JSON 的基本结构包含shapes、imagePath、imageWidth、imageHeight等字段其中shapes里每个元素包含label、points多边形坐标、shape_typepolygon 或 rectangle。如果你已经用 SAM 拿到了掩码需要先用 OpenCV 的cv2.findContours把掩码转成多边形坐标contours, _ cv2.findContours(mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 取面积最大的轮廓 contour max(contours, keycv2.contourArea) points contour.squeeze().tolist()这个转换过程中有个我踩过的坑SAM 生成的掩码有时会在边缘有锯齿状的小凸起直接转多边形会导致点太多导入标注工具后编辑起来非常卡。我的解决办法是转之前先对掩码做一次形态学开运算然后对轮廓做一次 Douglas-Peucker 简化from imutils import perspective approx cv2.approxPolyDP(contour, 2, True) # 2 是简化阈值可以自己调这样处理过的多边形既保留了轮廓主体形状又剔除了噪音点后续人工微调时会轻松很多。转换完成后在 X-AnyLabeling 里新建项目选择“打开 LabelMe 工程”把图片目录和对应 JSON 目录填进去就能看到所有自动标注结果了。3.4 X-AnyLabeling 的人工修正与质检流程X-AnyLabeling 的界面和 LabelMe 很像左边是图片列表中间是画布右边是标注信息面板。加载完自动标注结果后我的质检流程通常是三步第一步全面浏览一遍全部图片。用方向键快速翻图注意力集中在明显的问题上——比如一张图里完全没标注的目标、标注框严重错位、掩码明显偏离物体边界。这一步不做精细修改只做“标记”。第二步对标记出来的问题图逐张修正。如果目标漏检了我会直接用 X-AnyLabeling 内置的 SAM 模型在目标中心点点击一下让模型自动生成掩码然后我只需要拖动边界点微调效率非常高。这也是我选 X-AnyLabeling 的关键原因——它不是只能手动编辑它自带的 AI 辅助能力可以直接在人工环节继续降低工作量。第三步检查标签一致性。自动标注经常会出现同一个目标在不同图片里被标成不同类别的情况比如“person”和“people”。这种问题靠视觉检查很难发现我的办法是导出 YOLO 格式后统计全部标签的类别分布如果某个类别的目标数量异常少或者类别名称有拼接错误就说明提示词或模型输出出了问题需要回头修改文本提示词重新跑一遍。这里必须提醒人工质检环节千万不要图快直接全盘接受自动标注结果。Grounded-SAM 的漏检率在复杂场景下可能高达 20%-30%尤其是小目标和密集重叠目标。我的经验是第一轮数据至少要抽出 50% 的图片做人工抽检否则训练出来的模型会继承教师模型的错误模式。3.5 导出训练格式YOLO 还是 COCO看任务定质检和修正完成后X-AnyLabeling 可以直接导出 YOLO 格式和 COCO 格式。我的选择标准很简单做检测任务导出 YOLO 格式。每个类别一个 ID图片和标签文件同名。做分割任务导出 COCO 格式。掩码信息以 RLE 或多边形形式存进 JSON训练框架可以直接读取。但这里有个容易出错的地方YOLO 格式的归一化坐标是相对图片宽高的而 X-AnyLabeling 导出时默认使用的是原始像素坐标。如果你直接拿导出的标签去训练可能检测框位置全部偏移模型完全学不出来。我每次导出后都会写一个校验脚本随机抽几张图把框画回去看一眼import cv2 for img_name in sample: img cv2.imread(fimages/{img_name}.jpg) with open(flabels/{img_name}.txt) as f: for line in f: cls, cx, cy, w, h map(float, line.strip().split()) cx, cy, w, h cx*img.shape[1], cy*img.shape[0], w*img.shape[1], h*img.shape[0] x1, y1 int(cx-w/2), int(cy-h/2) x2, y2 int(cxw/2), int(cyh/2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(fcheck/{img_name}_check.jpg, img)这个“画框检查”的步骤看着不起眼但真的救过我太多次了。格式问题导致的标注偏移如果没有可视化检查会直接污染整个训练集。3.6 用 autodistill 跑通“教师-学生”蒸馏流程人工修正后的数据现在可以用来训练一个小模型。autodistill 的用法比想象中简单核心就三段代码from autodistill.detection import DetectionBaseModel, DetectionModel from autodistill_grounded_dino import GroundedDINO from autodistill_yolov8 import YOLOv8 # 定义教师模型和提示词Onnx 框架的 base_model 也支持 base_model GroundedDINO(ontologyyour_ontology) # 定义学生模型 target_model YOLOv8(yolov8n.pt) # 在一个数据目录上跑教师模型标注 base_model.label(./raw_data/images, extension.jpg) # 用标注结果训练学生模型 target_model.train(./raw_data/data.yaml) # 用训练好的学生模型给新数据标注 target_model.label(./new_data/images, extension.jpg)这个框架内部会自动完成调用教师模型标注 → 把标注结果放进缓存目录 → 生成数据配置文件 → 调用 Ultralytics 训练循环。你不需要手动设置太多东西但必须保证 images 目录里的图片命名干净不能有中文或空格否则数据配置文件的路径逻辑会出问题。我踩过的一个比较隐蔽的坑是autodistill 默认会把教师模型标注的结果放在./raw_data下的隐藏缓存目录里如果你自己手动删除过这个目录后续训练时数据会丢失。它要求的目录结构非常严格images 目录要存在标注结果会自动生成。建议第一次运行时盯一眼控制台输出确认它提示的标注文件数量和你图片数量一致再进入训练步骤。4. 常见问题与排查技巧实录这一章是我长期使用这套流程之后的经验沉淀。很多问题不是看文档能解决的我把高频问题和对应的解决思路整理成下面的速查表现象可能原因解决思路X-AnyLabeling 安装后打不开报 Qt 相关错误PyQt/PySide 版本冲突或系统缺少图形库依赖检查依赖库是否安装齐全Linux 下安装 libxcb、libxkbcommon 等Windows 下尝试用官方 release 包Grounded-SAM 推理时报 CUDA out of memorySAM 的 vit_h 权重太大或 batch 策略占用显存换 vit_b 权重单张图跑关闭其他占用显存的进程降低输入图片分辨率X-AnyLabeling 导入 LabelMe 工程后图片不显示JSON 中的 imagePath 是相对路径和当前目录不对应检查 JSON 里 imagePath 字段改为相对图片目录的正确路径autodistill 训练时标签没有正确加载教师模型标注结果没生成成功或类别命名中包含非法字符检查缓存目录里的标注文件数量确认类别名称只用字母、数字、下划线YOLO 训练后检测框整体偏移导出格式时坐标未归一化参照 3.5 节的画框校验脚本逐一确认每个样本的框位置自动标注漏检严重box_threshold 设置过高或提示词描述不准确调低 box_threshold把提示词改成更具体的描述比如加入颜色、位置限定掩码边缘粗糙导入标注工具后卡顿轮廓点数量太多形态学处理 approxPolyDP 简化轮廓点除了表格里这些我再补充几个文字版的经验实录。关于显存不足的问题我有一套替换方案。如果你只有 6GB 显存的显卡跑 GroundedSAM 大概率会 OOM。我的办法是先用 Grounding DINOswin-tiny做检测输出检测框再把检测框裁剪成小图用 SAM 逐块做分割。这样显存占用会被压到非常低。思路就是“大图检测、小图分割”虽然代码复杂一点但能跑通是最重要的。关于 X-AnyLabeling 打不开的问题我再多说一句。这个工具在 Windows 上的启动路径最容易出问题。如果你下载的是 source 代码一定要先运行python -c from PyQt5.QtWidgets import QApplication测试 PyQt 是否能正常导入如果这行报错后面基本不用看。如果是直接启动预打包的 exe记得右键“以管理员身份运行”否则模型文件写入不到用户目录。关于提示词的设计我想重点展开。这是整个自动标注流程里最容易被低估的环节。提示词写得好不好直接决定标注质量的上限。我的经验是三个原则越具体越好。比如“a person”漏检率偏高换成“a person standing”或“a person riding a bike”会好很多。可以写多个并列描述。Grounding DINO 支持逗号分隔多个短语比如“a red car, a truck, a bus”。但要注意描述得越多推理速度越慢。同样的提示词不一定对所有图片有效。如果图片集里存在多个场景我建议先按场景分组每个场景单独跑一轮标注。5. 数据质量与流程效率我的长期使用心得这套流程我已经用了相当长时间也先后迭代过好几种不同的版本。单纯追求“全自动标注”是一个误区我认为效率最大化的关键在于“分层信任”——哪些环节交给模型哪些环节必须人工介入心里要有数。第一层是“信任但验证”像 Grounded-SAM 这类教师模型可以在粗筛阶段提供初始框和掩码但必须配合抽样质检我建议抽样比例不低于 20%并且要覆盖到所有场景类别不能只检查前几张图。第二层是“半自动修正”X-AnyLabeling 内置的 SAM 交互式分割可以在极短的时间内修正边界人工不需要从零画轮廓。我的实际体验是一个人一天可以质检修正 500-800 张分割标注数据而纯手动标注大概只有 50-80 张效率提升非常明显。第三层是“自举迭代”autodistill 把教师模型的标注能力迁移给学生模型之后后续新数据的标注压力会越来越小。我一个场景一开始用 Grounded-SAM 做标注训练完 YOLOv8 小模型后后续新图片直接用小模型自动标注漏检率通常能控制在 5% 以下再配合少量抽检基本可以脱离人工。关于工具选型我再多说几句。X-AnyLabeling、autodistill、Grounded-SAM 并不是互相替代的关系而是互补的关系。如果你只做检测不做分割可以砍掉 SAM 的环节直接用 Grounding DINO 出检测框如果你只做数据清洗和管理并且标注量不大可以只用 X-AnyLabeling 加上内置模型如果你要处理的是亿级规模的数据那 autodistill 这类蒸馏框架就几乎是必选的。最后分享一个小技巧。整套流程里最容易出错的地方往往不是模型本身而是中间环节的文件格式和路径管理。我自己的习惯是为每个项目建立一个固定的目录模板images/、labels/、masks/、check/、cache/这些都提前建好所有脚本都以相对路径访问。这样即使隔了很久再回来继续标注也不会出现“找不到文件”的尴尬。目录规范看起来是小事但在自动化流程里文件路径就是最容易被忽略的隐形炸弹。