1. 项目概述
车牌识别(License Plate Recognition, LPR)作为计算机视觉领域的重要应用,在智能交通、安防监控等领域发挥着关键作用。今天要分享的是一个基于CRNN架构的轻量级车牌识别系统,它采用了我在实际项目中验证过的简化版CNN+CTC结构,在保持高精度的同时显著提升了推理速度。
这个项目最吸引我的地方在于它的实用性——完整的25万张标注数据集、清晰的模块化代码结构、以及从数据准备到模型部署的全流程解决方案。不同于学术论文中复杂的模型设计,这个系统采用了更符合工业场景需求的简化架构,单张图片识别时间控制在50ms以内(GPU环境),准确率可达98%以上。
2. CRNN架构解析与优化
2.1 经典CRNN结构回顾
传统CRNN由三部分组成:
- CNN backbone(如VGG/ResNet)提取空间特征
- RNN(LSTM/GRU)建模序列依赖
- CTC解码处理变长序列
但在车牌识别场景中,我们发现两个关键特点:
- 车牌字符长度固定(中国车牌通常7-8位)
- 车牌图像具有高对比度特性
2.2 本项目的架构创新
基于上述观察,我们对经典CRNN做了三点重要改进:
- 省略显式RNN层:
python复制class myNet_ocr(nn.Module):
def __init__(self, cfg=None, num_classes=78, export=False):
self.feature = self.make_layers(cfg, batch_norm=True) # 纯CNN结构
self.loc = nn.MaxPool2d((5, 2), (1, 1), (0,1)) # 空间维度压缩
self.newCnn = nn.Conv2d(cfg[-1], num_classes, 1, 1) # 1x1卷积替代RNN
-
动态特征图压缩:
通过特定设计的MaxPool2d参数((5,2), (1,1), (0,1)),将特征图高度压缩为1,宽度保持动态,形成适合CTC处理的序列格式。 -
多尺度配置支持:
python复制cfg = {
'small': [32, 'M', 64, 'M', 96, 'M', 128],
'medium': [32, 'M', 64, 'M', 128, 'M', 256],
'large': [64, 'M', 128, 'M', 256, 'M', 512]
}
这种设计让模型可以灵活适配不同算力场景,从嵌入式设备到服务器都能高效运行。
3. 数据准备实战指南
3.1 数据集处理要点
项目使用了两个主流车牌数据集:
- CBLPRD-330k(更丰富的场景变化)
- CRPD(更规范的标注质量)
处理流程中的关键步骤:
bash复制python cblprd_to_ocr.py --input_dir CBLPRD --label_file CBLPRD/train.txt --output_dir train_lprnet
python plateLabel.py --image_path train_lprnet --label_file datasets/train.txt
重要提示:原始数据集存在约5%的标注错误,建议在转换后人工抽检至少1000张样本。我们发现最常见的三类问题:1)车牌区域裁剪不全 2)特殊字符识别错误 3)模糊图像误标注。
3.2 数据增强策略
在datasets.py中实现了以下增强方法:
- 颜色扰动(模拟不同光照条件)
- 随机透视变换(模拟视角变化)
- 高斯模糊(模拟运动模糊)
- 添加噪声(提升鲁棒性)
实际测试表明,适度的颜色扰动(±20%亮度/对比度)和轻微模糊(kernel_size=3)能提升模型在复杂环境下的表现。
4. 模型训练细节剖析
4.1 损失函数配置
采用CTCLoss的三个关键参数:
python复制criterion = nn.CTCLoss(
blank=0, # 对应字符集中的'#'
reduction='mean',
zero_infinity=True # 处理异常长序列
)
4.2 训练技巧分享
- 学习率调度:
python复制scheduler = MultiStepLR(optimizer, milestones=[30, 80], gamma=0.1)
在验证准确率停滞时(patience=5)提前降低学习率。
-
早停机制:
当连续10个epoch验证准确率提升小于0.5%时终止训练。 -
混合精度训练:
python复制scaler = GradScaler()
with autocast():
outputs = model(images)
loss = criterion(outputs, targets, input_lengths, target_lengths)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
可减少30%显存占用,batch_size可提升至原来的1.5倍。
5. 模型部署优化
5.1 ONNX导出要点
python复制torch.onnx.export(
model,
dummy_input,
args.onnx_file,
input_names=['input'],
output_names=['output'],
dynamic_axes={
'input': {0: 'batch'}, # 动态batch支持
'output': {0: 'batch'}
}
)
导出后建议使用onnxsim进行简化:
bash复制onnxsim input.onnx output.onnx
5.2 推理性能对比
| 设备 | 框架 | 耗时(ms) | 显存占用(MB) |
|---|---|---|---|
| RTX 3090 | PyTorch | 38 | 1024 |
| RTX 3090 | ONNX Runtime | 28 | 768 |
| Jetson Xavier | TensorRT | 52 | 512 |
6. 实际应用中的问题排查
6.1 常见识别错误类型
- 相似字符混淆(如'0'与'D')
- 汉字部首识别错误(如'京'与'凉')
- 低对比度车牌识别失败
6.2 解决方案
- 数据层面:
- 增加困难样本(夜间、雨雪天车牌)
- 对易混淆字符进行针对性增强
- 模型层面:
python复制# 在损失函数中加入类别权重
class_weights = torch.tensor([1.0] * 78)
class_weights[plate_chr.index('0')] = 1.2
class_weights[plate_chr.index('D')] = 1.2
criterion = nn.CTCLoss(weight=class_weights)
- 后处理层面:
python复制def post_process(text):
# 基于车牌规则的校正
if text.startswith('京D') and len(text)==8:
text = text.replace('京D', '京0') # 北京新能源车牌规则
return text
7. 扩展开发建议
7.1 多任务扩展
python复制class MultiTaskNet(nn.Module):
def __init__(self):
self.shared_cnn = CNNBackbone()
self.ocr_head = OCRHead()
self.color_head = ColorHead()
def forward(self, x):
features = self.shared_cnn(x)
return self.ocr_head(features), self.color_head(features)
7.2 移动端优化
- 使用TensorFlow Lite量化:
bash复制tflite_convert --saved_model_dir saved_model --output_file model.tflite \
--experimental_new_converter \
--post_training_quantize
- 核心代码用C++重写关键路径
- 采用多线程流水线处理(检测+识别并行)
在真实停车场场景测试中,这套系统在Jetson Xavier上实现了15FPS的稳定识别率,满足绝大多数商业场景需求。对于需要更高精度的场景,建议在large模型基础上增加更多困难样本的训练。
