简介:这是一份可运行的LabelImg历史版本压缩包,面向图像标注初学者、计算机视觉研究者及需要兼容旧项目的开发者,用于在目标检测与图像识别预处理中高效完成矩形框、多边形标注。资源共75个文件,以png图标、py脚本、pyc编译文件为主体,辅以svg矢量图、sh启动脚本、qrc资源清单、txt标签配置与md说明文档,包体仅977KB,轻量便携。已有4727人学习下载,说明其在规避新版兼容性问题或依赖固定版本时具有实际参考价值。包内不仅包含labelImg.py主程序,还提供canvas.py、shape.py、labelFile.py等核心模块,配合resources.qrc、predefined_classes.txt与README文档,既可支撑训练数据集的生成,也方便开发者学习标注工具的内部实现并自定义类别。支持输出PASCAL VOC XML与YOLO txt格式,配合示例图片可快速上手,对目标检测教学、科研实验和轻量级数据标注任务均有直接帮助。
1. 这个 LabelImg 可运行版本值得下:绕开环境折腾,直接开始标注
某项目要做目标检测 demo,训练数据里缺了标注,几百张图片堆在目录里等着画框。换过 pip 安装,提示缺 Qt 库;换 conda 环境,又撞上 Python 版本冲突。最后找了别人打包好的可运行版本,解压后双击就能打开界面,省下了整整两小时。LabelImg 是图片标注工具里的常客,支持 PascalVOC 和 YOLO 两种输出格式,做目标检测数据集时几乎是绕不开的一环。它解决的就是“把图片变成能训练的数据”这件事,新手拿过来就能用,熟手也可以用它快速出一批小样本。这篇文章按我从下载到使用的真实路径讲起,安装选型、快捷键配置、格式切换脚本、踩坑记录都放在后面,照着走基本不会翻车。
2. 安装与启动:拿到可运行版本后怎么选、怎么配
2.1 三种分发形态怎么选:源码、pip 与打包版
LabelImg 最常见的分发形态有三种。第一种是 pip 安装,执行pip install labelimg后命令行启动,优点是能拿到最新代码,缺点是依赖链太长,Windows 上经常卡在 PyQt5 和 OpenCV 的版本冲突上,装完启动直接报错的情况很常见。第二种是本地编译,要从源码把资源文件编译出来,需要执行类似pyrcc5 -o libs/resources.py resources.qrc的步骤,新手到这一步折腾半天,心态基本就崩了。第三种就是标题里说的“可运行版本”,通常把 Python 环境、PyQt5、libs 目录全部打进去,解压后直接执行可执行文件就能打开界面。
我一般推荐直接用可运行版本,原因很简单:标注工具的核心功能这些年没怎么变,稳定能跑比版本新更重要。可运行版本里自带启动入口,不用管 Python 版本,不用配 Qt 环境变量,也不用担心缺 DLL。下载时注意选带校验值的包,有些第三方站挂的旧版本安装包里面捆绑了奇怪的依赖,解压后杀毒软件会报警。如果你遇到这种情况,果断换一个来源。
三种形态的适用场景可以这样分:
| 形态 | 适用场景 | 优点 | 主要风险 |
|---|---|---|---|
| pip 安装 | 有 Python 环境、网络稳定 | 版本新、可升级 | PyQt5 依赖冲突、缺 DLL |
| 源码编译 | 要改源码、二次开发 | 完全可控 | 需要编译工具链、耗时 |
| 可运行版本 | 快速标注、给团队分发 | 开箱即用、零依赖 | 来源筛选成本、版本可能旧 |
如果你只是要把一批图片标注成数据集,可运行版本是效率最高的选择。如果是拿来做二次开发,比如给标注框加一个自动填充插件,那还是走源码编译的路线,可运行版本反而不好改。
2.2 首次启动检查三个配置点:目录、格式、类别表
拿到可运行版本后,解压到一个没有中文和空格的路径下,双击启动。第一次进入界面后不要急着标注,先检查三个关键配置点。
第一是“打开目录”和“保存目录”这一对按钮。LabelImg 的默认逻辑是,如果不手动设置保存目录,标注结果会存到图片所在目录。这在原图目录受管控或者只有只读权限的场景下,会直接保存失败。所以每次启动项目前,先点一次 Change Save Dir,选一个专门的输出目录。我习惯在数据集根目录下建一个labels文件夹,图片放在images文件夹,两个目录分开管理,避免标注文件混进原始图片里。
第二是工具栏上的 PascalVOC 按钮。这个按钮是格式切换的总开关,点击后会切换成 YOLO 字样。当前格式直接决定保存文件的扩展名:PascalVOC 输出 XML,YOLO 输出 TXT。如果你跑的是 YOLO 系列模型,一定在打开图片目录之前就把模式切到 YOLO,否则标注半天出来的全是 XML,还得再走一次转换脚本。切换后工具栏上的“PascalVOC”变成“YOLO”,同时画框界面左下角会显示当前模式,注意看一眼就不会搞错。
第三是predefined_classes.txt类别表文件。可运行版本压缩包里会带这个文件,里面每行一个类别名。画框时右键选择类别的下拉列表,就是从这里面读取的。按 Ctrl+U 可以重新加载自定义类别文件。我习惯把项目的所有类别一行一个写进去,包括背景类别,这样能避免每次手动输入类别名出现拼写错误。这个文件里不要留空行,空行会被工具当成一个空白类别写进标注数据,后期训练时会变成一个未知标签,非常恶心。
这里说一个启动层面的注意点:如果双击后闪退,或者弹窗提示缺少 Qt 平台插件,先别急着换版本。检查一下解压路径是不是纯英文,以及杀毒软件是不是把动态库文件隔离了。这两类问题占了启动失败案例的八成以上,具体处理放第 5 章排查部分展开。
整个配置动作控制在两分钟内。目录设好、格式切对、类别表加载完成,就可以正式进入标注流程了。
3. 标注实战:从画第一个框到拿到一份合法数据集
3.1 标准标注流程:五步走完一张图
打开图片目录后,界面左侧会列出目录下所有图片,中间是当前图片的显示区域,右侧是文件列表和标注框列表。标注一张图的标准流程分五步:选图、画框、命名、下一张、保存。刚上手时按这个顺序走,几百张图下来手速自然会上去。
第一步,在左侧列表里双击第一张图片,图片会加载到中央画布。第二步,点击左侧工具栏的 Create RectBox 按钮,或者直接用快捷键 W,鼠标在画布上拖出一个矩形框。拖框时可以松开鼠标再微调四角的控制点,不需要一次画准。第三步,松开鼠标后会弹出类别选择下拉框,从predefined_classes.txt里选一个类别,如果列表里没有,也可以直接在编辑框里输入新类别名回车确认。第四步,按 D 键切换到下一张图片,工具会自动保存当前标注。第五步,全部完成后按 Ctrl+S 手动保存一次,确认输出文件写到保存目录里。
这段流程里隐藏了一个关键参数:拖框的捕捉精度。LabelImg 的矩形框是按像素坐标对齐的,没有自动吸附边缘的功能。对于边界清晰的目标,尽量把框贴住目标的边缘,宁可多留 2 个像素,也不要少切目标区域。少切意味着训练时模型学到的是残缺特征,后期 mAP 会掉得莫名其妙。我一般在物体边缘放大到 200% 再微调框的四角,虽然慢一点,但数据质量能明显提升。
保存动作的逻辑要讲一下:按 D 切换下一张时,工具会触发自动保存,把当前图片的标注写入保存目录。但如果你没有设置过 Change Save Dir,它默认保存到图片所在目录。所以第 2 章说的保存目录配置一定要在启动时就做好,否则后期整理数据集时要到处找 XML 文件。
3.2 快捷键是效率分水岭:一张图几秒钟
标注效率主要差在快捷键使用上,鼠标点来点去和全键盘操作,速度能差三倍以上。LabelImg 的核心快捷键不多,但每一个都值得贴在显示器边上。下表是高频操作对应的默认快捷键。
| 快捷键 | 功能 | 备注 |
|---|---|---|
| W | 创建矩形框 | 画框后用 A/D 在上一张/下一张之间切换 |
| A / D | 上一张 / 下一张图片 | 切换时自动保存 |
| Ctrl+S | 保存当前标注 | 批量完成后手动保存一次 |
| Ctrl+R | 重置当前图片的标注框 | 删除全部框,适合标错重来 |
| Ctrl+Z | 撤销上一个框 | 误画框时用 |
| Del | 删除选中的框 | 先点击框选中再删除 |
| Ctrl+U | 加载自定义类别文件 | 启动时未加载时使用 |
使用节奏是这样的:看到图片后按 W 画框,右键或者弹窗里选类别,按 D 跳下一张,全程不需要碰鼠标来完成“点击按钮”的动作。连续标几百张图时,这套节奏能把疲劳感压到最低。
有一个细节容易被忽略:按 Del 删除框之前,需要先在画布上点击框选中它,选中的框会出现红色边框。直接按 Del 有时删除的是文件列表里的选区,而不是画布上的框。我的习惯是画框后马上在文件列表里确认一下类别名是否写对,一旦发现不对,点选框后按 Del 删除,重新画,不要用 Ctrl+Z 反复撤销,撤销过深会把前面几张图的标注状态也回滚掉。
3.3 保存文件长什么样:XML 和 TXT 的结构差异
不论使用哪种格式,保存出来的文件名与图片名保持同名,只是扩展名不同。图片是000001.jpg,标注文件就是000001.xml或者000001.txt。同一个图片目录下如果同时出现 XML 和 TXT,说明标注过程中切换过格式,这种情况在训练前一定要清理干净,否则数据加载脚本会不知道读哪个文件。
PascalVOC 格式的 XML 文件大概是这样的结构。标注工具生成的文件里会写清楚图像尺寸、通道数、标注框的坐标和类别名。这个文件的核心价值是坐标是绝对值,直接对应原图像素。
YOLO 格式的 TXT 文件则是每行一个目标,格式为:类别ID、中心点X、中心点Y、宽度、高度。注意五个值全部是归一化后的比例,数值范围在 0 到 1 之间。正因为这样,YOLO 格式的标签不能直接用于 VOC 接口的模型,转换时需要注意坐标换算。
这两种格式的切换逻辑,其实就是一个简单归一化与反归一化的过程,第 4 章会给出可以直接套用的脚本。这里想强调的是:保存文件本身不代表标注完成,打开 XML 或 TXT 检查一遍再继续下一批,这套习惯能省掉后期大量清洗数据的时间。
4. VOC 与 YOLO 格式切换:一个转换脚本和三个验证点
4.1 两种格式的本质差异:绝对坐标与归一化坐标
PascalVOC 和 YOLO 的标注格式代表了两种不同的数据组织思路。PascalVOC 以 XML 存储,记录的是矩形框左上角和右下角的绝对像素坐标。YOLO 格式以 TXT 存储,记录的是矩形框中心点以及宽高占比,全部数值归一化到 0 到 1 区间。
这个差异决定了使用场景:如果你是在跑某个带 VOC 接口的检测框架,直接喂 XML 就行;如果用的是 YOLO 系列的训练流程,数据加载脚本读的是 TXT。很多数据集下载下来是 VOC 格式,而你要用 YOLO 训练,就必须做一次转换。反过来也一样。
转换公式本身不难。VOC 里拿到左上角坐标和右下角坐标,宽度和高度的计算方式就出来了。中心点坐标等于左上角加右下角之和的一半,再除以图片宽高做归一化,框的宽高也做同样的归一化,最后全部约束在 0 到 1 之间。唯一需要注意的是,类别 ID 从哪里来。LabelImg 在 YOLO 模式下写 TXT 时,会按照类别表顺序分配 ID,从 0 开始。转换脚本里必须维护一个相同的类别 ID 映射表,否则转换后所有目标的类别就全乱了。
反方向转换,从 YOLO 转回 VOC,就是把归一化的中心点乘回原图尺寸,再加减宽高的一半算出左上角和右下角。这个过程中最容易出现越界,因为归一化值在四舍五入后可能让右下角超出图片宽度,需要做裁剪。
4.2 写一个可复用的转换脚本
这里给一个我自己常用的转换脚本,支持 VOC 转 YOLO,也支持反方向。代码注释里标明了每一步的作用,直接复制改一下路径就能跑。
import os import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, target_txt_path, class_map, img_width, img_height): """ 将PascalVOC格式的XML标注转换为YOLO格式的TXT标注 :param xml_path: 输入的XML文件路径 :param target_txt_path: 输出的TXT文件路径 :param class_map: 类别名到ID的映射字典 :param img_width: 图片像素宽度 :param img_height: 图片像素高度 """ tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall('object'): class_name = obj.findtext('name') if class_name not in class_map: raise ValueError(f"类别 {class_name} 不在映射表中,请检查类别文件") class_id = class_map[class_name] bndbox = obj.find('bndbox') xmin = float(bndbox.findtext('xmin')) ymin = float(bndbox.findtext('ymin')) xmax = float(bndbox.findtext('xmax')) ymax = float(bndbox.findtext('ymax')) # 计算中心点坐标和宽高,再归一化到[0,1]区间 center_x = (xmin + xmax) / 2.0 / img_width center_y = (ymin + ymax) / 2.0 / img_height box_width = (xmax - xmin) / img_width box_height = (ymax - ymin) / img_height # 强制裁剪到合法区间,防止越界 center_x = min(max(center_x, 0.0), 1.0) center_y = min(max(center_y, 0.0), 1.0) box_width = min(max(box_width, 0.0), 1.0) box_height = min(max(box_height, 0.0), 1.0) lines.append(f"{class_id} {center_x:.6f} {center_y:.6f} {box_width:.6f} {box_height:.6f}") # 一次性写入,避免反复打开文件造成性能浪费 with open(target_txt_path, 'w', encoding='utf-8') as f: f.write('\n'.join(lines))这段代码里有两个容易踩坑的地方。第一个是class_map字典必须和标注时的类别表顺序一致。比如 LabelImg 里类别名依次是 person、car、dog,那映射表就应该是{'person': 0, 'car': 1, 'dog': 2}。顺序一旦错位,训练时类别就全乱了。第二个是图片宽高参数,必须用图片的真实尺寸。如果你用 ImageNet 数据集直接下载的全分辨率图,但训练时做了缩放,那么转换脚本里的宽高也要跟着缩放,否则坐标会整体偏移。
上面脚本提供了转换核心逻辑,实际使用还需要一个外层遍历函数,遍历整个标注目录,逐文件调用转换,生成同名 TXT 文件,并跳过那些没有标注对象的空 XML。空 XML 在拼接字符串时不会有任何 content,TXT 文件会是一个空文件,这是合法状态,代表该图片没有目标。注意不要删除这类空 TXT,YOLO 训练流程会把它们当成负样本处理。
4.3 三个验证点:转换后必须检查坐标和类别
转换脚本跑完不能直接拿去训练,先做三件事验证转换结果。验证工具就是一个简单的可视化脚本:读取图片和对应的 YOLO 标注,把归一化坐标反算回像素坐标,用 OpenCV 在图片上画框,肉眼确认框的位置是否贴合目标。
第一个验证点是“框是否对齐目标”。随机抽出 20 张图,画框后逐张看。如果框整体偏移到左上方,说明中心点换算时用了错误的图片尺寸;如果框大小明显不对,说明宽高计算时可能把 xmax 减 xmin 的顺序搞反了。肉眼检查速度快,基本两分钟就能发现问题。
第二个验证点是“类别 ID 是否错位”。转换后打印每个 TXT 文件第一列的数值分布,和图片内容做人工核对。比如只标注了 person 和 car 两类,ID 0 和 ID 1 必须分别对应正确的类别。如果发现 ID 1 的框画在 person 上面,而映射表里 ID 1 却是 car,那就是类别表顺序和训练端不一致,需要统一两边的 class_names 文件。
第三个验证点是“坐标是否越界”。虽然脚本里做了裁剪,但裁剪本身可能掩盖源数据的问题。转换前你可以统计原始 XML 里是否有 xmax 超过图片宽度的记录。如果存在,说明标注时把框拉到了图像边缘之外,这种情况下建议返回数据标注环节修正,而不是靠脚本硬裁剪。硬裁剪的结果会导致模型在该区域学习到不完整的特征。
完成这三个验证后,转换好的标注文件可以放心进入训练流程。如果你在转换时使用了我上面这份脚本,建议把脚本保存到数据集目录下的tools文件夹里,下次换数据集直接复用,只需要改 class_map 和路径参数。
5. 常见问题排查:这四个坑我全部遇到过
5.1 启动闪退:Qt 平台插件缺失
现象:双击可执行文件后,界面一闪而过或者弹出 “Failed to load platform plugin xcb” 之类的报错。有时也会出现一个黑色的控制台窗口一闪即关。
原因:可运行版本里的 Qt 平台插件目录缺失,或者动态库依赖没有完整打包。解压时被杀毒软件隔离了一部分动态库,也会造成同样后果。Windows 上主要是platforms目录缺失,Ubuntu 上则是libqxcb.so找不到。
解决:这一步要查看解压目录下是否有platforms文件夹,没有的话把压缩包重新解压一遍,并临时关闭杀毒软件,排除误隔离。确认目录完整后,在环境变量里设置QT_QPA_PLATFORM_PLUGIN_PATH指向platforms所在路径;Windows 上还需要保证PyQt5\Qt\bin在系统 PATH 中。如果还闪退,直接换一个打包版本,有些精简版会刻意去掉不常用的平台插件以减小体积,换带完整依赖的版本即可。
5.2 标注保存失败或目录不可写
现象:标注完点保存,提示Permission denied,或者保存文件没有任何反应。检查保存目录时发现一个文件都没写进去。
原因:没有设置“保存目录”,默认写到图片目录下,而图片目录位于受管控的只读路径。另一个常见原因是图片路径或保存路径中包含中文、空格、特殊符号,工具内部的文件读写库对 Unicode 路径处理不完善。
解决:这里养成一个习惯,启动第一件事就是按 Change Save Dir 指向一个纯英文路径,比如D:\dataset\labels。注意路径中不要出现中文目录名,推荐用labels_train这样的命名。改完路径后随便标注一张,按 Ctrl+S 看能不能保存成功。如果还失败,看一下文件夹属性里的写权限,把数据集的读写权限放开给当前用户。某开发者在这个问题上折腾了一个下午,最后发现是公司电脑的文档目录被策略限制写入了。
5.3 自动保存模式没生效,标注数据丢失
现象:标注了一百张图,按 D 不断切换下一张,看起来一切正常。全部标完退出,再去保存目录里看,只有最后一张图的标注文件存在,前面的全部丢了。
原因:这是最坑的一个玄学问题。虽然 LabelImg 在切换图片时保存由用户控制,但实际行为取决于你是否点击过保存按钮。如果某个版本的自动保存机制异常,切换图片时并没有触发写入,整个过程只有第一次保存被记录。很多新手不知道这一点,一口气标了几百张,最后发现数据全丢。
解决:因此策略调整为每完成 20 张就按一次 Ctrl+S 保存,同时确认底部状态栏显示“Saved”字样。另外每张图画完后,也可以打开保存目录看是否有同名文件生成,一旦发现文件没出现,就不要继续往下标,先停下来定位保存问题。还有一个可靠办法:不要用默认的自动保存机制,标注完一张后主动 Ctrl+S 再按 D,虽然慢了一点,但数据安全比速度更重要。标注完一批后,在打开的文件目录里检查文件数量是否和图片数一致。
5.4 大图加载卡顿与滚动丢框
现象:遇到高分辨率图片,比如单张 8000×6000 像素的卫星图或扫描图,拖动画布时明显卡顿,滚动缩放到一定程度后标注框显示错位,或者框跑到图片外面不见了。
原因:LabelImg 的绘制线程基于 Qt,对超大图像的渲染没有做金字塔切片处理,所有像素一次性加载到内存里计算,卡顿是必然的。框错位多半是缩放比例过大时浮点精度丢失,或者图片尺寸和标注 XML 里记录的尺寸不一致,后者的可能性更高。
解决:在标注之前,先写个脚本把超大图片统一缩放到合适尺寸,比如最长边 2000 像素以内,同时把对应标注坐标做等比缩放。如果不想动原图,就在标注时把 XML 里的 imageWidth 和 imageHeight 值记清楚,确保和实际显示尺寸一致。标注卡的另一个办法是关闭工具的“自动加载下一张”功能,改用双击左侧文件名切换,减少内存占用。遇到滚动后框消失的情况,先缩小视图比例,框通常会恢复显示,这说明只是绘制缓存不稳定,重新渲染就能解决。
6. 一个自动化习惯:批量标注前跑数据清单检查
拿到一批新图片准备标注时,我习惯先写一个数据清单检查脚本,跑完再做人工标注。脚本的核心是统计图片基本信息:数量、尺寸分布、格式、是否存在损坏文件。这个不起眼的预处理动作,能在标注前排除掉大量会让工具崩溃或输出异常坐标的文件。
import os from PIL import Image from pathlib import Path def scan_images(image_dir, valid_ext=('.jpg', '.jpeg', '.png', '.bmp')): """ 扫描图像目录,输出基本信息和异常文件列表 :param image_dir: 图片目录路径 """ total = 0 damaged = [] size_distribution = {} for root, _, files in os.walk(image_dir): for name in files: ext = Path(name).suffix.lower() if ext not in valid_ext: continue path = os.path.join(root, name) total += 1 try: with Image.open(path) as img: # 验证文件完整性 img.verify() w, h = img.size bucket = f"{w}x{h}" size_distribution[bucket] = size_distribution.get(bucket, 0) + 1 except Exception as e: damaged.append((path, str(e))) print(f"图片总数: {total}") print(f"异常文件数: {len(damaged)}") print("尺寸分布:") for size, count in sorted(size_distribution.items()): print(f" {size}: {count} 张") if damaged: print("异常文件列表:") for path, error in damaged: print(f" {path} -> {error}")这个脚本跑完,返回的信息直接决定标注策略。如果尺寸一致性差,比如一部分是 1920×1080,一部分是 600×400,最好先统一缩放到一个固定尺寸再标注,否则转换 YOLO 格式时会因为 imageWidth 参数不一致而出错。如果存在损坏图片,直接把这些文件移出数据集,损坏图片会导致标注保存时写入失败,白白浪费时间。
标注完成后再跑一个统计脚本,检查生成的标注文件里每个类别的框数量分布,以及是否还有空标注的图片。类别分布严重不均匀的时候,比如 person 类别有 500 个框、dog 类别只有 3 个框,要先补采数据再训练,否则模型对 dog 的召回率会低得离谱。这两个自动检查步骤,帮我规避了多次“标注完才发现数据不合法”的返工。从那以后,我每次标注新数据集都强制把这两步走完再开始标,已经很久没有在数据环节翻过车了。希望帮到你。
本文还有配套的精品资源,点击获取