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

两者主要差异在于:
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 格式需要以下步骤:
- 从文件名提取四个角点像素坐标
- 计算最小外接矩形(bounding box)
- 将矩形转换为 (center_x, center_y, width, height) 格式
- 归一化坐标(除以图像宽高)
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)
验证方法
转换完成后,建议进行以下验证:
- 可视化检查:随机选择几张图像,用 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()
- 数据集统计:检查标注文件数量和图像数量是否匹配
- 坐标范围检查:确保所有坐标值都在 0 - 1 范围内
避坑指南
- 坐标归一化问题:
- 确保除以的是图像的宽度和高度,而不是固定的值
-
注意 OpenCV 的
shape返回的是(高度, 宽度) -
图像尺寸不一致:
-
CCPD 数据集中的图像尺寸可能不同,必须对每张图像单独获取尺寸
-
特殊字符处理:
-
CCPD 文件名包含多种特殊字符,使用
Path模块处理更安全 -
性能优化:
- 大批量处理时,可以使用多进程加速
- 先解析所有文件名再批量处理,减少 IO 操作
性能优化建议
- 批量处理 :使用
multiprocessing模块并行处理 - 内存映射:对于非常大的数据集,考虑使用内存映射文件
- 增量处理:处理过程中定期保存进度,避免中途失败全部重来
- 缓存机制:已处理的文件跳过,只处理新增文件
扩展思考
完成基础转换后,可以考虑以下扩展功能:
1. 数据增强:在转换过程中加入随机裁剪、旋转等增强方法
2. 多任务学习:同时保留车牌检测和字符识别标注
3. 可视化工具:开发交互式工具检查标注质量
4. 数据集划分:自动按比例划分训练集、验证集和测试集
希望这篇指南能帮助你顺利完成 CCPD 到 YOLO 格式的转换。在实际应用中,你可能需要根据具体需求调整代码,比如处理不同类别的车牌或添加额外的预处理步骤。如果有任何问题或改进建议,欢迎交流讨论。
