共计 3540 个字符,预计需要花费 9 分钟才能阅读完成。
背景介绍
CCPD(Chinese City Parking Dataset)是目前最全面的中文车牌识别数据集之一,包含超过 30 万张真实场景下的车牌图像。数据集特点包括:

- 覆盖不同天气条件(晴天、雨天、雾天等)
- 多种角度和光照变化
- 包含新能源车牌和普通蓝牌
- 每张图片附带精确的边界框(bounding box)和车牌号标注
新手常见问题:
- 标注信息分散在文件名和额外 txt 文件中
- 图像尺寸不统一(从几十 KB 到几 MB 不等)
- 特殊字符(如新能源车牌字母 ’D’)需要特殊处理
数据准备
- 下载数据集:
- 官方地址:https://github.com/detectRecog/CCPD
-
建议下载 CCPD2019 基础版(约 12GB)
-
目录结构:
CCPD2019/ ├── ccpd_base/ # 基础数据集 │ ├── 01_92_182_26_92_182_26_182.jpg │ └── ... ├── ccpd_blur/ # 模糊车牌 ├── ccpd_db/ # 低亮度车牌 └── ccpd_fn/ # 夜间车牌 -
文件名解析:
示例文件名01_92_182_26_92_182_26_182.jpg包含: - 车牌号:苏 A92E26
- 边界框坐标:(182,92)到(182,26)
预处理流程
完整 Python 处理脚本:
import cv2
import os
from pathlib import Path
# 解析文件名获取标注信息
def parse_filename(filename):
parts = filename.split('_')
# 车牌号转换(示例:01-> 苏, 92->A)province_code = parts[0]
plate_number = parts[1] + parts[2] + parts[3]
# 边界框坐标
left, top = int(parts[4]), int(parts[5])
right, bottom = int(parts[6]), int(parts[7])
return {'plate': f'{province_dict[province_code]}{plate_number}',
'bbox': [left, top, right, bottom]
}
# 转换所有数据
def convert_dataset(input_dir, output_dir):
Path(output_dir).mkdir(exist_ok=True)
for img_file in Path(input_dir).glob('*.jpg'):
img = cv2.imread(str(img_file))
info = parse_filename(img_file.stem)
# 保存为 YOLO 格式
with open(f'{output_dir}/{img_file.stem}.txt', 'w') as f:
x_center = (info['bbox'][0] + info['bbox'][2]) / 2 / img.shape[1]
y_center = (info['bbox'][1] + info['bbox'][3]) / 2 / img.shape[0]
width = abs(info['bbox'][2] - info['bbox'][0]) / img.shape[1]
height = abs(info['bbox'][3] - info['bbox'][1]) / img.shape[0]
f.write(f"0 {x_center} {y_center} {width} {height}")
# 保存调整后的图像
cv2.imwrite(f'{output_dir}/{img_file.name}', img)
数据增强
针对车牌识别的特殊增强方法:
-
透视变换(模拟不同拍摄角度):
def perspective_transform(img): h, w = img.shape[:2] pts1 = np.float32([[0,0], [w,0], [0,h], [w,h]]) pts2 = np.float32([[0,0], [w,0], [random.randint(0,50),h], [random.randint(w-50,w),h]]) M = cv2.getPerspectiveTransform(pts1, pts2) return cv2.warpPerspective(img, M, (w,h)) -
颜色扰动(应对不同光照条件):
def color_jitter(img): img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img[:,:,1] = img[:,:,1] * random.uniform(0.7, 1.3) # 饱和度变化 img[:,:,2] = img[:,:,2] * random.uniform(0.7, 1.3) # 明度变化 return cv2.cvtColor(img, cv2.COLOR_HSV2BGR) -
模糊处理(模拟运动模糊):
def motion_blur(img): kernel_size = random.randint(3, 10) kernel = np.zeros((kernel_size, kernel_size)) kernel[:, kernel_size//2] = 1/kernel_size return cv2.filter2D(img, -1, kernel)
模型训练
PyTorch 基础训练示例:
import torch
import torchvision
# 自定义数据集类
class LicensePlateDataset(torch.utils.data.Dataset):
def __init__(self, img_dir, transform=None):
self.img_dir = Path(img_dir)
self.img_files = list(self.img_dir.glob('*.jpg'))
self.transform = transform
def __len__(self):
return len(self.img_files)
def __getitem__(self, idx):
img_path = self.img_files[idx]
img = cv2.imread(str(img_path))
# 读取对应标注文件
with open(str(img_path).replace('.jpg', '.txt')) as f:
label = f.read().strip()
if self.transform:
img = self.transform(img)
return img, label
# 定义简单 CNN 模型
class PlateRecognitionModel(torch.nn.Module):
def __init__(self, num_classes=68): # 31 个省份 +10 数字 +24 字母 + 新能源
super().__init__()
self.backbone = torchvision.models.resnet18(pretrained=True)
self.fc = torch.nn.Linear(512, num_classes)
def forward(self, x):
features = self.backbone(x)
return self.fc(features)
# 训练循环
def train(model, dataloader, criterion, optimizer, epochs=10):
for epoch in range(epochs):
for imgs, labels in dataloader:
optimizer.zero_grad()
outputs = model(imgs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
避坑指南
- 字符编码问题:
- 新能源车牌包含特殊字母(如 ’D’)
-
解决方案:建立完整字符映射表(31 省份简称 +24 字母 +10 数字)
-
标注格式混淆:
- CCPD 同时使用文件名和 txt 存储标注
-
建议:预处理时统一转换为 YOLO 或 COCO 格式
-
图像尺寸差异大:
- 直接 resize 会导致小车牌模糊
- 解决方案:先检测车牌区域再 resize
性能优化
- 模型层面:
- 使用 CRNN(CNN+RNN)处理序列识别
-
添加注意力机制提升小目标检测
-
数据层面:
- 针对模糊 / 低照度样本过采样
-
使用 GAN 生成极端天气样本
-
后处理:
- 基于车牌规则校验(如省份简称校验)
- 时序融合(对视频流连续帧结果投票)
实践建议
- 从小数据集开始(如 CCPD_base 的 10% 样本)验证流程
- 使用 wandb 或 tensorboard 监控训练过程
- 尝试在自有数据上微调模型
希望这篇指南能帮助你快速上手 CCPD 数据集!如果在复现过程中遇到问题,建议先检查数据预处理是否正确,这是大多数错误的根源。
正文完
发表至: 未分类
近两天内
