简介:labelImg-master.zip 是开源图像标注工具 labelImg 的源代码压缩包,面向计算机视觉与深度学习方向的开发者、研究人员,用于高效创建和编辑目标检测、语义分割所需的像素级标注。压缩包共 123 个文件,其中以 Python 源码(.py)、编译缓存(.pyc)、图标资源(png/svg)、配置文件(cfg/yml/properties)及说明文档(md/rst)为主,整体仅 6.81MB,结构紧凑,便于下载与部署。目前已有 11265 人浏览学习,是图像标注领域常用工具之一,在目标检测与分割任务中具有较高使用率。解压后即可获得完整主程序、依赖说明、示例图像与许可文件;其中 GUI 与核心模块齐备,支持矩形框、多边形等标注方式,可输出 YAML/XML 格式,直接对接 YOLO、Faster R-CNN 等模型训练流程。适合需要快速开展数据标注工作的团队或个人,无论是目标检测模型训练前的数据准备,还是语义分割项目的标注流程,都能从中获得稳定支持。
1. 下载完 labelImg-master.zip,先搞懂它是干什么的
花半小时下载了一个叫 labelImg-master.zip 的压缩包,解压以后发现里面全是 .py 文件,双击没有任何反应,连个界面都没弹出来——这不是文件坏了,而是你还没搞懂它到底是什么。如果你正在做目标检测,手头攒了一批图片准备训模型,这个工具就是数据准备阶段绕不开的一环:把图片里的目标用矩形框标出来,生成训练框架能直接读的标注文件。很多人以为标注工作最费时间的是画框,实际踩过一轮才知道,最费时间的是格式对不上、命名不规范、画完没保存导致的返工。这篇文章就围绕这个压缩包,把安装、标注、格式检查、常见坑一次讲清楚,目标是让你拿到手就能直接开工。
2. labelImg 的选型逻辑:VOC 与 YOLO 两种标注格式,用哪个看训练框架
2.1 labelImg 解决的是哪一环:目标检测的数据准备
目标检测模型的训练输入,不只是图片本身,还需要告诉模型“这张图里某个目标在哪个位置、属于哪个类别”。这个信息靠人工标注来产生,而标注工具负责把人的视觉判断转换成结构化的数据文件。labelImg 在整个流程里的位置非常靠前,它产出的是最原始的标注文件,后续的划分训练集、数据增强、格式转换全部依赖这份文件。所以它的稳定性比花哨程度更重要——你不需要它帮你做复杂的滤镜、透视矫正或者半自动预标注,你需要它老老实实画框、保存、给下一张。
我一般会把标注工具的选择原则归纳成三条:离线可用、格式标准、上手成本低。在线协作平台虽然功能更多,但对个人开发者来说,图片要传服务器,标注结果还要导来导去,反而是负担。labelImg 的定位恰好卡在这条线上:单机离线运行,输出 PascalVOC 的 XML 文件,也能一键切换成 YOLO 的 TXT 文件,对国内开发者来说最常用的检测框架都能直接消费这两种格式。
2.2 PascalVOC 标注文件:XML 结构与坐标换算
labelImg 默认保存格式是 PascalVOC 风格的 XML 文件,一个图片对应一个同名 XML。打开一个典型的标注文件,结构一目了然:
<annotation> <folder>JPEGImages</folder> <filename>img_001.jpg</filename> <path>/data/images/img_001.jpg</path> <source> <database>Unknown</database> </source> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>person</name> <bndbox> <xmin>100</xmin> <ymin>150</ymin> <xmax>320</xmax> <ymax>480</ymax> </bndbox> </object> </annotation>这段 XML 里,filename标注的是图片文件名,size里的 width 和 height 是图片的原始像素尺寸,object下的name是类别名,bndbox里的四个值分别代表目标的左上角和右下角坐标。这里的坐标单位是像素,原点在图片左上角,向右为 x 正方向,向下为 y 正方向。path字段记录的是你打开图片时的绝对路径,在换机器或者挪目录后很容易失效,所以训练框架读取时一定要以filename为准去拼接路径,不要依赖path。
这个格式最大的好处是可读性强,任何语言都能用现成的 XML 解析库读取。但坏处是数据冗余,一张图里如果有几十个大目标,XML 文件体积会明显膨胀,训练前的数据加载也会多一层解析开销。
2.3 YOLO 标注文件:一行一个框的归一化坐标
PascalVOC 格式虽然标准,但 YOLO 系列的训练流程更偏好自己的 TXT 格式。在 labelImg 里可以用快捷键切换保存格式,切过去后产生的标注文件长这样:
0 0.28125 0.38426 0.22917 0.30556一行代表一个目标,一共 5 个数字:第一个是类别编号,从 0 开始;后面四个依次是归一化后的目标中心 x、中心 y、框宽度、框高度。归一化的意思是所有数值都除以图片的宽或高,结果落在 0 到 1 之间。比如要计算中心 x,公式是(xmin + xmax) / 2 / width,把刚才 XML 里的像素坐标代进去,就能对上 TXT 里的第一个小数。
VOC 坐标和 YOLO 坐标的换算关系,是把数据从一个框架迁到另一个框架时最容易翻车的地方。我习惯在项目里保留一份 VOC 格式作为原始数据,模型训练时再实时转换成 YOLO 格式,这样原始标注信息不会丢。但如果你只用 YOLO 格式,建议额外保存一份图片尺寸记录,否则哪天想转回 VOC 做可视化校验,没有宽高信息就会很被动。
2.4 同类标注工具对比:什么时候非它不可
有读者可能会问,现在不是有很多标注工具吗,为什么还要用这个老牌的压缩包?这里放一个选型参考,按实际场景判断:
| 工具类型 | 适合场景 | 局限 |
|---|---|---|
| labelImg 这类轻量桌面工具 | 个人或三五人小团队、单机离线、纯矩形框检测 | 只支持矩形框,不支持多边形分割,无协作功能 |
| 多边形标注工具 | 实例分割任务,目标轮廓不规则 | 操作成本高,输出格式复杂,画一个目标要十几秒 |
| 在线标注平台 | 团队并行标注、需要任务分配和进度管理 | 图片要上传服务器,数据出网环节有安全顾虑 |
| Web 版标注服务 | 短期外包大量数据 | 费用高,标注质量不可控,二次清洗成本大 |
我做目标检测项目时默认用 labelImg,除非任务里明确要分割掩码才会换多边形工具。它对小团队的不可替代性在于:不用配服务器,不用装数据库,解压后有几个 Python 依赖就能跑,任何一台开发机能随时开工,这恰恰是很多在线方案做不到的。
3. 把 labelImg 跑起来:三平台安装命令与启动参数设置
3.1 Windows 启动失败的基层检查:Python 与 PyQt5 依赖
在 Windows 上双击 labelImg-master.zip 里的 labelImg.py 没反应,是最常见的启动翻车现场。原因很简单:这个工具本质是一个 Python 脚本,GUI 基于 PyQt5 框架,你的系统里如果没有对应的 Python 环境和依赖库,双击脚本自然只是闪一下或者根本没动静。正确的做法是先配好依赖,再在命令行里启动:
python -m pip install --upgrade pip pip install pyqt5 lxml cd labelImg-master python labelImg.py第一行是升级 pip 工具,防止装包时因为版本太老出现奇奇怪怪的报错。第二行安装两个核心依赖:pyqt5 负责渲染标注界面,lxml 负责读写 XML 标注文件。装完进入解压后的目录,直接用 python 解释器执行脚本。这里有个细节:如果你的电脑装了多个 Python 版本,建议用python -m pip而不是直接pip,确保装依赖的 Python 和你运行脚本的 Python 是同一个环境,否则依赖白装,启动照样报错。
启动后如果提示缺少别的模块,比如ModuleNotFoundError: No module named 'libs',多半是你在别的目录下运行的脚本,没有把 labelImg-master 作为当前工作目录。确认一下终端路径是否已经切到解压目录,再重复运行一次即可。
3.2 Linux 与 macOS 的解压直用流程
Linux 和 macOS 上的安装思路本质上一样,只是依赖安装方式略有区别。假设你已经在终端里把压缩包解压好了,剩下的流程就三步:
cd ~/downloads/labelImg-master pip install -r requirements/requirements-linux-python3.txt python labelImg.pymacOS 用户把 requirements 文件名换成 requirements-macos.txt 即可。这个 requirements 文件比手动敲 pyqt5 和 lxml 更完整,里面还包含了其他运行必要的辅助库。如果 pip 因为系统权限报错,加上--user参数在你自己的用户目录下安装,不会污染系统 Python。
macOS 上比较容易踩的一个坑是 PyQt5 安装成功后,运行时提示Segmentation fault。这种现象多数不是代码问题,而是系统的图形环境和 PyQt5 版本不匹配,常见做法是卸掉重装指定版本:
pip uninstall pyqt5 pip install pyqt5==5.15.10如果用的是 Conda 环境,也可以让 Conda 帮忙装 Qt 库栈,稳定性通常比 pip 直接装更好。启动后的界面和 Windows 上没有任何区别,三平台的项目标注数据格式完全通用。
3.3 这三个启动参数,能让标注效率直接翻倍
labelImg 启动时支持带参数,很多老手都不知道它可以直接打开图片目录和标注目录,省掉每次手动点文件夹的重复操作:
python labelImg.py ./images ./annotations ./classes.txt第一个参数是图片目录,程序启动后会直接把该目录下的图片加载到左侧文件列表;第二个参数是标注文件的输出目录,保存的 XML 或 TXT 会统一写到这里;第三个参数是类别清单文件,里面每行一个类别名,标注时下拉框里直接出现预设类别。这三个参数组合起来,能让你从“打开界面 → 手动选图片目录 → 手动设保存目录 → 逐个输入标签”的循环里解放出来。
另外两个界面设置建议在开工前就打开:一是 View 菜单下的 Auto Save mode,开启后每切换一张图片都会自动保存当前标注;二是在编辑框里确认保存目录和图片目录一致,避免标注文件散落到工作目录。别忘了,这类工具没有账号体系,不会帮你云同步,所有数据都保存在本地,把目录理顺就是给自己省时间。
4. 完整标注一轮数据:目录规范、快捷键表与 XML 输出自查
4.1 标注前的准备工作:命名规范与目录结构
拿到一批原始图片,别急着打开工具开画。数据准备工作里的命名规范,直接决定了训练阶段会不会出低级错误。我在项目里固定的目录结构是这样的:
my_project/ ├── images/ # 原始图片,统一为英文命名 │ ├── img_001.jpg │ ├── img_002.jpg │ └── ... ├── annotations/ # 标注输出目录 └── classes.txt # 类别清单图片文件名必须满足三个要求:全英文、无空格、不含中文。原因后面避坑章节会细说,这里先记住结论:img_001.jpg这样的命名最稳妥。图片文件名要和标注文件的主名完全一致,img_001.jpg对应img_001.xml,这是所有检测框架默认的关联方式,一旦不一致,训练时数据加载器会直接跳过这张图,既浪费你刚才的标注工作,还让训练数据量悄悄缩水。
classes.txt的内容是每个类别一行,比如:
person car bicycle类别名的拼写、大小写必须从第一天就固定下来。如果后续发现类别清单有变动,可以把 classes.txt 改好,再通过启动参数重新加载,不用修改任何 XML 文件。
4.2 画框快捷键与标注节奏
labelImg 的标注操作完全可以用键盘驱动,熟练后鼠标只在画面里画框时使用。我平时最常用的一组快捷键整理如下:
| 快捷键 | 功能 | 说明 |
|---|---|---|
| W | 画矩形框 | 按住左键拖动,画完弹出类别选择框 |
| D | 下一张图片 | 会自动保存当前图,前提是开了 Auto Save |
| A | 上一张图片 | 用于回头补充漏标目标 |
| Ctrl + S | 手动保存 | 任何时候都可以按,不会覆盖已有框 |
| Ctrl + D | 复制当前框到下一张 | 适用于连续帧图片,相同位置目标快速复用 |
| Del | 删除选中的框 | 框被点中后边框高亮,再按 Del 删除 |
| Space | 在图片和标注框之间切换显示 | 可以暂时隐藏所有框,检查漏标 |
| Ctrl + E | 隐藏空标注图片 | 快速跳到还没有标注的图片 |
标注节奏上,我习惯先“粗筛”后“细补”:第一遍快速把明显目标画完,遇到边缘模糊、被遮挡的目标先跳过;整目录过完一遍后,再回头用 A 键逐张检查,专门处理那些第一遍没决定的目标。这样做的好处是不要让完美主义打断流畅度,一鼓作气的标注效率远比边画边纠结高得多。一个小项目几千张图,按每张图 3 到 5 个目标计算,一个人一天到一天半能完成一轮,前提是目录规范和类别清单都提前准备好了。
4.3 XML 输出字段逐个解析:从文件名到 bounding_box
标完一张图,保存之后建议当场验证一下 XML 内容,不要攒上百个文件再回头看。用 Python 快速读取一张测试:
from lxml import etree root = etree.parse('annotations/img_001.xml').getroot() # 检查文件名和实际图片是否匹配 filename = root.findtext('filename') print('filename:', filename) # 逐个打印目标框 for obj in root.iter('object'): name = obj.findtext('name') bbox = obj.find('bndbox') xmin = int(bbox.findtext('xmin')) ymin = int(bbox.findtext('ymin')) xmax = int(bbox.findtext('xmax')) ymax = int(bbox.findtext('ymax')) print(f'class={name}, bbox=({xmin}, {ymin}, {xmax}, {ymax})')这段脚本的核心价值是告诉你 XML 里的值到底对不对。findtext是 lxml 里取子节点文本的常用方法,比.text更保险,节点不存在时返回 None 而不是抛异常。坐标转换成 int 后,要肉眼检查 xmin、ymin 一定小于 xmax、ymax,如果出现 xmax 小于 xmin 的情况,说明画框时发生了交叉,这个框在训练时可能会让模型学到错误的边界。检查脚本本身也是积累,它会演变成最后一章里的完整体检工具。
5. labelImg 常见问题避坑:五个翻车现场与修复记录
5.1 现象:图片加载报错,文件列表灰的
很多人反映打开图片目录后,左侧文件列表里一张图都不显示,终端里报类似Cannot load image的信息。原因不在 labelImg 本身,而是图片路径里带了中文或特殊字符,PyQt5 在 Windows 下处理中文路径时先转成百分号编码,再反解回来就找不到文件了。这是 Qt 自带的老毛病,和图片格式没有关系。
解决思路只有一个:把整个项目和图片目录放到纯英文路径下,文件名也统一改成 ASCII 字符。不要尝试在代码里打补丁去修编码,每台机器表现还不一样,修完这版下次又犯。我给的建议是最终项目根目录都保持英文路径,图片名用数字加下划线,比如img_001.jpg。
5.2 现象:XML 文件名和图片名对不上,训练时丢数据
标注完一批图,发现 XML 文件名和图片名有一个点没对齐,比如图片是car_001.JPG,标注文件却保存成car_001.jpeg.xml。原因有两个:一是图片后缀使用了大写.JPG或.JPEG,labelImg 在写入 filename 字段时没有做统一规整;二是在“打开文件”模式下标注,保存路径和基准目录会沿用上一次的历史路径,容易写偏。
解决方式是开工前统一后缀,全部转成小写.jpg;打开图片时只用“打开目录”模式,不要一张一张用“打开文件”。保存目录在启动参数里显式指定,避免默认写到终端当前路径。如果已经出现对不上的情况,写个小脚本批量把 XML 里的 filename 和主文件名对齐即可,不需要重标。这里提醒一下,图片后缀不一致是个隐蔽问题,从img_001.jpg突然混进来一个img_002.jpeg,模型的图像解码器能读,但标记文件匹配逻辑会失效。
5.3 现象:程序崩溃,大半天的标注白干了
用着用着程序闪退,重启后打开标注目录,发现只有前 20 张图有 XML,后面 180 张全空。这是没开自动保存时最典型的翻车现场。labelImg 默认不会每画一个框就自动写盘,如果中途崩溃,内存里没落盘的标注全部丢失,这种数据丢失没有任何后悔药。
解决方式是进入菜单 View,勾选 Auto Save mode,它的作用是每切换一张图片时强制把当前图片的标注写入文件。即使这样,我仍然建议养成每小时按一次 Ctrl + S 的习惯,因为如果长时间在同一张图上反复调整选框,切换前崩溃还是会丢。还有一种更稳妥的做法:给项目目录接一个本地文件同步,实时把 XML 备份到另一块磁盘或目录,成本极低但效果极好。
5.4 现象:画框错位,框和鼠标位置对不上
在超大分辨率图片上画框,尤其是 4K 以上的长图,可能出现鼠标在目标左上角按下,生成的框却明显偏移的情况。这个现象在图片缩放比例不是 100% 时更容易暴露,本质是绘图控件的缩放坐标和图像逻辑坐标之间的映射发生了漂移,拖拽过快时控件来不及把鼠标事件转换回图像坐标。
解决方法是画框前先把图像视图调整为“适应窗口”,或者直接选择 100% 缩放画框。不要在一张被拉伸到很小比例的图上画精细小目标,宁可放大到目标清晰可辨后再画。另一个辅助做法是打开 View 菜单里的坐标显示,实时观察坐标值的变化,如果鼠标不动坐标也在跳,说明视图状态有问题,先把缩放比例复位。
5.5 现象:训练时报类别数量不对,Loss 异常波动
标注工作全部完成,进入训练阶段,却出现类别数与设计不符,或者 Loss 一直不下降。排查一圈发现是类别名不统一:同一辆车,有的框标成car,有的标成Car,还有的标成了“汽车”。这些标签分布在几百个 XML 里,肉眼根本看不出来。
解决方式有两种:一是严格从源头上用classes.txt预置类别,标注时永远从下拉列表选,不手动输入;二是完成后写脚本统计所有 XML 里出现的名称去重,超出类别清单的一律标红处理。建议两条一起做,如果某天临时让同事帮忙标了几十张,第二种检查能兜底外部人力带来的拼写污染。
6. 标注完别急着训练:先跑一遍我常用的标注结果体检脚本
标注文件全部生成后,我从不直接丢给训练脚本,而是先跑一段“体检”。这一步看起来多花五分钟,实际上能拦住大部分训练阶段才暴露的数据问题,省下的是反复调试的半天时间。脚本的逻辑围绕四个检查点:文件对应关系、类别名合法性、空标注文件、坐标边界。
import os import glob from lxml import etree img_dir = 'images' xml_dir = 'annotations' classes = set('person car bicycle'.split()) # 换成你的 classes.txt 内容 xml_list = glob.glob(os.path.join(xml_dir, '*.xml')) print(f'XML 文件数量: {len(xml_list)}') for xml_path in xml_list: xml_name = os.path.splitext(os.path.basename(xml_path))[0] # 1. 图片是否存在 img_path = os.path.join(img_dir, xml_name + '.jpg') if not os.path.exists(img_path): print(f'[缺失图片] {xml_name} 对应的 jpg 不存在') root = etree.parse(xml_path).getroot() # 2. XML 内文件名是否一致 filename = root.findtext('filename') if filename and filename.split('.')[0] != xml_name: print(f'[文件名不一致] {xml_path}: 内部 filename={filename}') # 3. 空标注与类别名 objs = root.findall('object') if not objs: print(f'[空标注] {xml_path}: 没有 object 节点') for obj in objs: name = obj.findtext('name') if name not in classes: print(f'[未知类别] {xml_path}: {name}') # 4. 坐标合法性 for obj in objs: bndbox = obj.find('bndbox') xmin = int(bndbox.findtext('xmin')) ymin = int(bndbox.findtext('ymin')) xmax = int(bndbox.findtext('xmax')) ymax = int(bndbox.findtext('ymax')) if xmin < 0 or ymin < 0 or xmax <= xmin or ymax <= ymin: print(f'[坐标异常] {xml_path}: bndbox=({xmin}, {ymin}, {xmax}, {ymax})') print('体检完成,以上输出全部为空则说明基础数据没有发现问题')这段脚本我让它在项目根目录下运行,图片目录和标注目录作为变量放在顶部方便修改。glob.glob负责收集所有 XML,os.path.splitext用来剥离后缀拿到主文件名,这两行是文件匹配检查的关键。坐标检查里用xmax <= xmin而不是==,因为在 labelImg 里一个点宽度的废框也不应该出现。跑完脚本,所有输出为空,我才把数据放进训练流程。
如果后续用 YOLO 格式训练,我会在格式转换后加一段归一化坐标检查,核心判断是确认每个小数的值域都在[0, 1]区间内,超出就是转换脚本有 bug。曾经有一回我帮 A 同学排训一个检测模型的问题,Loss 一直居高不下,最后排查到标注数据里混了几条中心坐标算错的 YOLO 行,就是被这个检查拦下来的。一个工具能稳定跑完整个项目周期,靠的不是它有多少功能,而是你在每一步都守着数据质量这条底线。希望这些经验能帮你少走一段弯路,也祝你在标注这条辛苦活上不在格式问题上再吃暗亏。
本文还有配套的精品资源,点击获取