
简介LabelImg 是一款广泛用于目标检测数据集标注的可视化工具压缩包内提供完整源码面向需要制作 Faster R-CNN、YOLO、SSD 等模型训练样本的研究者与工程师。全包共 127 个文件以 44 个 Python 源码文件为核心配合 38 个 PNG 图标资源、6 个 Shell 脚本以及若干配置文件与说明文档整体仅 6.39MB便于离线获取和在本地搭建标注环境。工具生成的 XML 严格遵循 PASCAL VOC 格式可直接接入主流检测框架省去自行编写标注程序的成本。已有 357 人下载学习适合因网络原因无法访问 GitHub 的读者也可作为学习 PyQt 图形界面与标注逻辑的参考实现。其中包含 setup.cfg、egg-info 等打包配置以及示例图片和测试位图方便快速验证安装流程。1. labelImg-master.rar 是什么解压之前先搞清楚这包能干什么拿到一个名为labelImg-master.rar的压缩包十有八九是从同事或开源平台分享过来的图像标注工具源码。它的用途非常集中给图片里的目标画矩形框打上类别标签再把结果导出成带 XML 或 TXT 格式的数据集喂给目标检测训练脚本。做物体检测项目、YOLO 训练、数据预标注时这是绕不开的一环。很多人第一次碰这个包时会以为它是个能直接双击运行的软件结果解压后点labelImg.py屏幕上闪一下黑窗就没了然后开始怀疑是压缩包损坏。其实这包不是成品软件而是一套 Python 源码必须先装好依赖、用解释器启动才能看到真正的图形界面。反直觉的地方就在这里labelImg-master.rar能帮你省下大量人工标注时间但它的价值不是「解压即用」而是「源码可控」。你可以在它的基础上改类别文件、改快捷键、改成你自己团队的标注规范。这篇文章就是沿着一条我实际跑通的路径把这个包从解压到产出训练数据的过程完整过一遍。适合那些正在做目标检测数据准备的开发者、数据团队以及一个人负责算法加数据的开发者。下面的操作步骤不会只给命令还会把每一步的参数含义、常见坑和排错思路说清楚。2. 把 labelImg-master 跑起来环境、依赖与第一个窗口2.1 环境准备先确认 Python 和 Qt 版本再动手压缩包里的代码长得像老项目很多版本还在用 Python 2 和 Qt4 写界面直接拿新环境跑大概率报错。我一般拿到labelImg-master后第一步不是急着解压双击而是把包解开先看目录结构。一个典型的源码包里有libs/、data/、resources/根部放着labelImg.py、requirements.txt或者setup.py。requirements.txt直接告诉你要装什么如果你手里的包没有就用最稳妥的组合Python 3.8 PyQt5 lxml。用 conda 建独立环境是最省心的做法万一后面把环境搞坏了删掉重来也就一分钟不用祸害系统 Python。在命令行里依次执行conda create -n labelimg python3.8 -y conda activate labelimg pip install pyqt5 lxml这里python3.8是我实测多轮后觉得最稳的版本。PyQt5 对 Python 3.10 以上有些兼容问题而 3.6 以下又可能缺类型语法支持。lxml是保存和解析 Pascal VOC 格式 XML 文件的底层库不装的话画完框点保存会直接报ModuleNotFoundError: No module named lxml。这一步的逻辑很直白PyQt5 负责窗口和绘图lxml 负责把标注数据序列化成 XML两者缺一不可。如果你不想用 conda直接用系统 pip 也可以但要留意是否会被系统权限挡住。我遇到过一个很典型的场景某公司在共享服务器上装 Python 环境没有 sudo 权限只能用pip install --user这时候依赖会装到当前用户目录启动时如果环境变量没对还是会提示找不到模块。所以环境装完之后先执行python -c import PyQt5, lxml; print(ok)验证一下再走后面的启动步骤。2.2 安装与启动从源码跑而不是双击依赖装好之后进入正题。在解压出来的目录里打开命令行Windows 下是 cmd 或 PowerShellLinux/macOS 就是终端。推荐从源码运行而不是用一个叫labelImg的 pip 包。源码跑的好处是你可以随时改界面语言、改预设类别、修 bug还能看到日志输出。启动命令分两步走cd labelImg-master pyrcc5 -o libs/resources.py resources.qrc python labelImg.py第二条命令pyrcc5是 PyQt5 自带的资源编译器作用是把resources.qrc里引用的图标、样式资源全部编译成libs/resources.py。如果不执行这一步很多从 GitHub 拉下来的代码会缺少编译后的资源文件界面能启动但工具栏图标变成空白框有些版本甚至会报ModuleNotFoundError: No module named libs.resources。这一步是源码用户最容易翻车的地方因为压缩包里可能没带resources.py或者带的是旧版本编译产物。你可能会问为什么命令里没有pip install -r requirements.txt因为不是所有labelImg-master包都带这个文件。有的 fork 版本把依赖直接写进了说明文档有的则依赖了libs目录下的模块不再额外需要安装。我更推荐的启动方式是在python labelImg.py后面直接加参数比如打开指定图片目录python labelImg.py ./images ./data/predefined_classes.txt注意这种用法的前提是程序支持命令行参数。主界面菜单里也有Open Dir选项但命令行指定能省去每个标注员手动点路径的时间。第一个参数是图片文件夹第二个参数是类别文件路径。如果你把类别文件放在别处后续保存的 XML 里类别名就是从那个文件读出来的。2.3 验证安装第一个窗口和三个可点的菜单启动成功后会出现一个灰色主窗口左侧竖排着一列工具按钮。验证环境是否真正可用我习惯做一轮「空转测试」从菜单栏 File 里选Open Dir打开你的测试图片目录然后按快捷键W进入画框模式在图片上拉一个矩形再按CtrlS保存。保存后到图片目录下看是否多了一个同名 XML 文件。这一步能同时验证三件事PyQt5 能不能正常渲染、lxml 能不能写 XML、程序有没有权限写文件。如果画框时鼠标没有出现矩形或者保存按钮是灰色大概率不是环境问题而是图片格式太特殊。常见做法是把图片先统一转成 JPG 或 PNG尤其是原始数据集里带 EXIF 旋转信息的照片标注软件读出来的方向可能和显示不一致最后标注框位置是错的。这个问题我在实际项目中踩过很深的坑后面避坑章会展开讲。窗口里还有两个值得点开的地方菜单栏的Edit和View。Edit下可以设置标签颜色、是否启用自动保存View下可以切换显示标签、显示框的大小。这些设置直接影响标注速度和导出格式。自动保存建议从一开始就打开避免标了一百张图最后一不小心按错键丢了数据。验证完成后我会把那个测试 XML 删掉然后从头开始正式标注。3. 标注流程与核心设置从打开图片到导出 XML3.1 标注操作一个矩形框背后的效率逻辑打开图片目录后主界面会列出所有图片。单击一张图片它会显示在画布中央。按W进入画框模式鼠标从目标左上角拖到右下角松开后就是预测框。框画完会自动弹出一个Choose Label Class对话框让你选择类别名。选完之后框会变成绿色左侧列表里显示当前图片所有框的类别和坐标。需要修正时按Ctrl鼠标点击选中框或者直接按Del删除。这里有一个很多人忽略的性能细节在labelImg-master源码中画框和平移图片用的是同一个事件循环。当图片分辨率超过 4000 像素时画完一个框后如果立刻去拖拽图片界面会明显卡顿。常见做法是把大图先用 OpenCV 压缩到 2000 像素左右再标注但要注意标注出来的坐标是压缩后图片的坐标后期训练时要么把图片同步缩到压缩后大小要么在转换脚本里做坐标换算。我一般会在标注前把训练图片统一规范化避免后面返工。import cv2 image cv2.imread(raw_photo.jpg) scaled cv2.resize(image, (2000, int(image.shape[0] * 2000 / image.shape[1])), interpolationcv2.INTER_AREA) cv2.imwrite(scaled_photo.jpg, scaled)看不出来这段代码有什么特别的它把宽超过 2000 的图片等比缩放INTER_AREA是缩小图像时比较稳的插值方式可以减少锯齿和细节丢失。参数说明2000是目标宽度可以根据自己的模型输入尺寸调整interpolation选用INTER_AREA能比默认的INTER_LINEAR在缩小场景下保留更多结构信息。转换完之后标注用的目录里放的是缩放后图片XML 里的坐标对应的也就是缩放后的图像训练数据预处理时不要反过来用原图。标注过程中有一个容易被当玄学处理的问题类别名一旦在对话框里输入错了改起来非常麻烦。所以我的习惯是打开程序前先写好predefined_classes.txt每行一个类别名不要带空格不要带中文。程序在读这个文件后画框弹出的对话框会直接从文件里读取候选类别不需要打字标注速度能提升不少。如果再配合自动保存一张图从画框到保存大概能控制在 10 秒以内。3.2 核心设置三个必调的参数打开Edit菜单下的Auto Save Mode这个选项决定按CtrlS后控件是否自动跳到下一张图片。默认关闭你按一次保存只保存当前图还得手动点下一张。我建议打开这样保存后自动切到下一张图连续标注时手腕不用来回挪。第二个必调设置在View菜单下Auto Label Mode。打开之后每次画完框不会再弹出对话框问你类别而是直接沿用上一次选择的类别。这个模式适合批量标注同一类目标的场景比如一个视频抽帧里全是划痕。第三项是Display Labels打开后 XML 保存的框上会显示类别名如果不打开你看到的就是蓝色纯框导出后才知道有没有标错。这三个设置改一次就会写入程序配置下次启动默认沿用。配置文件的存放位置因系统而异Windows 是用户目录下的.labelImgSettings.pklLinux 是~/.labelImgSettings.pkl。如果你改了配置后启动报错把这个文件删掉再启动就能恢复到默认状态这是最有效的后悔药。核心参数调整完了再看保存格式。labelImg-master默认保存为 Pascal VOC 格式的 XML文件内容大概是这样的annotation folderimages/folder filename000001.jpg/filename pathC:/Users/xxx/Desktop/images/000001.jpg/path source databaseUnknown/database /source size width1920/width height1080/height depth3/depth /size segmented0/segmented object namedog/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin100/xmin ymin120/ymin xmax200/xmax ymax240/ymax /bndbox /object /annotation这段 XML 里filename和path是两个容易出问题的字段。path保存的是绝对路径一旦你的图片目录被移动、重命名下次再打开这个 XML软件就找不到原始图片框和标签对不上。我在实际项目中经常见到标注完的数据集发给训练环境训练脚本先读 XML 再找图片发现 path 指向前一台机器的路径结果全部报错。所以导出数据集前建议用脚本把所有 XML 里的 path 字段批量改成本地相对路径或者干脆删掉path节点让训练脚本只依赖folder和filename。size记录了图片宽高和通道数转换 YOLO 格式时要靠它做归一化。object里一个 target 节点描述一个检测目标bndbox的四个值必须是整数。这里有个坑手动拖动框时如果图片缩放显示鼠标松开的位置会被程序乘以缩放比例所以 XML 里存的是原始图片像素坐标而不是你在屏幕上看到的框的坐标。因此无论画框时显示多么小XML 坐标都是图片真实尺寸下的这点可以放心。3.3 保存格式VOC 和 YOLO 之间怎么选Pascal VOC 是人类可读格式缺点是文件大、解析慢。YOLO 格式每个图对应一个同名 TXT每行class_id x_center y_center width height坐标都是相对于原图宽高的归一化小数。训练 YOLO 系列模型时官方要求用 TXT 格式训练 Faster R-CNN 或 SSD 时很多框架直接吃 VOC XML。所以标注阶段统一用 VOC XML 保存训练之前再转换是一种通用套路。如果你的项目明确只做 YOLO也可以在保存前进入File Save As手动选择 YOLO 格式。但我不建议这么做因为labelImg-master的 YOLO 保存功能在部分 fork 版本里不稳定可能出现保存了 TXT 但没有同步保存类别顺序表的情况。类别顺序表一旦丢失所有 TXT 里的 class_id 就全乱了。更稳妥的流程始终是全程用 VOC XML 标注最后用脚本统一转成 YOLO TXT并且把类别表和转换脚本一起归档。下一章就讲这个转换脚本和四个边界坑。4. 把标注结果喂给训练脚本VOC 转 YOLO 格式与路径坑4.1 转换脚本一个顺手但必须手写的脚本训练脚本要求的数据格式通常和标注格式不一致。我见过最稳的做法是专门写一个voc2yolo.py放在数据集准备阶段的独立目录里。它读入每一个 XML提取object里的坐标再除以size里的宽高输出成归一化的小数。这个操作本身不复杂但边界条件处理不好的话转换出来的 TXT 会坑到训练环节。import os import glob import xml.etree.ElementTree as ET classes [dog, cat, car] # 必须与 predefined_classes.txt 顺序一致 def convert(xml_file, output_dir): tree ET.parse(xml_file) root tree.getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) basename os.path.splitext(xml_file)[0] with open(os.path.join(output_dir, basename .txt), w) as f: for obj in root.iter(object): class_name obj.find(name).text if class_name not in classes: continue class_id classes.index(class_name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) center_x ((xmin xmax) / 2) / width center_y ((ymin ymax) / 2) / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height f.write(f{class_id} {center_x:.6f} {center_y:.6f} {box_w:.6f} {box_h:.6f}\n) xml_files glob.glob(annotations/*.xml) os.makedirs(labels, exist_okTrue) for xml_file in xml_files: convert(xml_file, labels)这个脚本的逻辑是先用ET.parse解析 XML拿到图片宽高然后遍历所有object节点把bndbox的左上角和右下角转成中心的坐标接着除以宽高得到归一化结果最后写入目标 TXT。注意classes列表的排序必须和训练时用的类别列表一致否则模型推理时类别名就错位了。参数说明里最值得一提的是glob.glob(annotations/*.xml)的路径匹配。它会一次性找全所有 XML所以脚本所在目录的结构必须是annotations/存 XML、images/存原图。如果你把 XML 和图片混在一起就得先整理目录。TXT 输出到labels/和图片目录平行。这样 YOLO 工具链在读取images/和labels/时才能对齐同名文件。4.2 四个边界坑坐标越界、空标签、错别字、重复类别这个脚本看着简单跑起来大概率会遇到四类问题。第一类是坐标越界。手动标注时鼠标稍微拉出图片边界bndbox的 xmax 就可能大于 width转换结果变成超过 1 的归一化值某些训练框架会直接报错。解决办法是在脚本里加一句xmax min(xmax, width)xmin max(xmin, 0)对 y 轴同样处理。第二类是空标签。有些 XML 文件里object节点不存在或者某个目标没有 name。脚本里class_name not in classes会跳过它但如果你发现输出的 TXT 是空文件最好回原图检查一下是不是有目标没标。数据集里空标签文件太多训练时会浪费大量时间因为它们有图无 target模型不知道该往哪里学。第三类是错别字和大小写差异。Dog和dog在 Python 里是两个完全不同的字符串。标注员手滑把car打成car尾随空格转换脚本不报错但classes.index会因为找不到而抛异常。我一般会把脚本开头改成classes [c.strip().lower() for c in classes]同时对 XML 里的class_name也做strip().lower()从源头去掉这类问题。第四类坑是重复类别名。predefined_classes.txt里如果写了dog两遍classes.index只会返回第一个位置但YOLO训练脚本读取类别文件时可能又会把两个dog都算进类别数导致类别数量不一致。这个比较隐蔽因为在标注阶段不会报错直到训练开始后才频繁出现维度不匹配的错误。我现在的习惯是每次标注前用 Python 读一遍类别文件检查是否有重复项再开始干活。转换完成之后最后一步是划分训练集、验证集和测试集。常见做法是用脚本把图片文件名按比例随机分成三个清单生成train.txt、val.txt、test.txt。这三个文件每行是图片的绝对路径或相对路径。注意有的框架读的是images/train整个目录有的框架则读train.txt里的路径列表。你需要根据训练工具来确定。我的做法是同时输出目录结构和 txt 清单哪个框架都能直接用。5. labelImg 避坑指南5 个让标注中断的真实问题5.1 双击 labelImg.py 黑窗一闪而过现象解压后直接双击labelImg.py窗口出现不到一秒就消失没有任何错误提示。新手很容易以为压缩包坏了。原因.py文件的默认打开方式是 Python 解释器但如果系统没有把.py关联到 Python或者解释器运行时缺依赖错误信息会直接打印在控制台双开方式根本看不到。解决不要双击而是打开命令行先cd到解压目录再执行python labelImg.py这样所有报错会在终端保留。如果能看到ModuleNotFoundError就照着缺少的模块名pip install补齐如果没有任何输出但窗口还是没弹出来检查系统 Python 是否安装成功执行python --version。5.2 标注一半CtrlS 保存不了也不报错现象按保存键有反应但去图片目录里找不到对应的 XML 文件。原因很多情况下程序启动时没有设置默认保存目录启动参数里第二个参数传的类别文件路径在部分 fork 版本中被误当作保存目录。另外如果开启了自动保存模式程序保存后自动切图你看到的「无反应」其实是保存到了别的位置。解决先检查启动命令后面是否多传了路径。正确做法是只传一个图片目录和一个类别文件不要画蛇添足。然后在程序里点File Save As手动指定一个输出目录看是否生成 XML。如果手动保存成功说明自动保存的目录路径有问题把Auto Save Mode关掉再试。5.3 第二次打开软件之前的标注框全部不见了现象标注完一批图关掉软件隔天再打开同一个图片目录图像上没有任何框。原因labelImg-master在打开图片目录时只会自动加载与图片同名且位于同目录下的 XML 文件。如果你的 XML 保存在另一个文件夹或者你刚用Open Dir而不是Open File打开单张图片程序不会主动去关联。解决先确认 XML 文件和图片文件确实同名且同目录。然后在菜单里用Open选择一张图片再点Open XML手动加载对应的 XML。如果多个 XML 都丢失了最直接的办法是检查你的保存路径是否被改到了别处或者程序有没有按你的设置自动保存。养成每次标注完把images和annotations两个文件夹一起拷走的好习惯能少很多这种问题。5.4 图片打开后画框但保存的框位置明显偏移现象屏幕上框住了一只猫保存后训练检查时发现框偏到了身体外。原因图片里有 EXIF 旋转信息。手机或相机直出的 JPG 常带这个标签软件读取时按原始像素排列显示但很多库默认会应用旋转方向后再显示两者不一致导致标出来的坐标错位。解决标注前用批量处理把所有图片先旋转成固定方向并把 EXIF 信息移除。常见做法是from PIL import Image import os for file in os.listdir(raw): if file.lower().endswith((.jpg, .jpeg)): img Image.open(os.path.join(raw, file)) fixed ImageOps.exif_transpose(img) fixed.save(os.path.join(clean, file))这段代码会把图片按 EXIF 方向摆正然后用新的图像数据覆盖保存不再带旋转属性。逻辑说明ImageOps.exif_transpose是 Pillow 库提供的函数它读取 EXIF 里的方向字段返回旋转后的新图像保存到新目录后标注软件看到的画面和最终图像像素一致框的位置就不会再偏移。参数说明raw和clean两个目录名随意改但建议全程只用clean目录做标注。5.5 用了中文类别名保存的 XML 乱码现象类别输入为「狗」XML 打开后变成乱码或者训练脚本读出来是空串。原因labelImg-master底层对编码的处理并不统一部分版本写 XML 时不会强制编码为utf-8而 Windows 下默认可能是gbk。解决如果你一定要用中文标签可以在保存后手动用 Python 脚本把 XML 编码统一转成 UTF-8。但更推荐的做法是使用英文类别名比如用dog代替「狗」因为训练框架的类别映射文件通常按 ASCII 处理中文容易在数据加载环节出现各种意外。这算是我这几年标注数据集最深的一条血泪经验。6. 进阶用快捷键与批量改名把标注效率再提一档标注工作最磨人的不是画框而是重复劳动。等你把一组数据标完回头审视时会发现时间都浪费在「点击右键菜单选类别」「在长列表里找图片」「手工改文件名」上。前面的章节把环境、流程和坑都理清了这一章再往前走一步讲两个我一直在用的提升效率的方法。第一个方法是自定义快捷键。labelImg-master代码里已经内置了一批默认快捷键W开始画框、D切换到下一张图、A切换到上一张图、Del删除当前框、CtrlS保存。这些都是源码里写死的如果你想改成更顺手的按键直接改libs/canvas.py中对应的QKeySequence映射即可。比如把「删除当前框」从Del改成E让右手不用离开鼠标找到Qt.Key_Delete那一行换成Qt.Key_E。改完重启软件新快捷键立即生效。这个操作不依赖任何外部库是纯源码级定制也是为什么我一直推荐用labelImg-master源码而不是打包版的原因。第二个方法是批量改名和校验。标注过程中经常会有图片重命名但 XML 里的 filename 字段并不会自动改。我习惯在标注全部结束后跑一个同步脚本遍历每个 XML提取其中的 filename改成和对应images/目录里实际文件名一致。脚本的思路很简单根据 XML 文件路径反推出图片旧路径再用os.path.exists判断新路径是否存在不存在就把文件名替换成目录下的实际文件名。这一步能彻底规避「XML 对不上图」的问题。如果你的项目里老师或客户习惯用编号图片还可以顺手把文件名统一修改。这里不写具体代码了因为不同项目的命名规则不一样但核心逻辑就是文件名XML 名。第三个方法是用小批候选集做抽样验证。标注完一批数据后我习惯按类别人工过一遍共 30 张随机图重点看两类问题漏标的负样本以及坐标差太多的正样本。labelImg-master可以用View Zoom放大检查单张图但一个个翻太慢。我会写一个 10 行脚本随机抽样 30 个 XML用cv2.rectangle把框画回图像上再导出成一张缩略图拼图人眼一眼就能扫完全部验证结果。验证完再交付训练风险会小很多。整个流程走到这里你手里的labelImg-master就已经不是一个陌生的压缩包了而是一条可以复用的数据生产线。从环境安装、跑通界面、标注设置到格式转换、踩坑修复再到最后的验证和快捷键微调每一步都会花一点手续费但后面的训练阶段会一直受益。我记得第一次批量标注时因为没有提前清理 EXIF 信息最终返工了 400 张图从那以后就养成了标注前先用脚本清洗全图目录的习惯。希望这些经验能帮到你少走一些我走过的弯路。本文还有配套的精品资源点击获取
拿不准这条消息跟你有没有关系?
工种不同、批次不同,要求可能差很多。打电话把你的情况说清楚,我们按信阳、平顶山本地的口径给你捋一遍。