news 2026/8/27 17:23:38

YOLOv5车牌检测与LPRNet识别实战:基于CCPD数据集实现完整系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5车牌检测与LPRNet识别实战:基于CCPD数据集实现完整系统

简介:在智能交通与停车管理场景中,车牌识别系统通常由目标检测与字符识别两部分组成。目标检测技术用于在复杂背景中定位车牌区域,而序列识别算法则负责将车牌图像转换为字符文本。YOLOv5作为单阶段检测器的代表,具备高速度与多尺度检测能力;LPRNet则是一种轻量级端到端识别网络,通过CTC损失函数实现无需字符分割的序列预测。两者结合可构建高性价比车牌识别流水线。CCPD数据集作为真实停车场场景的大规模数据源,其独特的文件名编码方式为训练提供了丰富的标注信息。本文围绕CCPD数据处理、YOLOv5检测模型训练、LPRNet识别模型调优及系统集成的完整链路,分享实际工程中的踩坑经验与优化思路,助力开发者快速搭建可落地的车牌识别方案。 车牌识别这个方向,我前前后后折腾了大概两个月,从最开始只会调YOLOv5现成权重,到后面把LPRNet整个训练推理链路跑通,再到把检测和识别串成一条完整流水线,中间踩的坑比想象中多得多。如果你正在研究YOLOv5车牌检测和LPRNet车牌识别,并且打算用CCPD数据集来搞一套自己的方案,那这篇东西应该能帮你省下不少时间。我会尽量把整个项目的思路、关键细节、实操步骤和排坑经验一次讲清楚,不讲废话,专注让你能把项目真正跑起来。

1. 项目整体设计与思路拆解

1.1 车牌识别系统要解决的核心问题

车牌识别本质上是两个任务的串联:先得在图像里找到车牌在哪里,再把找到的车牌区域里的字符读出来。听起来简单,实际做起来牵扯的问题不少。首先是检测环节,车牌在画面里可能有大小差异、角度倾斜、光照不均、遮挡模糊等各种情况,检测模型必须在这种复杂背景下稳定地框出车牌位置。其次是识别环节,拿到检测框之后,里面的字符排列并不一定是水平的,有可能是倾斜的、模糊的,甚至带着铆钉和边框干扰,识别模型要从这些干扰里把汉字、字母、数字逐个认出来。

这套组合里,YOLOv5负责检测,LPRNet负责识别,两者的分工非常明确。YOLOv5是典型的单阶段目标检测器,速度和精度平衡得比较好,在车牌这种相对规则的检测目标上表现很稳。LPRNet则是一个不需要字符级分割的轻量级识别网络,它直接对整张车牌图像做序列识别,通过CTC损失来对齐字符序列,这样省去了传统方案里先切字符再逐个识别的麻烦,也让整个流程简洁了不少。

1.2 为什么选YOLOv5+LPRNet这套组合

说句实话,车牌识别不是没有更省事的方案,比如直接用HyperLPR或者PaddleOCR里现成的车牌识别模型,但自己搭一套YOLOv5+LPRNet的组合有一个明显的好处:可定制性极强。你可以随时替换训练数据,调整检测策略,甚至把LPRNet的backbone换掉来适配边缘设备。对于实际工程项目来说,这种可控感很重要。

YOLOv5在检测端的优势不用多说,生态成熟、部署方案多、训练资料全。它针对小目标和密集场景的优化比较到位,车牌在监控画面里往往只占很小一块区域,YOLOv5的多尺度检测能力可以覆盖从近到远的各种车牌尺寸。LPRNet这边的选型理由也很实在,它的参数量只有不到两百万,在CPU上也能跑到几十毫秒级别的推理速度,这在很多需要部署到低成本设备的场景里特别吃香。同时LPRNet用的是全局特征序列识别,不需要精确的字符切分,对车牌字符粘连、边框干扰的容错能力更强。

1.3 整体流程架构

我的整体架构分四步走:第一步把CCPD数据集里的图片和标注解析出来;第二步训练YOLOv5检测模型,得到能框出车牌区域的权重;第三步训练LPRNet识别模型,把裁剪出来的车牌图像转成字符串;第四步把检测和识别串成一个完整的推理流水线,输入一张大图,直接输出车牌号。

考虑到CCPD数据集的标注格式比较特殊,它把车牌的四个角点坐标直接编码在文件名里,所以第一步的数据解析是整条链路里最容易出问题的地方。后面我会专门花一节讲这块,因为数据没处理好,后面训练什么都是白搭。

2. 环境准备与数据集深度解析

2.1 环境配置要点

先明确一下我的环境版本,帮助你做个参考。我用的是Ubuntu 20.04,Python 3.8,PyTorch 1.10.0,CUDA 11.3,显卡是RTX 3060 12G。YOLOv5用的官方6.0版本,LPRNet用的开源PyTorch实现。

装环境这件事看似常规,但有三个细节值得注意。第一,PyTorch和CUDA的版本一定要匹配,建议直接去PyTorch官网用对应命令安装,不要自己猜版本组合。第二,如果训练数据量比较大,建议把torch、torchvision、opencv-python这些包的版本锁定,避免后面莫名奇妙的兼容性问题。第三,LPRNet那个开源实现里用到的warp_ctc_pytorch在有些环境里编译比较麻烦,我试下来直接用原生PyTorch的CTCLoss替代完全没问题,这也省去了自己编译扩展库的麻烦。

提示:训练LPRNet前可以先把项目里的依赖装齐,我用的是requirements.txt一次性解决,避免反复装包浪费时间。

2.2 CCPD数据集结构拆解

CCPD数据集全称是Chinese City Parking Dataset,是车牌识别领域用得比较多的数据集,总共大概30万张图片,全部来自真实停车场场景。它的标注方式非常有意思,所有关键信息都直接编码在文件名里,不需要额外的JSON或XML标注文件。

具体看一个文件名例子:

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

这个文件名里,每个字段用横杠分隔,分别代表:

字段含义
025车牌省份缩写对应的编号
95_113车牌区域的大致中心坐标和宽高信息
154&383_386&473车牌左上角坐标(154,383)和右下角坐标(386,473)
386&473_177&454_154&383_363&402车牌的四个角点坐标(顺序依次是右下、左下、左上、右上)
0_0_22_27_27_33_16亮度、模糊度等属性信息
37车牌类型对应的编号
15车牌颜色类型

这里面最关键的字段就是四个角点坐标。为什么需要角点坐标而不是直接给一个矩形框?因为CCPD里的车牌很多是倾斜的,简单的水平矩形框没办法准确框住倾斜车牌,用角点坐标方便后面做透视变换校正。

2.3 数据预处理与标注格式转换

YOLOv5训练需要的是归一化的中心坐标加宽高格式标注,也就是每张图片对应一个txt文件,每行是class_id x_center y_center width height。CCPD给的是角点坐标,所以我们要自己转。我的转换逻辑很简单,取四个角点中x和y的最小值和最大值,构成一个包含车牌的外接矩形,然后归一化写入txt。这种做法虽然会引入一点背景噪声,因为外接矩形比实际车牌区域略大,但实际操作下来对检测精度影响很小。

CCPD官方没有给训练集和验证集的划分文件,我是自己按比例划分的。我选了大概10万张图片做训练,1万张做验证,随机打乱后按文件名索引存成train.txt和val.txt。注意划分的时候要保证车牌省份的分布尽量均匀,不然某个省份的字符可能训练不到。

LPRNet那边不需要标注文件,它直接从图片文件名里读取车牌字符串。CCPD文件名里第一个字段代表省份缩写,但完整的车牌字符序列其实要看文件名里的一个映射关系。实际用起来比较麻烦,因为CCPD的命名规则没法直接反推出车牌号码。我的处理方式是把检测框裁出来之后手动标注了大概5万张车牌图片,按省份、字母、数字的标准格式做标签,这在后面LPRNet训练里直接用。

注意:CCPD文件名里的第一个字段是省份缩写编号,需要自己维护一个字典映射,比如0对应皖、1对应沪、2对应津、3对应渝、4对应冀等,具体映射表在CCPD相关论文和开源代码里都能找到。

3. YOLOv5车牌检测模块实现

3.1 YOLOv5模型结构与检测原理

YOLOv5的模型结构由Backbone、Neck和Head三部分组成。Backbone使用CSPDarknet,负责提取图像特征;Neck使用PANet结构,把不同层级的特征图融合起来,这样既能检测大目标也能检测小目标;Head负责输出最终的边界框和类别概率。

车牌检测和通用目标检测有个不太一样的地方,车牌的宽高比非常固定,大概在3:1到4:1左右,而且字符集中在一个矩形区域内。这意味着YOLOv5的anchor尺寸其实可以针对车牌做特殊调整。官方默认的anchor是80类COCO数据集聚类出来的,用在车牌上不算最优解。我训练前对数据集重新做了K-Means聚类,算出了适合车牌尺寸的anchor,例如[8,4, 12,5, 18,8][28,12, 40,16, 58,22][72,28, 96,36, 128,48]这样的组合。实测下来收敛更快,mAP也有小幅提升。

3.2 训练参数配置与调优

训练YOLOv5的关键参数有这几个:img-size、batch-size、epochs、学习率。我这边因为GPU显存有限,把img-size设为640,batch-size设为32,epochs设了100轮。学习率用的是YOLOv5默认的余弦退火策略,初始学习率0.01,最终学习率0.001。

有几个训练细节想特别提醒一下。第一,YOLOv5默认会做Mosaic数据增强,把四张图拼成一张训练,这对小目标检测帮助很大,但是到了训练后期建议把Mosaic关掉,因为过度的拼接可能让模型对小目标的定位不够精细。我在最后20个epoch把mosaic关闭了,验证集上的mAP确实涨了一点。第二,车牌检测里类别只有一个,也就是plate这一类,如果误设成多个类别,会带来没必要的计算开销。第三,训练过程中要留意验证集的mAP@0.5和mAP@0.5:0.95这两个指标,如果mAP@0.5已经到98以上但mAP@0.5:0.95偏低,说明模型对精确定位还不够好,可以适当增加epoch或者调小anchor的缩放比例。

训练命令大概是这样的:

python train.py --data plate.yaml --weights yolov5s.pt --img 640 --batch-size 32 --epochs 100 --device 0

其中plate.yaml的内容大致如下:

train: /data/ccpd/train.txt val: /data/ccpd/val.txt nc: 1 names: ['plate']

3.3 模型推理与检测结果处理

训练完成后,推理就简单了。加载best.pt权重,输入图片,输出带置信度的检测框。这里有个实操细节:检测置信度阈值和NMS的IoU阈值需要配合着调。我这边置信度阈值设0.5,NMS的IoU阈值设0.45,效果比较平衡。如果阈值设太高,容易漏检模糊车牌;设太低,容易出现重复框或者误检。测试下来置信度阈值0.4到0.5之间比较适合停车场场景。

因为车牌是一个矩形目标,我在推理后还会做一个过滤操作,把检测框的宽高比过滤在2.5到5.0之间,低于或者高于这个范围的基本都是误检,直接丢弃。这个简单的小技巧能显著减少误检率,尤其在画面里有反光、招牌、纹理等干扰的情况下特别有用。

实操心得:检测框输出之后不要急着把图片裁出来做识别,建议先根据角点信息做透视校正。YOLOv5输出的是水平外接矩形,但车牌本身可能是倾斜的,水平裁剪会把背景和相邻车牌的干扰带进来,影响LPRNet识别效果。

4. LPRNet车牌识别模块实现

4.1 LPRNet网络结构与识别原理

LPRNet的核心思路是端到端的序列识别,不需要先把车牌的每个字符切出来。它接收一张固定高度的车牌图片,经过CNN提取特征序列,再通过RNN建模序列上下文关系,最终用CTC损失函数完成序列到序列的对齐。你只需要告诉网络"这串字符是什么",它自己会学到字符之间的位置对应关系。

LPRNet的backbone其实不复杂,由若干个卷积层和降采样层组成,最后接一个双向LSTM来增强序列特征。整体参数量大概在1.5M左右,对比动辄几十M的大模型,这个体量对边缘设备非常友好。这也是我选它做识别端的核心原因,后续如果要部署到Jetson Nano或者树莓派上,性能至少是能接受的。

4.2 字符集设计与标签处理

车牌识别的字符集相比OCR通用场景要小得多,只包含省份汉字、字母和数字。我国车牌第一位是省份汉字,共有31个省级行政区简称,第二位是发牌机关代号字母,后面是字母和数字的组合。我用的字符集有68个字符,包括31个汉字、24个字母(I和O不用于普通车牌)、10个数字,再加上一个空白符用于CTC对齐。

标签处理这里有个容易踩坑的地方。CCPD数据集的标注方式不直接给出车牌字符串,需要从文件名解析或者手动标注。我自己做了一个辅助工具,用YOLOv5检测出车牌区域后截图保存,再用一个简单的UI工具批量打标签,最终得到一份格式为图片路径 车牌字符串的标签文件。这部分工作量确实不小,但数据质量决定模型上限,值得花时间做。

4.3 模型训练与关键参数

LPRNet训练时输入图片的高度我固定为48,宽度自动缩放,保持和原始车牌比例一致。训练过程中的核心参数包括:

参数数值说明
输入高度48网络要求固定高度
批量大小64根据显存调整
初始学习率0.01使用Adam优化器
学习率衰减每5轮衰减0.1逐步收敛
训练轮数60足够达到收敛
损失函数CTC Loss原生PyTorch实现

训练过程中需要关注两个指标,一个是loss值,一个是字符准确率。我这边大概训练到第20轮的时候loss已经降得比较慢,但字符准确率还在缓慢上升,所以还是让它跑满60轮。LPRNet训练对数据集的要求不高,5万张图片已经足够训练出一个识别准确率很高的模型了。

4.4 车牌图像预处理与校正

把检测框里的车牌喂给LPRNet之前,一定要做图像校正。前面提到YOLOv5输出的是水平外接矩形,如果直接裁剪,得到的是包含车牌但可能有倾斜的矩形图。我用的方法是用OpenCV做透视变换,把四个角点映射到一个标准的水平矩形上。具体来说,用cv2.getPerspectiveTransform算一下变换矩阵,再用cv2.warpPerspective把区域扳平。这一步对识别准确率的提升非常明显,尤其在CCPD数据集里倾斜车牌占比不低的情况下。

预处理还有一个细节是归一化。LPRNet训练时用的是ImageNet的均值和方差做标准化,推理时也要保持一致,不然特征的分布会偏移,识别成绩会打折。

5. 检测与识别系统集成

5.1 完整推理流水线搭建

检测和识别单独跑通之后,下一步就是把它们串起来。我的流水线设计是:读入原始图像,交给YOLOv5得到检测框,对每个检测框做透视校正和预处理,再交给LPRNet得到车牌字符串,最后把结果标注回原图。

流水线的核心代码如下:

import cv2 import torch import numpy as np def detect_recognize(frame, detect_model, recognize_model): # 1. YOLOv5检测 results = detect_model(frame, size=640) boxes = results.xyxy[0][:, :4].cpu().numpy() scores = results.xyxy[0][:, 4].cpu().numpy() plates = [] for box, score in zip(boxes, scores): if score < 0.5: continue x1, y1, x2, y2 = [int(v) for v in box] # 宽高比过滤 w, h = x2 - x1, y2 - y1 if w / h < 2.5 or w / h > 5.0: continue # 2. 裁剪车牌区域 plate_img = frame[y1:y2, x1:x2] # 3. LPRNet识别 plate_text = recognize_model(plate_img) plates.append((box, score, plate_text)) return plates

实际跑起来,检测加识别整体耗时在GPU上大约30毫秒,CPU上大约150到200毫秒,可以满足绝大多数停车场道闸场景的实时性要求。

5.2 性能优化手段

如果觉得速度还不够,有几个方向可以优化。第一个方向是模型轻量化,YOLOv5从s到n有很多版本,我在实际部署时选了YOLOv5n,精度掉得不多但速度几乎翻倍。第二个方向是TensorRT加速,把YOLOv5和LPRNet都转成TensorRT的engine文件,推理速度还能再快不少。第三个方向是检测帧率控制,停车场场景里车牌变化并不频繁,没必要对每一帧都做完整检测识别,可以做帧差判断,画面没变化时直接复用上一次的结果,这个优化效果立竿见影。

注意:转TensorRT时如果遇到算子不兼容的问题,建议先看看YOLOv5和LPRNet里有没有用到动态shape,如果有,需要先把输入尺寸固定成静态shape再导出,能省去很多麻烦。

6. 常见问题与排查经验

6.1 数据集解析时遇到文件名编码问题

CCPD的文件名包含中文字段吗?不包含,但它用数字编码表示省份汉字。有些新手朋友在使用正则解析文件名时,分不清154&383_386&473这种坐标格式,容易解析错位置。我的建议是写一个专门的解析函数,先按横杠分段,再逐字段解析,每一步都print出来核对,不要一股脑写完整流程再调试。

6.2 车牌检测框不准怎么办

如果YOLOv5预测的检测框和真实车牌贴合不够紧密,框偏大或者偏小,可以在后处理里做一个外扩或者收缩。我实际测试发现,检测框略微往外扩几个像素,对LPRNet识别的准确率反而有帮助,因为太紧的框可能裁掉车牌边缘的字符。但扩太多也不行,会把背景噪声引进来。我这边用的是宽度和高度各扩5%的比例。

6.3 LPRNet识别结果乱码问题

识别结果乱码有几种可能。第一种是字符集没有对齐,训练时字符集的索引顺序和推理时不一致,这种问题一般是代码bug,仔细检查映射关系就行。第二种情况是车牌图像预处理和后处理不匹配,比如训练时用了灰度图,推理时却传了彩色图。第三种情况是车牌倾斜太严重,透视校正做得不够好,可以增加一些随机旋转和透视变换的数据增强来提升模型的鲁棒性。

6.4 GPU显存不足的解决方案

训练YOLOv5时如果GPU显存不够,有几个常用的办法。降低batch-size是最直接的思路,但会影响BatchNorm的统计效果,建议同步调小img-size或者改用更小的模型版本。还可以开启梯度累积,等效扩大batch-size。LPRNet那边显存占用很小,一般不用担心。

6.5 从CPU到GPU迁移推理的坑

开发阶段可能在CPU上跑通代码,部署时换到GPU上发现结果不一致。这种问题多半是模型权重加载时device没有正确指定,或者输入张量没有转移到GPU上。排查思路很简单,把模型的device和输入的device分别打印出来对比一下,能解决大多数问题。

6.6 夜间和强光场景的效果提升

如果车牌识别要在夜间或者强逆光场景下工作,单纯靠模型很难做到完美。我试过两个有效的手段,一个是加图像增强预处理,比如CLAHE自适应直方图均衡化,提升暗部细节;另一个是用视频流的多帧融合策略,连续取几帧做识别,取置信度最高的结果,能显著降低单帧光照干扰导致的误识别。

7. 后续可扩展的方向

这套YOLOv5+LPRNet的方案跑通之后,可以往好几个方向继续扩展。比如检测端可以换YOLOv8或者RT-DETR,精度和速度都有提升空间;识别端可以尝试加入注意力机制,增强对模糊车牌的建模能力;部署端可以往TensorRT、ONNX Runtime、RKNN等方向走,适配不同的硬件平台。

还可以考虑从单张图片识别扩展到视频流识别,加入目标跟踪模块,实现多帧跟踪识别,提高识别置信度和稳定性。对停车场管理系统来说,结合车辆颜色识别、车标识别等功能,能构建更完整的车辆结构化信息输出。

我个人经验是,这类项目最重要的是把数据处理管线和模型训练流程彻底跑通,让每一个环节都变成可复用的工具链。这样一来,无论以后换数据集、换模型还是换部署平台,都只是局部模块的替换,整体框架不需要重写。这个思路分享一下,希望能帮你在自己的车牌识别项目里少走一些弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 17:05:02

TIS 数据同步指南:三步搭好批流一体的数据集成平台

TIS 数据同步指南&#xff1a;三步搭好批流一体的数据集成平台 【免费下载链接】tis Support agile Ontology DataOps Based on Flink, DataX and Flink-CDC with Web-UI 项目地址: https://gitcode.com/GitHub_Trending/ti/tis 还在手改 json、对着命令行一条条排同步任…

作者头像 李华