资讯动态

自动标注实战:X-AnyLabeling、autodistill与Grounded-SAM构建COCO数据集

发布时间:2026/10/2 4:02:43 来源:尧图企业网站定制
先说个容易混淆的点。如果你去搜“自动标注”大概率会看到一堆AutoCAD自动标注外挂相关的东西——那是给图纸加尺寸、加引线的辅助工具和我们计算机视觉圈子里说的“自动标注”完全不是一回事。我们要聊的自动标注是用模型来给训练数据打标签检测框、分割掩码、关键点标完之后再去训练另一个模型。最近我刚好把 X-AnyLabeling、autodistill 与 Grounded-SAM 这三个东西组合起来走完了一条从原始图片到 COCO 分割数据集的完整链路中间踩了不少坑也把工具之间的分工彻底摸清楚了。这篇文章就按我的实操顺序把这套流程完整写下来。我手头的场景是路面病害检测需要标注裂缝、坑槽、修补带这三类目标。裂缝边缘不规则坑槽大小差异巨大传统的手动画框非常痛苦画一个裂缝掩码有时候要花三五分钟。这个场景特别适合用模型辅助标注因为视觉特征相对明确但又不能全靠模型自己做——裂缝的边界、阴影导致的误检、新旧修补带的纹理差异都需要人来兜底。于是我用 X-AnyLabeling 做第一轮人机协同标注autodistill 搭批量伪标签流水线Grounded-SAM 作为背后的定位与分割引擎三轮迭代之后数据集从 260 张人工标注扩展到了 3000 张可用图片。整个过程不算复杂但每一步都有值得记录的选择和教训。1. 先把三件工具的分工捋清楚它们不是竞品是流水线上的三个工位很多刚接触自动标注的朋友容易陷入一个问题这三个工具我都听说过到底该用哪个我先给一个结论它们三个根本不是同一层的东西硬要比“谁更强”没有意义它们更像是装配线上的三个工位各干各的活。1.1 为什么自动标注不是“点一下自动”就完事自动标注的本质是用一个已经能工作的模型或者用文本提示驱动的开放词表模型先给未标注数据出一个“初稿”再由人来修正这个初稿。这个逻辑听起来简单但它会带来一个根本性变化标注工作的重心从“从零画框”变成了“检查、修正、确认”。前者考验耐心后者考验眼光。所以效率提升的幅度取决于初稿质量的上限而不是工具本身花不花哨。我实测下来在路面病害这种纹理复杂、边界模糊的场景里纯自动标注的初稿质量能到七八成可用剩下两三成需要人工修。但即便是这样整体效率也比纯手动画掩码提升了三倍以上。如果换成目标检测项目比如检测行人、车辆这种矩形目标自动标注的初稿质量可以到九成以上人工只需处理遮挡和极小目标。1.2 X-AnyLabeling 的定位与我的使用方式X-AnyLabeling 是一个基于 PyQt5 的交互式标注工具它最打动我的地方是在同一个界面里既支持手动标注又内置了多个深度学习模型做自动推理。你可以加载一个 YOLOv8 检测模型让它在当前图片上跑一遍生成检测框和类别然后你只需要调整错框、补漏框、改类别就能完成标注。它的核心思路是人机协同不是全自动。这也意味着它是一个“前端工具”你需要的是模型推理能力模型文件可以在界面里直接导入。我自己的使用习惯是先用已有的旧模型跑全图预测再在界面上逐张检查遇到新的裂缝类型就手动补标保存之后把它纳入下一轮的训练集。1.3 autodistill 的核心逻辑与使用方式autodistill 是一个 Python 库它的设计思路非常明确用一个能力更强的“教师模型”给未标注数据打伪标签然后用这些伪标签去训练一个更轻量的“学生模型”。教师模型可以是 Grounded-SAM 这类开放词表模型也可以是任何你本地已经训练好的推理模型学生模型可以是 YOLOv8、RT-DETR 这些适合部署的模型。它的核心概念是 CaptionOntology也就是一个“提示词到类别名”的映射字典。你告诉它“哪些描述对应哪个标签”它就拿着这些描述去调用教师模型标注数据然后把标注结果转成目标模型能读的格式。这种方式特别适合批量化处理而且整个流程可以在脚本里跑不用开图形界面。1.4 Grounded-SAM 的组成部分Grounded-SAM 是这套方案里最核心的标注引擎名字来自两个模型的组合Grounding-DINO 负责“文本检测”——输入一句“路面裂缝”它在图中找出所有相关的目标框Segment Anything ModelSAM负责“框内分割”——把检测框给出的区域进一步细化成精确的像素级掩码。我在这个项目里只画了一句提示词就能让模型把裂缝和坑槽的掩码轮廓都抠出来。虽然分割结果不能直接作为最终标注但作为“初稿”已经能省下大量时间。这里要强调Grounded-SAM 不是独立开箱即用的版本需要自行组合 GroundingDINO 与 SAM 的推理脚本。1.5 一张表看懂三者的分工与数据流工具扮演的角色输入输出谁说了算X-AnyLabeling人工协同前端图片 本地模型VOC/COCO 格式标注人做最终检查autodistill批处理编排层未标注图片 教师模型对齐目标模型的伪标签模型出初稿配置约束规则Grounded-SAM底层定位与分割引擎图片 文本提示词检测框 像素掩码模型出结果阈值负责过滤三者之间的数据流向大致是Grounded-SAM 负责把文本提示变成掩码autodistill 负责把掩码组织成结构化的标签并喂给学生模型X-AnyLabeling 则是人工修正这些结果时唯一的图形化入口。接下来我按实际使用顺序把这套流程拆开来讲。2. X-AnyLabeling先用它把第一批数据“盘活”项目一上来我手里只有 200 多张已经画好框的图片掩码只有几十张根本不够训练分割模型。我的第一反应不是急着上复杂方案而是先用 X-AnyLabeling 把现有的几张掩码图变成可用的初版训练数据同时验证整个标注流程走不走得通。2.1 部署与 Linux 踩坑记录X-AnyLabeling 的部署不算难但 Linux 下有几个坑比较稳定地出现。我在 Ubuntu 20.04 上克隆源码后直接装依赖跑起来时报了一个很经典的错误libGL.so.1: cannot open shared object file: No such file or directory这是缺少 OpenGL 运行库导致的PyQt 的图形栈依赖它。解决办法很直接sudo apt update sudo apt install libgl1 libglib2.0-0之后启动程序也有讲究。我从源码目录运行git clone https://github.com/CVHub520/X-AnyLabeling.git cd X-AnyLabeling pip install -r requirements.txt python app.py如果只是 Windows 上用上述第三行改成pip install X-AnyLabeling基本就能跑条件是你的环境有 Python 3.8 以上且没有把 PyQt5 的依赖搞乱。另外Linux 下如果用到 GPU 推理建议提前把 CUDA 的 PyTorch 装好否则程序会自动退回 CPU 推理一张 1200 万像素的图上跑一次 YOLOv8s 可能要等好几秒标注节奏会非常难受。2.2 加载模型跑自动预标注启动界面之后关键操作是加载模型。这个方法在“模型”菜单的“创建模型”里可以手动输入模型配置的 JSON 路径我直接填的是 X-AnyLabeling 官方提供的 YOLOv8-seg 配置模型文件放到了model_data目录下。加载之后每打开一张图片我按一下“自动标注”按钮界面里就会叠出一层检测框和掩码。第一次跑出来的结果说实话有点粗糙——裂缝经常被断成好几段坑槽的边界也偏大。但这时候你不要急着否定它因为它的定位是“初稿”你要做的是在初稿上改而不是推倒重来。我修正了一百多张图片之后第一个版本的数据集就成形了。这一版不追求完美追求的是能训练出一个“初始学生模型”。有了这个初始模型后面的自动标注质量才会有质的飞跃这是整个流程里最关键的一个飞轮起点。2.3 快捷键与标注效率X-AnyLabeling 提供了一组还算顺手的快捷键我用得最勤的几张列在这里操作快捷键说明保存当前图片标注CtrlS按完即保存别一直攒着撤销上一步CtrlZ修正误画时高频使用取消当前编辑框Esc画一半不想要了删除选中的标注Delete配合自动检测修正漏检最常用放大当前区域滚轮标注裂缝边缘时必备切换上一张/下一张方向键或A/D看个人习惯复制当前标注CtrlC / CtrlV同类目标密集时能省不少事我的实际工作流是这样的先批量打开一批图片逐张点自动标注快速浏览一遍看到明显问题就键盘修正标注状态靠谱的图直接 CtrlS。每天结束前统一检查导出的格式再生成训练集。习惯了这套节奏之后我一天大概可以完成 300 张以上图片的修正量比纯手工标注快得多。2.4 标签格式导出的注意事项X-AnyLabeling 支持导出 VOC XML 和 COCO JSON也能导出掩码 PNG。我最终选择的是 COCO JSON因为后续 autodistill 和训练脚本对 COCO 格式的兼容性最好。导出之前有个容易忽略的点类别名和 ID 的顺序必须和训练脚本里的配置文件一致否则训练时会出现“标签错位”问题看起来检测框位置对但类别全都偏了。我吃了一次这样的亏。第一版导出的 JSON 里类别顺序是“裂缝、坑槽、修补带”但训练脚本读到的顺序是“坑槽、裂缝、修补带”结果模型的预测结果一路全错。从那次以后我养成了一个习惯每次导出之后先写一小段脚本检查 categories 列表再开始训练。3. autodistill把“伪标签”当成正式工作流的一部分第一轮用 X-AnyLabeling 迭代完之后我手上有了一个性能还不错的学生模型但数据量还不够大。这时候我引入 autodistill 来批量扩展数据。很多教程把 autodistill 包装成“全自动标注神器”但我的使用体验是它真正擅长的是承接流水线而不是凭空创造可用数据。3.1 先别误会它是自动标注的全部autodistill 做的是三件事调用教师模型给图片打标签把标签写成目标模型需要的格式然后启动目标模型的训练流程。它不负责评估伪标签质量也不负责处理数据清洗这些环节依然需要人工脚本和抽检。所以我把它定位成一个“编曲工具”——它把 Grounded-SAM 这种底层的标注引擎和目标模型之间的接口理顺了让整条流水线能以脚本方式反复运行。在路面病害这个项目里我用它处理了 1200 张新采集的未标注图片全程没有打开一次图形界面。3.2 CaptionOntology 的设计与语义提示autodistill 里最核心的概念是 CaptionOntology它是一个字典键是给教师模型看的描述值是写入标注文件的类别名。我一开始写的是这样的from autodistill.detection import CaptionOntology ontology CaptionOntology({ a crack on the road: 裂缝, a pothole on the road: 坑槽, a repaired patch on the road: 修补带 })跑完一批之后我发现提示词的写法对结果影响非常大。Grounded-SAM 对短语层面的描述非常敏感“a crack on the road”这种笼统表达容易漏掉细裂缝。我后来把描述改成更具体、更贴近实际照片的写法效果立刻不一样ontology CaptionOntology({ a thin dark crack on asphalt pavement: 裂缝, a wide pothole with broken asphalt edge: 坑槽, an asphalt patch with rectangular repair boundary: 修补带 })原因是 Grounding-DINO 这类模型在开放词表模式下更像是在做“文本特征与视觉特征的匹配搜索”。描述词越贴近真实场景特征空间里匹配到的区域就越准确。简单的类别名不是不能用但通常只能给出一个比较宽松的候选集合需要更长的时间在 NMS 里消耗。3.3 最小可跑的示例代码我建议第一次跑 autodistill 不要直接上大流程先用 20 张图片跑通最小示例确认标签格式没有问题再放开全量数据。一个最小可跑的流程如下from autodistill.detection import CaptionOntology from autodistill_grounded_sam import GroundedSAM from autodistill_yolov8 import YOLOv8 # 1. 定义提示词到标签的映射 ontology CaptionOntology({ a thin dark crack on asphalt pavement: 裂缝, a wide pothole with broken asphalt edge: 坑槽, an asphalt patch with rectangular repair boundary: 修补带 }) # 2. 教师模型自动标注 base_model GroundedSAM(ontology) base_model.label( input_folderraw_images, output_folderauto_labels ) # 3. 学生模型用伪标签训练 target_model YOLOv8(yolov8s.pt) target_model.train( auto_labels/annotations.json, auto_labels/images )这个示例跑通之后我就把它包装成一个循环执行的批处理脚本每天晚上新图片进入raw_images脚本自动标注、自动训练第二天早上直接看训练日志。这才是 autodistill 真正省时间的地方。3.4 为什么我会在两套流程里都用 Grounded-SAMX-AnyLabeling 可以加载本地模型推理autodistill 也可以把教师模型换成 Grounded-SAM。这里存在一个隐患两套流程都在调用 Grounded-SAM但它们的调用方式完全不同。在 X-AnyLabeling 里我是在图形界面上点击运行模型推理只针对当前一张图我可以立即看到结果并人工修正。在 autodistill 里Grounded-SAM 是以批处理方式运行的推理结果直接写成 JSON 文件我只能在事后通过脚本检查。这意味着同样的“提示词”在两套流程里需要维护两份配置。我的做法是把提示词和阈值参数统一放在一个 YAML 文件里两个流程都去读它。这样在 X-AnyLabeling 里优化过的提示词可以直接同步到 autodistill 的批处理任务中。否则你很容易出现手动标注效果不错、批处理结果却一言难尽的情况最后排查半天发现只是提示词没有同步。4. Grounded-SAM从一句话到分割掩码的完整链路如果说前面两个工具是生产线的框架那 Grounded-SAM 就是真正的“标注机械臂”。它接受一句自然语言描述输出检测框和分割掩码是整条流程里最能提升标注效率、也最容易出幺蛾子的部分。4.1 Grounding-DINO 做检测把文本变成框Grounding-DINO 的检测流程本质上是在做两件事先用文本编码器把提示词变成一组特征向量再用视觉编码器从图片中提取候选区域特征最后通过跨模态匹配决定“哪些区域与当前提示词的语义最相关”。它输出的不只是一个类别名还有一个置信度分数。这个分数就是后面我们要调的box_threshold。在我的场景里裂缝的视觉特征变化非常大——有的裂缝细得像头发丝有的裂缝宽到能塞进一只脚所以检测分数的跨度也很宽。如果阈值设得太高细裂缝就全漏了设得太低路面接缝、阴影边缘都会被误检成裂缝。4.2 SAM 做分割把框变成掩码拿到 Grounding-DINO 的检测框之后SAM 会以这个框为“提示”生成掩码。SAM 支持三种输入提示点、框、掩码。Grounded-SAM 使用的是框提示也就是把检测框编码成提示向量送入 SAM 的掩码解码器。SAM 的输出往往比检测框要精细得多因为它会尝试沿着目标的实际边缘游走。我在实测中发现SAM 对裂缝的边缘处理意外地好——它能把一条弯弯曲曲的裂缝完整抠出来而不是像传统分割模型那样只能给出一个大致的区域。这也是为什么我选择用 Grounded-SAM而不是直接用一个分割模型来做自动标注它同时兼顾了目标定位Detection和目标轮廓Segmentation两个环节可以互相补偿。4.3 安装与模型权重下载Grounded-SAM 的安装比前两个工具都要繁琐因为它依赖两个独立的模型权重。我以一个干净的 conda 环境为例把整个安装过程捋一遍git clone https://github.com/IDEA-Research/Grounded-Segment-Anything.git cd Grounded-Segment-Anything conda create -n gsa python3.10 conda activate gsa pip install torch2.0.1 torchvision0.15.2 pip install segment-anything pip install opencv-python pycocotools matplotlibGroundingDINO 需要单独编译安装。这一步对新手来说最容易卡住因为需要指定 GPU 算力cd Grounded-Segment-Anything python -m pip install -e GroundingDINO如果遇到CUDA_HOME相关的报错要先把 CUDA 工具链装好确认nvcc -V能正常输出。我见过很多人在这一步被卡了一两天最后发现是系统中存在多个 CUDA 版本导致编译时选错了环境变量。下载权重也很关键我用到的是这两个文件sam_vit_h_4b8939.pthSAM 的最大权重分割精度最高但显存占用也最大。groundingdino_swint_ogc.pthGrounding-DINO 的 Swin-T 权重检测速度较快适合批量推理。如果你的显卡只有 8GB 显存建议把 SAM 换成sam_vit_b_01ec64.pth否则很容易在推理长图时直接显存溢出。我自己的 GPU 是 12GB跑 640 分辨率上百张图偶尔也会崩后来乖乖把分辨率降到 640 以下才算稳定。4.4 实际推理中要调整的参数Grounded-SAM 的官方推理脚本里有三个参数值得细调参数作用经验值box_threshold检测框的置信度阈值0.25~0.35text_threshold文本与视觉特征的匹配阈值0.25~0.35nms_threshold重叠框去重阈值0.5~0.8我在路面裂缝场景里最终把box_threshold和text_threshold都设在 0.3。太高了漏检严重太低了误检一堆0.3 是一个相对平衡的点。用 NVIDIA 显卡推理时显存占用会随阈值降低而上升因为低阈值会保留更多候选框后续 SAM 要处理的分割请求也更多。一个经验是在批量跑之前先拿 20 张覆盖不同场景的图片试参把误检和漏检都调到一个可接受的范围再开始全量推理。不要指望一个阈值在所有场景下都完美你只能找一个“最不坏”的平衡点。4.5 从框到掩码后又该做什么Grounded-SAM 的输出不只是掩码它还会把检测框、类别名、置信度一并保存。我建议在流水线里保留这些原始输出不要直接丢弃——置信度是后续人工抽检的重要依据。实际写脚本时我一般这样组织输出output/ images/ 0001.jpg masks/ 0001.png boxes/ 0001.json0001.json里保存的是检测框坐标、类别名、置信度。这样如果某个类别的置信度整体偏低我可以单独筛出来重新检查而不是重新跑一遍完整推理。类似这种“先保存原始输出再派生格式”的组织方式让我在后期的数据清洗阶段节省了大量时间。5. 全流程串起来从 3000 张原始图片到可用的 COCO 数据集分开讲完三件工具之后我把整条流水线再合拢到一起。很多人把自动标注理解成“图进去标签出来然后训练”但真实项目远没有这么干净。你需要面对的是图片质量参差不齐、提示词在部分场景下失效、伪标签中有一批需要人工修正、格式转换可能丢信息。所以我把整个流程设计成了三层先人工把底子打好再用自动标注大规模扩展最后人工抽检收口。5.1 整体作业顺序我的完整流程分成七个步骤每一步之间有清晰的交付物用 X-AnyLabeling 手工标注 200 张基础图训练一个初始 YOLOv8-seg 模型。用初始模型跑一批 600 张新图在 X-AnyLabeling 里逐张修正形成第二轮训练集。在 autodistill 里配置好 CaptionOntology 和 Grounded-SAM 参数。对大量未标注图片自动生成伪标签。写脚本按置信度分层抽检伪标签挑出低置信度样本人工修正。把人工修正结果与自动标注结果合并为正式训练集导出 COCO JSON。训练正式版本的分割模型回到第 2 步继续迭代。我实际跑了三轮。第一轮后模型 mAP0.5 大概在 0.35 左右基本只能找出大坑槽。第二轮后到了 0.56细裂缝的召回有明显改善。第三轮之后稳定在 0.63人工抽检的工作量也开始显著下降。这个趋势说明自动标注的收益在“数据飞轮”里是滚雪球式上涨的——一开始人工费劲后面越来越轻松。5.2 数据翻转格式与文件夹结构怎么规范化整个流程跑完了数据格式问题会变成最大的隐形地雷。我踩过的最典型的坑是autodistill 输出的 JSON 是它自己的格式X-AnyLabeling 导出的是 COCO 格式两者合并时键名不一致导致训练脚本直接报错。我的解决方案是写一个统一的数据集整理脚本把一切最终输出都转成标准 COCO 格式并统一图片和掩码的命名规则。图片一律按六位数字编号掩码文件名与图片同名JSON 里的categories固定为[{id: 1, name: 裂缝}, {id: 2, name: 坑槽}, {id: 3, name: 修补带}]。所有后续训练代码都只认这个规范。5.3 人工抽检的关键方法自动标注永远需要人工抽检但抽检不能凭感觉。我的做法是按置信度给每个类别分层从每一层里随机抽固定数量的样本而不是简单地从全量里随机抽。低置信度层多抽一些高置信度层少抽一些这样能在有限的精力里覆盖最多的“危险样本”。路面病害场景还有一个特殊问题裂缝在阴影里、雨天积水反光时即使是人工判断也很模糊。遇到这种情况我会把图片单独放进一个uncertain文件夹等训练到后续版本时再重新审视而不是强行打一个不靠谱的标签。一个错误的标签会污染整个类别风险远大于暂时不标。5.4 迭代一次后的效果对比拿第一轮和第二轮的模型做对比差异非常直观指标第一轮后第三轮后训练图片数2603000mAP0.50.350.63人工纠错耗时/百张60分钟15分钟细裂缝召回率40%左右75%以上这些数字谈不上顶尖但对于一个路面病害检测的落地项目来说已经具备工程可用性。更重要的是整个流程后期的边际成本非常低——新增一批 500 张图片从自动标注到抽检修正半天就能完成这在纯手工时代是不可想象的。6. 这一轮下来我踩过的坑提前帮你避掉最后把我在实际运行中遇到的最典型的几个问题集中说一下。这些问题在官方文档里基本不会写但在真实项目里几乎必然会遇到。6.1 “漏检”不一定是模型弱可能是提示词不对我最开始写提示词用的是“a crack on the road”跑出来的结果惨不忍睹很多细裂缝完全没有被检测到。后来我分析了一下发现 Grounding-DINO 在匹配时是拿整句描述的语义特征与图像区域特征做比对“crack”这个词的视觉特征其实很模糊它可以指冰裂纹、龟裂、毛发状裂缝模型不知道你要的是哪一种。把提示词改成更具体的行为描述之后漏检率立刻降了下来。比如“a long continuous dark line on asphalt with small branches”比“a crack”要有效得多。多写一些同义词、近义词或者用短语描述颜色和形状都比单一类别名管用。6.2 显存溢出与批处理崩溃批量推理时最常见的杀手就是显存溢出。我一开始图省事把所有图片设置为 1024 分辨率跑了几百张就崩了。后来我把分辨率调到 768 或 640并把批处理脚本里的一次性推理张数设为 1稳定性立刻提高。如果一张大图上需要分割的目标特别多SAM 的推理时间会显著上升这时候可以考虑先只保留 Grounding-DINO 的检测框对掩码做按需后处理而不是对全图掩码同时生成。6.3 文件名与标注名的错位这个问题特别隐蔽。我在合并数据集时用 Python 的os.listdir()读取文件列表然后按顺序给图片分配标注。Windows 和 Linux 的文件排序规则不一样同一个脚本在两个系统上跑出来的顺序可能不同。结果就是我第一批数据里有两张图片的标注和张冠李戴。后来的解决方法是训练脚本里完全用图片 ID 或显式的文件名映射来索引标注绝不依赖文件系统的排序顺序。这一点对于任何做过自动化训练的人来说可能觉得很基础但实际发生的时候却特别容易疏忽。6.4 显而易见的边缘案例自动标注模型对“常规场景”表现良好但到了遮挡严重、强光、极暗环境、雨天反光这一类边界条件可靠性会急剧下降。如果你手里有这类图片我强烈建议不要直接进批处理而是单独走人工流程。原因在于这些边界案例往往也是模型上线后最需要覆盖的情况——如果自动标注时丢掉它们训练出来的模型在真实环境下大概率会出问题。宁可花一点时间手工标注这批图片也不要让它们变成数据集里的噪声。6.5 伪标签置信度的取舍最后一个常见问题伪标签的置信度阈值到底该设多少。设高了漏检多数据量上不去设低了误检多训练噪音大。我的经验是先用一个相对保守的值比如 0.4跑一批检查误检率如果误检率太高就缓慢调低同时加大抽检比例。这个取舍没有标准答案跟你的目标类别复杂度、数据场景、模型部署环境都有关。我通常的做法是训练过程中同时输出一个“无人工修正”版本和“人工修正”版本的精度对比如果差距小于两个点说明伪标签质量够用人工介入的比例可以降低。整个项目做下来我的最大感受是自动标注不是“机器替代人”而是把人的精力从“画框”转移到“审图”上。你省下来的时间不是用来休息的而是用来把数据质量做得更好。如果你打算在自己的项目里复制这条流程我的建议是先拿 100 张图小规模跑通确认每一步的产物都正确再放量。不要一上来就批量处理几千张图因为一旦格式或提示词有问题返工的成本远高于省下的时间。

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

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

免费获取报价 →
↑