CCPD数据集转YOLO格式实战指南:从数据解析到格式转换

1次阅读
没有评论

共计 3733 个字符,预计需要花费 10 分钟才能阅读完成。

image.webp

背景介绍

CCPD(Chinese City Parking Dataset)是一个大型车牌检测数据集,包含超过 30 万张车牌图像,广泛应用于智能交通系统。数据集中的标注信息以文件名和文本文件存储,包含车牌的四角坐标、车牌号码等信息。而 YOLO 格式要求标注文件为 .txt 格式,每行包含类别索引和归一化后的边界框坐标(中心点 x,y 和宽高 w,h)。

CCPD 数据集转 YOLO 格式实战指南:从数据解析到格式转换

两者主要差异在于:
1. CCPD 使用四角坐标,YOLO 使用中心点 + 宽高
2. CCPD 坐标是绝对像素值,YOLO 需要归一化到 0 -1
3. CCPD 标注分散在文件名和文本中,YOLO 需要集中存储

技术方案

1. CCPD 数据解析

CCPD 数据集的标注信息隐藏在文件名中,格式如:025-95_113-154&383_386&473-386&473_177&454_154&383_363&402-0_0_22_27_27_33_16-37-15.jpg。我们需要解析:
– 车牌区域坐标:154&383_386&473表示左上和右下点
– 车牌四个角点:386&473_177&454_154&383_363&402
– 车牌号码:0_0_22_27_27_33_16(需要额外解码)

2. 坐标转换算法

从 CCPD 的四角坐标转换为 YOLO 格式需要以下步骤:

  1. 从文件名提取四个角点像素坐标
  2. 计算最小外接矩形(bounding box)
  3. 将矩形转换为 (center_x, center_y, width, height) 格式
  4. 归一化坐标(除以图像宽高)

3. 文件组织方式

转换后的文件结构应为:

dataset/
├── images/
│   ├── train/
│   └── val/
└── labels/
    ├── train/
    └── val/

代码实现

以下是完整的 Python 转换脚本,包含详细注释:

import os
import cv2
import glob
from pathlib import Path

def parse_ccpd_filename(filename):
    """解析 CCPD 文件名,提取坐标和车牌信息"""
    parts = Path(filename).stem.split('-')
    # 提取四个角点坐标
    corners = [tuple(map(int, p.split('&'))) for p in parts[2].split('_')]
    # 提取车牌区域坐标(左上和右下)x1, y1 = map(int, parts[1].split('&'))
    x2, y2 = map(int, parts[1].split('_')[1].split('&'))
    return {'corners': corners,  # 四个角点 [(x,y),...]
        'plate_area': (x1, y1, x2, y2),  # 车牌区域 (x1,y1,x2,y2)
        'plate_number': parts[3],  # 车牌号码编码
        'brightness': parts[4],  # 亮度信息
        'blur': parts[5]  # 模糊度信息
    }

def ccpt_to_yolo(ccpd_anno, img_w, img_h):
    """将 CCPD 标注转换为 YOLO 格式"""
    # 获取四个角点坐标
    corners = ccpd_anno['corners']

    # 计算最小外接矩形
    x_coords = [p[0] for p in corners]
    y_coords = [p[1] for p in corners]
    x_min, x_max = min(x_coords), max(x_coords)
    y_min, y_max = min(y_coords), max(y_coords)

    # 计算中心点和宽高
    box_w = x_max - x_min
    box_h = y_max - y_min
    center_x = x_min + box_w / 2
    center_y = y_min + box_h / 2

    # 归一化
    center_x /= img_w
    center_y /= img_h
    box_w /= img_w
    box_h /= img_h

    return [center_x, center_y, box_w, box_h]

def convert_ccpd_to_yolo(ccpd_dir, output_dir):
    """批量转换 CCPD 数据集到 YOLO 格式"""
    # 创建输出目录
    os.makedirs(os.path.join(output_dir, 'images/train'), exist_ok=True)
    os.makedirs(os.path.join(output_dir, 'labels/train'), exist_ok=True)

    # 遍历 CCPD 数据集
    for img_path in glob.glob(os.path.join(ccpd_dir, '*.jpg')):
        # 读取图像获取尺寸
        img = cv2.imread(img_path)
        img_h, img_w = img.shape[:2]

        # 解析文件名
        anno = parse_ccpd_filename(img_path)

        # 转换为 YOLO 格式
        yolo_box = ccpt_to_yolo(anno, img_w, img_h)

        # 准备输出路径
        stem = Path(img_path).stem
        new_img_path = os.path.join(output_dir, 'images/train', f'{stem}.jpg')
        label_path = os.path.join(output_dir, 'labels/train', f'{stem}.txt')

        # 保存图像和标注
        cv2.imwrite(new_img_path, img)
        with open(label_path, 'w') as f:
            # YOLO 格式:class_id center_x center_y width height
            f.write(f'0 {yolo_box[0]} {yolo_box[1]} {yolo_box[2]} {yolo_box[3]}\n')

if __name__ == '__main__':
    ccpd_dir = 'path/to/ccpd_dataset'  # 替换为你的 CCPD 数据集路径
    output_dir = 'yolo_dataset'
    convert_ccpd_to_yolo(ccpd_dir, output_dir)

验证方法

转换完成后,建议进行以下验证:

  1. 可视化检查:随机选择几张图像,用 OpenCV 绘制转换后的边界框,确认位置准确
import cv2
import random

# 随机选择一张图像
image_dir = 'yolo_dataset/images/train'
label_dir = 'yolo_dataset/labels/train'
img_files = os.listdir(image_dir)
sample = random.choice(img_files)

# 读取图像和标注
img = cv2.imread(os.path.join(image_dir, sample))
with open(os.path.join(label_dir, sample.replace('.jpg', '.txt'))) as f:
    line = f.readline().strip()
    class_id, x, y, w, h = map(float, line.split())

# 将归一化坐标转换回像素坐标
img_h, img_w = img.shape[:2]
x = int(x * img_w)
y = int(y * img_h)
w = int(w * img_w)
h = int(h * img_h)

# 绘制矩形
x1, y1 = int(x - w/2), int(y - h/2)
x2, y2 = int(x + w/2), int(y + h/2)
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)

# 显示图像
cv2.imshow('Verify', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
  1. 数据集统计:检查标注文件数量和图像数量是否匹配
  2. 坐标范围检查:确保所有坐标值都在 0 - 1 范围内

避坑指南

  1. 坐标归一化问题:
  2. 确保除以的是图像的宽度和高度,而不是固定的值
  3. 注意 OpenCV 的 shape 返回的是(高度, 宽度)

  4. 图像尺寸不一致:

  5. CCPD 数据集中的图像尺寸可能不同,必须对每张图像单独获取尺寸

  6. 特殊字符处理:

  7. CCPD 文件名包含多种特殊字符,使用 Path 模块处理更安全

  8. 性能优化:

  9. 大批量处理时,可以使用多进程加速
  10. 先解析所有文件名再批量处理,减少 IO 操作

性能优化建议

  1. 批量处理 :使用multiprocessing 模块并行处理
  2. 内存映射:对于非常大的数据集,考虑使用内存映射文件
  3. 增量处理:处理过程中定期保存进度,避免中途失败全部重来
  4. 缓存机制:已处理的文件跳过,只处理新增文件

扩展思考

完成基础转换后,可以考虑以下扩展功能:
1. 数据增强:在转换过程中加入随机裁剪、旋转等增强方法
2. 多任务学习:同时保留车牌检测和字符识别标注
3. 可视化工具:开发交互式工具检查标注质量
4. 数据集划分:自动按比例划分训练集、验证集和测试集

希望这篇指南能帮助你顺利完成 CCPD 到 YOLO 格式的转换。在实际应用中,你可能需要根据具体需求调整代码,比如处理不同类别的车牌或添加额外的预处理步骤。如果有任何问题或改进建议,欢迎交流讨论。

正文完
 0
评论(没有评论)