3. LabelImg实战:从安装到验证的完整标注流程

标注工具那么多,为什么我偏偏推荐 LabelImg?

说白了,它够轻量、够直接。你不需要配置复杂的环境,也不用学一堆新概念。打开软件,画框,命名,保存——就这么简单。我最早做目标检测项目时,用的就是它。那时候数据量不大,几百张图,LabelImg 帮我扛过了最基础的标注阶段。

好,咱们直接进入正题。

3.1 LabelImg 安装与配置

安装方式有两种,我分别说说。

3.1.1 方式一:pip 安装(推荐)

这是我最常用的方式。干净、快速、好卸载。

pip install labelImg
labelImg

嗯,就两行命令。如果你用的是 Python 3.6 以上版本,基本不会出问题。我在 Windows 和 Ubuntu 上都试过,一路绿灯。

我的习惯: 建议在虚拟环境里装。这样不会污染全局 Python 包,项目之间也互不干扰。

3.1.2 方式二:源码运行

如果你需要二次开发,或者想看看它底层怎么实现的,可以走这条路。

git clone https://github.com/tzutalin/labelImg.git
cd labelImg
pip install -r requirements/requirements-linux-python3.txt
python labelImg.py

注意,不同系统要选不同的 requirements 文件。Windows 用 requirements-windows-python3.txt,macOS 同理。

我曾经踩过的坑: 有次在 Ubuntu 20.04 上直接运行 python labelImg.py,结果报了个 Qt 相关的错。后来发现是 PyQt5 没装全。解决方案很简单:pip install pyqt5 单独装一下就好。

3.1.3 启动后的界面认识

启动后你会看到这样一个界面:左侧是工具栏,中间是图像显示区,右侧是标注列表。别被它简陋的外表骗了——该有的功能一个不少。

LabelImg 界面布局 工具栏 打开/保存 画框工具 删除框 缩放/平移 上一张/下一张 图像显示区 (标注框会显示在这里) person 标注列表 person (1) car (2) dog (1) 三大区域:左侧工具栏 → 中间图像区 → 右侧标注列表

3.2 图像标注流程

安装好了,咱们开始干活。标注这件事,看着简单,但细节决定成败。

3.2.1 准备工作:数据组织

我个人习惯把图片按类别分文件夹放好。比如:

datasets/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   ├── img_002.jpg
│   │   └── ...
│   └── val/
│       ├── img_100.jpg
│       └── ...
└── labels/   # 标注文件会自动生成在这里
    ├── train/
    └── val/

为什么要这样分?因为 YOLO 训练时需要区分训练集和验证集。你想想看,如果所有图片混在一起,后面还得再写脚本去分,多麻烦。一步到位,省心。

3.2.2 开始标注:画框与命名

打开 LabelImg,按以下步骤操作:

  1. 设置保存格式: 点击 View → Auto Save mode,勾上。再点击 PascalVOC 切换为 YOLO 格式。
  2. 打开图片目录: 点击左侧 Open Dir,选择你的图片文件夹。
  3. 设置保存目录: 点击 Change Save Dir,选择 labels 文件夹。
  4. 画框: 点击左侧 Create RectBox 按钮(或按快捷键 W),在目标物体上拖拽画框。
  5. 命名: 松开鼠标后,弹出对话框输入类别名称。比如 person、car。
  6. 保存: 按 Ctrl+S 或点击 Save。
  7. 下一张: 按 D 键切换到下一张图片。
快捷键速查表:
操作快捷键
画框W
删除选中框Delete
上一张A
下一张D
保存Ctrl+S
缩放滚轮
平移按住 Ctrl + 拖拽

3.2.3 标注时的几个关键原则

我在项目中遇到过不少因为标注不规范导致模型训练效果差的情况。这里总结几条铁律:

  • 框要贴边: 标注框必须紧贴目标物体的边缘,不要留白太多,也不要切掉物体的一部分。
  • 遮挡处理: 如果物体被遮挡,只标注可见部分。不要脑补被挡住的部分。
  • 类别一致: 同一个类别,命名必须完全一致。大小写、空格都不能错。person 和 Person 会被当成两个不同的类。
  • 小目标标注: 对于很小的物体(比如远处的行人),也要标注。YOLO 对小目标的检测能力,很大程度上取决于你有没有标注它。
我曾经犯过的错: 有次标注了 2000 张图,结果发现类别名写错了——car 写成了 car (多了一个空格)。训练时模型把 car 当成了一个独立类别,导致检测结果全乱套。后来我写了个脚本批量替换才救回来。所以,命名时一定要仔细。

3.3 标注文件检查与验证

标注完了,别急着训练。先检查一下标注文件的质量。这一步很多人会跳过,但我建议你千万别省。

3.3.1 YOLO 标注文件格式

LabelImg 保存的 YOLO 格式标注文件是 .txt 文件,每张图片对应一个同名文件。格式如下:

class_id x_center y_center width height

注意,这里的坐标都是归一化后的值(0~1 之间)。举个例子:

0 0.5 0.5 0.3 0.4
1 0.2 0.3 0.1 0.15

这表示:第一行是类别 0 的物体,中心点在图片的 (0.5, 0.5) 位置,宽高占图片的 30% 和 40%。第二行是类别 1 的物体。

3.3.2 检查清单

我每次标注完都会做这几步检查:

检查项检查方法常见问题
文件完整性确保每张图片都有对应的 .txt 文件漏标、跳标
坐标合法性所有坐标值在 0~1 之间负数或大于 1 的异常值
类别 ID 范围类别 ID 从 0 开始,连续递增跳号、重复号
框大小合理性width 和 height 不能为 0误操作产生的零宽零高框
图片与标注匹配图片文件名和标注文件名一致后缀名不匹配、命名错误

3.3.3 用脚本批量验证

手动检查几百个文件太累了。我写了个小脚本,一键验证:

import os

def check_labels(label_dir, num_classes):
    errors = []
    for fname in os.listdir(label_dir):
        if not fname.endswith('.txt'):
            continue
        path = os.path.join(label_dir, fname)
        with open(path, 'r') as f:
            lines = f.readlines()
        for i, line in enumerate(lines):
            parts = line.strip().split()
            if len(parts) != 5:
                errors.append(f"{fname}: 第{i+1}行格式错误")
                continue
            cls_id = int(parts[0])
            if cls_id < 0 or cls_id >= num_classes:
                errors.append(f"{fname}: 类别ID {cls_id} 超出范围")
            coords = [float(x) for x in parts[1:]]
            if any(c < 0 or c > 1 for c in coords):
                errors.append(f"{fname}: 坐标值异常 {coords}")
    if errors:
        for e in errors:
            print(e)
    else:
        print("所有标注文件检查通过!")

# 使用示例
check_labels("datasets/labels/train", num_classes=2)
我的建议: 把这个脚本保存为 check_labels.py,每次标注完跑一遍。花 10 秒钟检查,能省下后面几小时的调试时间。

3.3.4 可视化验证

除了脚本检查,我还会用可视化方式看一眼。LabelImg 本身就支持:重新打开一张图片,看看框的位置对不对。或者用 OpenCV 画框显示:

import cv2
import numpy as np

def draw_yolo_boxes(image_path, label_path, class_names):
    img = cv2.imread(image_path)
    h, w = img.shape[:2]
    with open(label_path, 'r') as f:
        for line in f.readlines():
            cls_id, xc, yc, bw, bh = map(float, line.strip().split())
            x1 = int((xc - bw/2) * w)
            y1 = int((yc - bh/2) * h)
            x2 = int((xc + bw/2) * w)
            y2 = int((yc + bh/2) * h)
            cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
            cv2.putText(img, class_names[int(cls_id)], (x1, y1-5),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
    cv2.imshow('Check', img)
    cv2.waitKey(0)

# 使用示例
draw_yolo_boxes("datasets/images/train/img_001.jpg",
                "datasets/labels/train/img_001.txt",
                ["person", "car"])

看到框和物体匹配上了,心里才踏实。

3.4 本章小结

标注这件事,说白了就是「细心活」。工具本身不复杂,复杂的是你怎么保证每一张图、每一个框都准确无误。我见过太多人因为标注质量差,导致模型训练效果不好,最后反过来怀疑算法有问题。其实问题往往出在数据上。

记住三个要点:

  • 安装用 pip,省事
  • 标注要规范,框贴边、名一致
  • 检查不能省,脚本+可视化双重验证