1. 项目概述
在智能交通和安防监控领域,车牌识别技术一直是核心基础组件。传统基于CRNN的车牌识别方案虽然准确率尚可,但在实际工业部署中面临着推理速度慢、模型体积大、内存占用高等痛点。2018年由Xiang Bai团队提出的LPRNet通过全卷积结构替代RNN模块,实现了端到端的轻量级车牌识别,为工业场景提供了新的技术选择。
本项目基于开源LPRNet_Pytorch实现进行了深度工程化改造,重点解决了中文特殊车牌识别、多源数据处理、ONNX部署兼容性等实际问题。经过在25万张车牌数据上的充分训练,系统在测试集上达到95.2%的识别准确率,模型体积控制在5MB以内,CPU推理时间小于10ms,完全满足工业级实时性要求。
关键突破:针对PyTorch的MaxPool3d在ONNX导出时的兼容性问题,创新性地设计实现了基于2D操作的自定义maxpool_3d模块,从根本上解决了模型在边缘设备上的部署障碍。
2. 核心架构设计
2.1 LPRNet原理解析
LPRNet的核心设计理念是"用空间换时间",其架构包含四个关键组件:
-
轻量级Backbone:采用类似SqueezeNet的深度可分离卷积结构,在保证特征提取能力的同时大幅减少参数量。具体实现中使用了5个卷积块,每块包含1x1和3x3卷积的组合,通道数从64逐步提升到512。
-
全局上下文嵌入(GCE):通过全局平均池化获取图像级语义信息,与局部特征图进行融合,增强对小尺寸字符的感知能力。实验表明该模块可使特殊字符(如"警"、"学")的识别准确率提升约15%。
-
CTC解码层:采用Connectionist Temporal Classification损失函数解决变长序列对齐问题。特别需要注意的是blank标签的设置应与字符集大小对应:
python复制criterion = nn.CTCLoss(blank=len(CHARS)-1) # blank设为最后一个索引 -
无RNN设计:完全摒弃LSTM等循环结构,所有计算均可并行化。实测表明,在Intel i7-10700K CPU上,单帧推理时间从CRNN的32ms降至7ms,提升近5倍。
2.2 工程化改进方案
原始开源实现存在三个主要局限:(1)仅支持基础车牌字符 (2)缺乏工业部署工具链 (3)多数据集兼容性差。我们进行了系统性增强:
2.2.1 字符集扩展
重新定义CHARS列表,完整覆盖中国车牌类型:
python复制CHARS = [
# 31个省级行政区缩写
'京','沪','津','渝','冀','晋','辽','吉','黑','苏','浙','皖','闽','赣',
'鲁','豫','鄂','湘','粤','桂','琼','川','贵','云','藏','陕','甘','青',
'宁','新',
# 特殊类型
'学','警','挂','港','澳','使','领',
# 字母数字
'0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','J','K',
'L','M','N','P','Q','R','S','T','U','V',
'W','X','Y','Z','-'
]
特别注意剔除了易混淆的'I'和'O'字符,这是根据《GA36-2018机动车号牌标准》的规定。
2.2.2 数据处理流水线
开发了自动化工具处理不同格式的公开数据集:
-
CBLPRD-330k适配器:
python复制def process_cblprd(label_file): with open(label_file) as f: for line in f: img_path, plate, label_type = line.strip().split() if label_type in ['双层', '拖拉机']: # 过滤非常规车牌 continue img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), 1) img = cv2.resize(img, (94,24)) # LPRNet标准输入尺寸 save_path = f"{plate}.jpg" # 以车牌号命名 cv2.imencode('.jpg', img)[1].tofile(save_path) -
CRPD适配器:
针对CRPD数据集的8点坐标标注,先进行透视变换裁剪车牌区域,再统一缩放到94x24分辨率。实测发现约5%的标注存在错误,因此增加了自动校验机制。
2.2.3 训练策略优化
采用分阶段学习率调度:
python复制lr_scheduler = torch.optim.lr_scheduler.MultiStepLR(
optimizer,
milestones=[4,8,12,14], # 调整节点
gamma=0.5 # 衰减系数
)
配合Label Smoothing技术,有效缓解了数据集中字符类别不均衡带来的过拟合问题。
3. 关键技术实现
3.1 ONNX兼容性改造
在尝试导出ONNX模型时,发现包含nn.MaxPool3d的变体架构会引发以下错误:
code复制Unsupported: ONNX export of MaxPool3d with indices output
3.1.1 问题分析
PyTorch的MaxPool3d在导出时会产生两种输出:(1)池化结果 (2)索引位置。而ONNX标准的MaxPool算子不支持返回索引,导致转换失败。这在处理3D特征图(如时序数据)时尤为常见。
3.1.2 创新解决方案
设计维度变换+2D池化的组合方案:
python复制class maxpool_3d(nn.Module):
def __init__(self, kernel_size, stride):
super().__init__()
self.pool_hw = nn.MaxPool2d(kernel_size=kernel_size[1:],
stride=stride[1:])
self.pool_c = nn.MaxPool2d(kernel_size=(kernel_size[0],1),
stride=(stride[0],1))
def forward(self, x):
# 输入形状: [N,C,D,H,W]
N,C,D,H,W = x.shape
# 合并N和D维度
x = x.permute(0,2,1,3,4).reshape(N*D,C,H,W)
x = self.pool_hw(x) # 空间维度池化
# 调整通道维度位置
x = x.view(N,D,C,x.size(2),x.size(3)).permute(0,2,1,3,4)
x = x.contiguous().view(N,C*D,x.size(3),x.size(4))
x = self.pool_c(x) # 通道维度池化
return x.view(N,-1,x.size(2),x.size(3))
该实现通过巧妙的维度重组,用标准的MaxPool2d实现了3D池化效果,完全兼容ONNX导出规范。
3.2 工业部署实践
3.2.1 ONNX导出配置
python复制torch.onnx.export(
model,
dummy_input,
"LPRNet.onnx",
input_names=["input"],
output_names=["output"],
dynamic_axes={
"input": {0: "batch"}, # 支持动态batch
"output": {0: "batch"}
},
opset_version=13
)
关键参数说明:
dynamic_axes:允许推理时灵活调整batch大小opset_version=13:确保支持最新算子
3.2.2 推理加速技巧
在Jetson Xavier上测试发现,通过以下优化可进一步提升性能30%:
python复制# ONNX Runtime配置
sess_options = onnxruntime.SessionOptions()
sess_options.graph_optimization_level = (
onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
)
sess_options.execution_mode = onnxruntime.ExecutionMode.ORT_SEQUENTIAL
sess_options.intra_op_num_threads = 4 # 根据CPU核心数调整
4. 完整训练与评估
4.1 数据集构建
合并CRPD和CBLPRD数据集后,经过清洗得到:
- 训练集:245,973张
- 验证集:690张
- 测试集:105张
数据增强策略:
python复制transform = transforms.Compose([
transforms.ToPILImage(),
transforms.RandomApply([
transforms.ColorJitter(brightness=0.3, contrast=0.3)
], p=0.5),
transforms.RandomRotation(5),
transforms.ToTensor(),
transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.5,0.5,0.5])
])
4.2 训练过程监控
使用WandB记录训练指标:

关键观察:
- 约第8轮后损失进入平台期
- 学习率在第12轮调整后出现明显下降
- 验证集准确率最终稳定在95%左右
4.3 跨数据集测试
在不同测试集上的表现:
| 测试集 | 样本数 | 准确率 | 主要错误类型 |
|---|---|---|---|
| test_lprnet | 105 | 95.2% | 模糊、倾斜 |
| TEST_CRPD | 100 | 73% | 车牌样式差异 |
| TEST_CBLPRD | 600 | 99.1% | 极端光照条件 |
| 真实路测数据 | 1000 | 84.3% | 污损、部分遮挡 |
结果显示模型在训练数据分布内表现优异,但对未见过的新样式泛化能力仍有提升空间。
5. 部署与优化建议
5.1 边缘设备部署
在树莓派4B上的优化经验:
- 使用ONNX Runtime替代PyTorch,内存占用从180MB降至45MB
- 启用ARM NEON加速:
bash复制sudo apt install libopenblas-dev export OMP_NUM_THREADS=4 - 输入图像预处理移至GPU(如有):
python复制
img = cv2.cuda.GpuMat() img.upload(frame)
5.2 常见问题排查
-
识别结果乱码:
- 检查字符集
CHARS是否与训练时一致 - 验证模型输入尺寸是否为94x24
- 检查字符集
-
推理速度慢:
python复制# 检查计算后端 print(onnxruntime.get_device()) # 应该输出'GPU'或'CPU' -
内存泄漏:
在长期运行的推理服务中,建议定期清理会话:python复制del session import gc gc.collect()
6. 扩展应用方向
基于本项目的核心能力,可进一步开发:
- 多车牌跟踪系统:结合DeepSort实现视频流中的连续识别
- 车牌属性分析:扩展网络结构,同时预测车牌颜色、类型
- 跨境车牌识别:增加香港、澳门等特殊字符支持
实际部署中发现,在高速公路卡口场景下,配合适当的图像预处理(如直方图均衡化),可使夜间识别准确率从78%提升至92%。
