简介本资源为labelImg目标检测标注工具的图文操作教程文档面向计算机视觉与机器学习方向的初学者、数据标注人员及算法工程师帮助其快速掌握图像标注流程与标注文件格式转换方法。压缩包内共1个doc文档约2.96MB内容涵盖软件安装与Auto Save设置、图片文件夹打开与保存目录配置、画框标注与快捷键切换、predefined_classes.txt预定义类别修改以及VOC与YOLO两种标注格式的生成与转换说明。教程结合电塔标注等实际案例展示了XML文件中图片大小、通道数、标签名称与选框位置等参数含义并说明如何借助xml_to_csv与tfrecord工具完成后续格式转换同时给出YOLO格式中类别id与归一化中心点坐标、宽高的组织方式。目前已有731人学习适合需要系统入门标注工具、对照实操与查漏补缺的读者参考。1. 标注工具选型为什么 labelImg 仍是目标检测入门的第一站如果你刚拿到一批图片准备做目标检测第一件让人头疼的事往往不是选模型而是怎么把几百上千张图里的目标框出来。labelImg 就是干这个的一个用 Python 写的轻量级图像标注工具支持 Pascal VOC 的 XML 和 YOLO 的 txt 两种主流格式装完就能用不需要 GPU也不需要联网。它解决的是「从原始图片到可训练标注文件」这一段最枯燥、也最容易出错的环节。适合谁适合刚入门目标检测的开发者、需要快速验证数据集可行性的算法同学以及带学生做课程设计的一线工程师。我见过太多人模型调得飞起结果标注格式错位训练时 loss 不降回头查半天才发现是坐标归一化的问题——这种血泪经验后面会细讲。2. 装好 labelImg 并跑通第一张图的标注2.1 三种安装方式的选择与取舍labelImg 的安装方式主要有三种pip 直接装、源码运行、以及打包好的可执行文件。选哪种取决于你的系统环境和是否需要改源码。pip 安装最省事适合 Windows 和 Linux 桌面环境# 建议在虚拟环境里装避免和系统 Python 包冲突 python -m venv labelenv source labelenv/bin/activate # Windows 用 labelenv\Scripts\activate pip install labelImg装完后直接在终端敲labelImg就能启动。注意pip 包名里那个大写 I 是官方约定写成全小写labelimg在部分镜像源上会找不到包。源码运行适合需要改快捷键、改默认保存路径的人git clone https://github.com/HumanSignal/labelImg.git cd labelImg pip install -r requirements/requirements-linux-python3.txt python labelImg.py可执行文件方式适合完全不想碰命令行的 Windows 用户下载后双击运行但版本更新慢遇到高分辨率屏幕可能缩放异常。我一般推荐 pip 装因为升级和卸载都干净。如果公司网络对 pip 源有限制换成国内镜像即可这里不展开。2.2 界面里每个按钮对应的实际操作启动后界面分三块左侧文件列表、中间画布、右侧标签列表和文件列表。核心操作就几个快捷键记住能省一半时间W画矩形框A上一张D下一张CtrlS保存当前标注CtrlShiftS另存为Del删除选中的框画框时按住鼠标左键拖拽松手后会弹出标签输入框。如果标签已经存在直接从右侧列表点选避免手打出错别字——标签名不一致是训练时类别对不上的头号原因。2.3 切换 VOC 与 YOLO 格式的关键设置labelImg 默认保存为 Pascal VOC 的 XML 格式。要切到 YOLO 的 txt 格式点左侧工具栏的「PascalVOC」按钮它会循环切换成「YOLO」。切换后保存的 txt 文件里每行是类别索引 中心x 中心y 宽 高且坐标都是归一化到 0~1 的浮点数。这里有个容易翻车的点YOLO 格式依赖一个classes.txt文件来定义类别顺序。这个文件必须和图片放在同一目录或者你在 labelImg 里通过「Open Dir」打开图片目录时它会自动读取同目录下的classes.txt。如果这个文件缺失或类别顺序和训练时不一致模型学到的类别就是错位的。# 一个典型的 classes.txt 内容每行一个类别顺序即索引 person car dog提示切换格式后之前用另一种格式标好的文件不会自动转换需要重新标注或写脚本转换。建议项目开始前就定好格式。3. 把标注结果整理成能直接喂给训练脚本的数据集3.1 目录结构该怎么摆标注完成后你得到的是一堆散落的图片和同名标注文件。训练框架通常要求固定的目录结构。以 VOC 格式为例常见做法是dataset/ ├── JPEGImages/ # 所有图片 ├── Annotations/ # 所有 XML ├── ImageSets/ │ └── Main/ # 存放 train.txt / val.txt └── classes.txtYOLO 格式则更简单图片和 txt 放一起再配一个classes.txt和训练用的train.txt、val.txt列表文件。3.2 用脚本自动划分训练集和验证集手动分文件容易漏、容易重。我一般写个小脚本按 8:2 随机划分并生成列表文件import os import random # 图片目录按实际路径改 img_dir dataset/JPEGImages # 输出列表的目录 out_dir dataset/ImageSets/Main os.makedirs(out_dir, exist_okTrue) # 只取图片文件排除隐藏文件 names [f[:-4] for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png))] random.seed(42) # 固定随机种子保证每次划分一致 random.shuffle(names) split int(len(names) * 0.8) train, val names[:split], names[split:] with open(os.path.join(out_dir, train.txt), w) as f: f.write(\n.join(train)) with open(os.path.join(out_dir, val.txt), w) as f: f.write(\n.join(val)) print(f训练集 {len(train)} 张验证集 {len(val)} 张)这段脚本的关键参数是random.seed(42)固定种子后每次运行划分结果一致方便复现实验。split控制比例数据量少时可以调到 0.9。注意文件名去掉了扩展名因为大多数训练脚本读列表时只认文件名主干。3.3 校验标注文件是否和图片一一对应标注过程中难免出现图片删了但 XML 还在或者 XML 内容为空的情况。训练前跑一遍校验能省很多事import os import xml.etree.ElementTree as ET img_dir dataset/JPEGImages ann_dir dataset/Annotations imgs {f[:-4] for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png))} anns {f[:-4] for f in os.listdir(ann_dir) if f.endswith(.xml)} # 有图无标注、有标注无图都要报出来 print(缺标注的图片:, imgs - anns) print(缺图片的标注:, anns - imgs) # 检查空标注文件 for a in anns: tree ET.parse(os.path.join(ann_dir, a .xml)) objs tree.findall(object) if len(objs) 0: print(空标注:, a)imgs - anns是集合差集直接列出不匹配的文件名。空标注文件在训练时会被当成负样本如果本意是正样本就会拖低召回。这个检查我每次建完数据集都会跑一遍。4. 标注环节最容易踩的五个坑4.1 框贴边导致坐标越界现象训练时提示坐标超出图像范围或者可视化时框跑到图外。原因画框时鼠标拖到了图像边缘外labelImg 记录的坐标可能略大于图像宽高。解决画框时留一两个像素边距或者写脚本把坐标裁剪到[0, width-1]和[0, height-1]范围内。4.2 标签名大小写不一致现象训练日志里类别数比预期多比如person和Person被当成两类。原因不同人标注时手输标签大小写没统一。解决提前建好classes.txt标注时只从右侧列表点选禁止手动输入。已经标完的用脚本统一转小写。4.3 YOLO 格式的类别索引错位现象模型把车识别成人或者类别完全乱套。原因classes.txt的顺序和训练配置里的类别顺序不一致。解决把classes.txt作为唯一类别来源训练脚本直接读它生成类别列表不要在两处分别维护。4.4 中文路径导致保存失败现象点保存没反应或者报编码错误。原因labelImg 在某些系统上对中文路径支持不好。解决图片目录和标注目录都用纯英文路径这是最省事的办法。4.5 忘记保存就切图现象标了十几张回头发现文件没生成。原因labelImg 不会自动保存切图前必须按CtrlS。解决养成「画完框就按 CtrlS」的习惯或者在设置里勾选自动保存选项部分版本支持。注意标注是个体力活但格式错误会让后面所有训练工作白费。宁可标慢一点也要保证每张图保存后抽查一下。5. 用 Python 批量转换与校验标注格式的进阶技巧5.1 VOC 转 YOLO 的完整脚本项目中途换格式是常事。与其重标不如写个转换脚本。下面这个把 VOC 的 XML 转成 YOLO 的 txtimport os import xml.etree.ElementTree as ET # 类别列表顺序即索引必须和训练时一致 classes [person, car, dog] ann_dir dataset/Annotations out_dir dataset/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(ann_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue # 跳过未定义类别避免索引越界 cls_id classes.index(name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 归一化并转为中心点宽高 cx (x1 x2) / 2.0 / w cy (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_name xml_file.replace(.xml, .txt) with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))关键点classes列表的顺序决定了 txt 里的类别索引必须和训练配置完全一致。if name not in classes这行是后悔药防止 XML 里出现没定义的类别导致索引报错。坐标保留 6 位小数足够再多没必要。5.2 转换后怎么验证没转错转完不能直接开训抽几张图把 YOLO 的 txt 画回图上看看import cv2 img cv2.imread(dataset/JPEGImages/000001.jpg) h, w img.shape[:2] with open(dataset/labels/000001.txt) as f: for line in f: cls_id, cx, cy, bw, bh map(float, line.split()) # 反归一化回像素坐标 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)打开check.jpg如果框的位置和原图目标吻合说明转换正确。这个方法比看数字直观得多我每次转换后至少抽三张不同场景的图验证。5.3 一个提高标注效率的小习惯标注前先把图片按场景分组相似的图放一起标标签可以连续点选不用反复切换。另外labelImg 支持「复制上一张的标注」功能快捷键CtrlD对于连续帧或相似场景能省大量重复劳动。但复制后一定要逐张检查目标位置变了就得手动调别偷懒。希望帮到你。本文还有配套的精品资源点击获取