1. 项目概述

在计算机视觉项目中,数据标注是模型训练的基础环节。使用labelimg工具标注YOLO格式数据集,并实现自动化数据集划分,能够显著提升目标检测项目的开发效率。本文将详细介绍从数据标注到数据集划分的完整工作流,包含工具使用技巧、格式转换原理和自动化脚本实现。

2. 环境准备与工具安装

2.1 LabelImg安装配置

LabelImg是开源的图像标注工具,支持多种标注格式输出。推荐使用Python虚拟环境安装:

conda create -n labelenv python=3.8
conda activate labelenv
pip install labelimg

安装完成后,通过命令行启动:

labelimg

注意:如果遇到PyQt5相关报错,需单独安装:

pip install pyqt5

2.2 辅助工具准备

建议同步安装以下工具:

  • OpenCV:用于图像预览和格式检查
  • tqdm:进度显示
  • pandas:数据统计
pip install opencv-python tqdm pandas

3. 标注工作流程详解

3.1 标注规范制定

开始标注前需明确:

  1. 类别定义:确定需要检测的物体类别清单
  2. 标注粒度:标注到物体整体还是部件级
  3. 遮挡处理:对部分遮挡物体的标注规则
  4. 标签命名:采用英文小写+下划线格式

建议创建 label_map.txt 文件记录类别信息:

person
car
traffic_light

3.2 LabelImg实操技巧

  1. 快捷键使用:

    • W :创建矩形框
    • Ctrl+S :保存当前标注
    • D :下一张图像
    • A :上一张图像
  2. 标注质量把控:

    • 边界框应紧密贴合物体边缘
    • 对模糊/小目标需放大确认
    • 定期使用 View->Auto Save mode 开启自动保存
  3. 文件组织建议:

dataset/
    ├── images/       # 原始图像
    ├── labels/       # 标注文件
    └── classes.txt   # 类别列表

4. YOLO格式解析

4.1 标注文件格式

YOLO格式的标注文件为 .txt 文本,每行表示一个物体:

<class_id> <x_center> <y_center> <width> <height>

其中坐标值为归一化后的相对值(0-1之间)。

4.2 坐标转换原理

从LabelImg的Pascal VOC格式(绝对坐标)到YOLO格式的转换公式:

def voc_to_yolo(x1, y1, x2, y2, img_w, img_h):
    x_center = ((x1 + x2) / 2) / img_w
    y_center = ((y1 + y2) / 2) / img_h
    width = (x2 - x1) / img_w
    height = (y2 - y1) / img_h
    return x_center, y_center, width, height

5. 自动化数据集划分

5.1 划分策略设计

典型的数据集划分比例:

  • 训练集:70%
  • 验证集:15%
  • 测试集:15%

需要考虑:

  • 类别分布均衡
  • 不同场景的均匀分布
  • 时序数据的连续性处理

5.2 Python实现代码

import os
import random
from tqdm import tqdm

def split_dataset(image_dir, output_dir, ratios=(0.7, 0.15, 0.15)):
    # 获取所有图像文件
    images = [f for f in os.listdir(image_dir) if f.endswith(('.jpg', '.png'))]
    random.shuffle(images)
    
    # 计算划分点
    total = len(images)
    train_end = int(total * ratios[0])
    val_end = train_end + int(total * ratios[1])
    
    # 创建输出目录
    os.makedirs(os.path.join(output_dir, 'train'), exist_ok=True)
    os.makedirs(os.path.join(output_dir, 'val'), exist_ok=True)
    os.makedirs(os.path.join(output_dir, 'test'), exist_ok=True)
    
    # 复制文件
    for i, img in enumerate(tqdm(images)):
        if i < train_end:
            dest = 'train'
        elif i < val_end:
            dest = 'val'
        else:
            dest = 'test'
        
        # 复制图像和对应标注
        img_path = os.path.join(image_dir, img)
        label_path = os.path.join(image_dir, img.replace('.jpg', '.txt'))
        
        os.system(f'cp {img_path} {output_dir}/{dest}/')
        os.system(f'cp {label_path} {output_dir}/{dest}/')

6. 质量检查与验证

6.1 标注可视化检查

使用OpenCV绘制标注框验证:

import cv2
import random

def visualize_annotation(img_path, txt_path):
    img = cv2.imread(img_path)
    h, w = img.shape[:2]
    
    with open(txt_path) as f:
        for line in f:
            class_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)
            
            color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
            cv2.rectangle(img, (x1,y1), (x2,y2), color, 2)
    
    cv2.imshow('Annotation', img)
    cv2.waitKey(0)

6.2 数据集统计分析

检查每个子集的类别分布:

import pandas as pd
from collections import defaultdict

def analyze_distribution(data_dir):
    stats = defaultdict(lambda: defaultdict(int))
    
    for split in ['train', 'val', 'test']:
        label_dir = os.path.join(data_dir, split)
        for txt_file in os.listdir(label_dir):
            if txt_file.endswith('.txt'):
                with open(os.path.join(label_dir, txt_file)) as f:
                    for line in f:
                        class_id = int(line.strip().split()[0])
                        stats[split][class_id] += 1
    
    return pd.DataFrame(stats).fillna(0)

7. 常见问题与解决方案

7.1 标注文件问题

问题1 :YOLO格式坐标值超出[0,1]范围

  • 原因:标注时边界框超出图像范围
  • 解决:在LabelImg中调整边界框或后期处理时进行裁剪

问题2 :图像与标注文件不匹配

  • 原因:文件名修改后未同步更新
  • 解决:使用校验脚本检查对应关系:
import glob

def check_pair(img_dir, label_dir):
    img_files = set([f.split('.')[0] for f in os.listdir(img_dir)])
    label_files = set([f.split('.')[0] for f in os.listdir(label_dir)])
    
    missing_img = label_files - img_files
    missing_label = img_files - label_files
    
    return missing_img, missing_label

7.2 数据集划分问题

问题3 :类别分布不均衡

  • 解决方案:采用分层抽样
from sklearn.model_selection import train_test_split

def stratified_split(df, test_size=0.2):
    train_df, val_df = train_test_split(
        df, 
        test_size=test_size, 
        stratify=df['class_id']
    )
    return train_df, val_df

问题4 :图像尺寸不一致

  • 解决方案:统一resize或修改模型配置
def resize_images(input_dir, output_dir, target_size=(640,640)):
    os.makedirs(output_dir, exist_ok=True)
    for img_file in os.listdir(input_dir):
        img = cv2.imread(os.path.join(input_dir, img_file))
        img = cv2.resize(img, target_size)
        cv2.imwrite(os.path.join(output_dir, img_file), img)

8. 进阶技巧与优化

8.1 半自动标注加速

使用预训练模型生成初始标注:

  1. 用YOLOv8预测结果导出为Pascal VOC格式
  2. 在LabelImg中加载预标注结果进行修正
  3. 导出最终YOLO格式标注

8.2 数据集增强

在划分前进行数据增强:

import albumentations as A

transform = A.Compose([
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
    A.Rotate(limit=15, p=0.3)
])

def augment_image(img, bboxes):
    transformed = transform(image=img, bboxes=bboxes)
    return transformed['image'], transformed['bboxes']

8.3 自动化流水线示例

完整自动化脚本框架:

class AnnotationPipeline:
    def __init__(self, config):
        self.config = config
        
    def run(self):
        self.prepare_dirs()
        self.generate_labels()  # 调用labelimg批量处理
        self.convert_format()   # VOC转YOLO
        self.split_dataset()    # 划分数据集
        self.verify_quality()   # 质量检查
        self.generate_report()  # 统计报告
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐