YOLOv5+LPRNet实战:车牌识别两段式方案 简介本资源是一个基于YOLOv5与LPRNet双模型协同的轻量级车牌识别完整实现面向计算机视觉初学者与项目实践者聚焦中国蓝牌及新能源绿牌的端到端检测与识别任务适配Windows平台开发环境。压缩包共61个文件含22个核心Python脚本覆盖数据预处理、YOLOv5训练/检测、LPRNet训练/识别、模型集成推理等全流程、27张CCPD原始样本图、3个配置yaml文件、3个txt标注映射表及权重文件yolov5_best.pt、lprnet_best.pth整体大小为16.72MB结构清晰、模块解耦便于分阶段调试与二次开发。已有124人学习下载提供从CCPD数据集格式转换ccpd2yolov5.py/ccpd2lpr.py、模型训练train_yolov5.py/train_lprnet.py到联合推理main.py/detect_yolov5.py/test_lprnet.py的全链路代码支持并附带实测结果图像det_result/rec_result目录显著降低车牌识别项目落地门槛。 车牌识别是视觉落地里很常见的需求但真正把它做到稳并不容易。我一开始用的是网上现成的端到端识别模型单张图跑起来倒挺快可到了实际停车场画面里就露馅了远处的小车牌、大太阳底下的反光、雨天的水渍、斜着进场的车各种漏检误检全冒出来。后来我拿到一个YOLOv5LPRNet做车牌检测识别的项目压缩包把数据准备、模型训练、推理串联完整跑了一遍才体会到拆成两段式的好处YOLOv5负责检测车牌位置LPRNet负责把检测框里的字符读出来训练数据用CCPD。这套组合在开源圈里成熟度高、代码结构清晰单卡就能训完部署也方便特别适合刚入门车辆视觉、或者想把车牌识别从“能跑”提升到“能上路”的朋友参考。下面我把整个流程、训练要点和踩过的坑一次性说清楚。1. 为什么车牌识别要拆成“检测识别”两步走1.1 一段式端到端方案的问题在哪儿很多刚接触到车牌识别的朋友第一反应是找一个端到端模型输入整张车辆图片直接输出车牌号码。这个思路本身没问题但实际落地时问题不少。首先车牌在原图中通常只占很小一块区域以CCPD为例图片是720×1160车牌大概只有100×30像素左右特征图下采样几轮之后车牌对应的特征非常少模型很容易漏检。其次端到端模型把“车牌在哪里”和“字符是什么”两个任务耦合在一起一旦识别错误你很难判断是检测环节出了问题还是字符识别环节出了问题。我之前调试过一个端到端模型同一张图白天识别对、晚上识别错又找不到中间量可以观察最后只能一点点换测试图猜原因。1.2 两段式方案的真实优势拆成检测和识别两个模型后事情就清晰了检测器负责输出每个车牌的边界框识别器只负责把框内区域转成字符串。中间只要把检测框可视化出来一眼就能看出是检测漏了、框歪了还是识别错了。这个“可排查性”在项目现场尤其值钱。另一个好处是两个任务可以分别调优。检测做不好就补检测数据、调anchor识别做不好就补车牌图集、调CTC解码。互不影响迭代速度比端到端快很多。1.3 为什么选YOLOv5和LPRNet这个组合YOLOv5在检测这块不用多说社区生态最成熟训练脚本、预训练权重、ONNX导出、TensorRT部署都有现成方案遇到问题搜一下基本都有答案。LPRNet是专门为车牌识别设计的轻量级卷积网络核心思路是CNN提取特征后直接输出字符序列概率用CTC损失对齐不需要把车牌字符一个个切开。这一点很关键因为实际场景里的车牌经常有倾斜、反光、字符间距不均硬分割很容易出错。LPRNet模型体积小、推理快跑在CPU上也能接受。相比接一整套通用OCR或大模型方案LPRNet在车牌这种固定格式场景下精度够用部署成本又低得多。2. CCPD数据集的真实面貌标注藏在文件名里2.1 数据集构成与选择CCPD是中国城市停车场的开源车牌数据集图片拍摄于真实停车场和路边场景图像分辨率约720×1160。官方按场景划分了多个子集最常用的是ccpd_base这是基础场景的大规模数据集数量最大适合用来训练检测器和识别器的主模型。另有ccpd_weather、ccpd_night、ccpd_blur、ccpd_rotate、ccpd_tilt、ccpd_fn、ccpd_challenge等子集分别对应天气干扰、夜间、模糊、旋转、倾斜、远近距离和综合挑战场景。我的建议是主模型用base集训练然后用其他子集当测试验证模型在真实复杂环境下的泛化能力。不要一开始就把所有子集混在一起训练不然模型在base上表现挺好换到night和weather上就崩你都不知道问题出在哪。2.2 标注信息都在文件名里CCPD和通常的目标检测数据集很不一样它没有PASCAL VOC那种xml标注也没有COCO的json标注所有标注信息都编码在文件名里。这也是很多人拿到数据集后第一卡点。文件名里包含了省份简称、车牌字符、倾斜角度、车牌四角顶点坐标、亮度、模糊度等信息字段以短横线分隔。写解析脚本之前务必先解压一个文件用字符串分割看一遍再对照官方说明确认字段顺序。下面这个脚本是核心思路字段序号以你手里的数据集版本为准import os def parse_ccpd_filename(filename): name filename.replace(.jpg, ) parts name.split(-) # 不同版本的字段顺序可能不同这里以“车牌文字在前、坐标信息在后”的常规版本为例 # parts[0] 为省份简称parts[1] 为车牌后段拼接即完整车牌 plate parts[0] parts[1] # 后续字段按顺序解析出四个顶点坐标、角度、亮度等 # points [(int(parts[2]), int(parts[3])), ...] return plate, points2.3 转换成YOLO训练格式检测器训练用的是归一化坐标需要把文件名里解析出来的四角点坐标转成YOLO的txt标注。每个标注文件跟图片同名一行对应一个目标。车牌检测只有一个类别所以类别编号写0即可。转换时我建议直接用四角点的外接矩形也就是取所有x的最大最小值、所有y的最大最小值。YOLOv5默认不支持旋转框直接用外接矩形省事识别阶段再做透视校正就行。def points_to_yolo(points, img_w720, img_h1160): xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h return f0 {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}要注意的是个别车牌角度特别大外接矩形会带进不少背景这类图可以筛掉或者后续用数据增强补偿。基础训练阶段没有必要追求所有标注都完美整体框准了就行。2.4 数据划分与增强划分数据集时我习惯按图片所属子集来做而不是把所有图片混在一起随机切。比如用ccpd_base的前面一部分做训练、后面一部分做验证然后单独拿ccpd_weather和ccpd_night做测试集。这样能直观看到模型在不同场景下的掉点情况。增强方面车牌识别场景有几个禁忌不能做水平翻转因为车的左右和字符顺序会被破坏不能做任意角度旋转旋转太多会改变车牌形状随机亮度、对比度、高斯噪声、轻微仿射变换都是可以加且效果不错的。训练YOLOv5时YOLOv5自带的mosaic增强对提升鲁棒性很有帮助可以直接开着。3. 检测器训练让YOLOv5学会只找车牌3.1 环境准备与目录结构先准备YOLOv5训练环境Python 3.8及以上、PyTorch 1.8以上基本都能跑。把项目clone下来安装requirements.txt里的依赖。如果机器有NVIDIA显卡建议直接装CUDA版PyTorch训练速度快很多没有GPU用CPU也能训练只是慢。目录结构我建议这样组织datasets/ccpd/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/图片和labels一一对应labels里是YOLO格式的txt文件。图片名和txt名要完全一致。这一步最容易出问题的是路径不一致跑训练时报“No labels found”十有八九就是目录结构或命名没对上。3.2 准备data.yaml和训练命令YOLOv5需要一个data.yaml指定训练集、验证集路径、类别数和类别名。车牌检测只有一个类别所以nc写1names写[plate]。示例train: datasets/ccpd/images/train val: datasets/ccpd/images/val nc: 1 names: [plate]然后执行训练python train.py --img 640 --batch 16 --epochs 100 --data ccpd.yaml --weights yolov5s.pt --device 0我实测下来ccpd_base子集取2万张图做训练用yolov5sbatch16、100个epoch在单张GTX 1080Ti上大约几个小时能训完。如果显存不够把batch降到8或者换成yolov5n。输入分辨率640×640对CCPD足够因为CCPD原图里的车牌相对清晰检测难度不算大。3.3 模型选型与anchor设置车牌检测是单类小目标检测yolov5s是性价比最高的选择yolov5n更快但精度稍低yolov5m及以上对小目标会有改进但训练和推理成本也上去了。对大多数场景yolov5s就够了。anchor的默认参数是基于COCO数据集生成的COCO里的目标尺寸分布和车牌差别很大所以训练时建议让YOLOv5重新聚类也就是不要加--noautoanchor参数这样模型会根据你的车牌数据集自动调整anchor。我有一次为了省时间关掉了autoanchor结果检测小目标车牌的召回率低了几个点重新打开后有明显改善。3.4 训练结果怎么看训练完成后看runs/train/exp*/里的结果文件。重点看confusion_matrix.png、PR_curve.png和results.csv。车牌检测相对简单训练收敛后mAP0.5应该能到0.99左右mAP0.5:0.95至少也应该在0.75以上。如果mAP上不去先检查标注有没有转错尤其是坐标是否归一化到0-1很多时候问题不在网络而在数据。检测置信度阈值在实际推理时设0.5左右就够太低会把车灯、广告牌上的“车牌样式”误检出来。4. 识别器训练LPRNet如何搞定不定长车牌字符4.1 LPRNet的原理与输入LPRNet的核心是CNNCTC。它不像传统OCR那样要先把车牌字符分割成单个字母再把每个字母识别出来而是把整张车牌图输入网络网络输出一个时间步的字符概率分布然后用CTC损失来完成序列对齐。这个设计思路对车牌识别很友好因为车牌字符是可变的省份汉字加字母加数字总长度6到8位不等而且实际场景里字符间距、模糊程度变化大CTC天然能处理这种不定长、弱对齐的情况。LPRNet输入通常是24×94的灰度图网络结构比较轻参数量很小CPU上单张识别也只有几十毫秒级别。4.2 从CCPD裁剪车牌训练数据识别器训练数据可以通过解析CCPD文件名里的四角点坐标直接从原图裁剪出车牌区域。这一步建议做透视校正把倾斜的车牌矫正到水平正框能显著降低识别难度。用OpenCV实现很方便import cv2 import numpy as np def crop_and_rectify(img, pts, plate_w94, plate_h24): src np.array(pts, dtypenp.float32) dst np.array([[0, 0], [plate_w - 1, 0], [plate_w - 1, plate_h - 1], [0, plate_h - 1]], dtypenp.float32) M cv2.getPerspectiveTransform(src, dst) return cv2.warpPerspective(img, M, (plate_w, plate_h))裁剪好的车牌图可以存成单张图文件名直接写车牌的字符内容比如“皖A12345.jpg”训练时按文件名读取标签非常方便。我建议切分训练集和验证集时要按车牌字符分布做基本检查保证验证集里省份、字符都覆盖到不然验证准确率会虚高。4.3 字符集定义与编码LPRNet的类别数由字符集决定。字符集至少包含全国各省份简称汉字约31个、数字0到9、英文字母去掉I和O避免和数字1、0混淆加起来大约65个类别再加上一个CTC空白符。实际项目中可以根据业务范围扩展或缩小字符集比如只做省内场景可以减少汉字类别。编码时每个字符对应一个索引车牌字符串转成索引序列补长或补短后交给CTC loss计算。特别注意cls索引从0开始但类别序号不要和CTC的blank冲突网络输出维度就是len(charset)1。4.4 训练要点与评估指标LPRNet训练相对简单损失函数用CTCLoss优化器可以用Adam。训练时数据增强同样不能翻转可以加亮度扰动、对比度抖动、模糊、轻微缩放和仿射变换。评估指标分两个层次字符级准确率和车牌级准确率。字符级是每个字符预测对的比例车牌级是整块车牌完全预测对的占比。一般来说字符级训练到99%以上不难车牌级在验证集上做到95%以上才说明识别器基本可用。判断过拟合的标志是训练loss还在降、验证loss开始升这时候提前停止不要一味多训。5. 串联起来推理从摄像头画面到车牌号码5.1 完整推理链路两个模型都训好后就要把它们串起来。推理流程是输入图像先做YOLOv5的letterbox保持宽高比填充到640×640。YOLOv5前向得到检测框按置信度阈值过滤再做NMS去除重叠框。把检测框坐标映射回原图尺寸在原图上裁剪出车牌区域。可选用检测框对应的四角点做透视校正或者直接用外接框。车牌图缩放成24×94转灰度归一化。送入LPRNet得到字符序列概率。用贪婪解码或beam search解码去掉重复和blank输出最终车牌字符串。这里最容易被忽略的是坐标映射。YOLOv5的输出坐标是letterbox之后的坐标推理时一定要用原始的ratio和pad参数映射回原图否则裁剪出来的区域会偏移。YOLOv5官方detect.py里有现成的代码看看就明白了。5.2 性能实测我在GTX 1080Ti上实测yolov5s单张640×640推理约3到5毫秒LPRNet单张车牌图约1到2毫秒加上图像读取和预处理整个流程单帧可以控制在10毫秒以内。CPU上yolov5s约200到300毫秒LPRNet约20到30毫秒如果是纯CPU实时处理25帧视频建议换yolov5n或者降低推理分辨率。部署到边缘设备比如Jetson系列时可以把两个模型都转成TensorRT engine检测速度能明显提升识别部分提升也很大。视频流场景不用每帧都跑识别结合目标追踪隔几帧识别一次车牌能省不少算力。5.3 多车牌与本文还有配套的精品资源点击获取