☰
YOLOv5车牌检测+LPRNet车牌识别完整资源包实战解析
2026/10/10 19:13:21 网站建设 项目流程

简介:面向智能交通、安防监控等场景的开发者,这份YOLOv5车牌检测与识别项目提供了从车牌定位到字符识别的完整实现方案,可应用于停车场出入口、电子警察、高速卡口等实际场景。压缩包为rar格式,共84个文件、约78.16MB,涵盖Python训练与推理脚本、预训练权重(pt/pth)、YAML结构配置、XML标注以及大量JPG/JPEG测试图片,覆盖数据准备、模型训练、检测与识别全流程。已有2057人学习下载,适合目标检测与OCR方向的中高级开发者用于算法实践与项目二次开发。解压后可直接运行测试脚本,快速在样例图片上完成车牌定位与字符识别;同时可修改配置、更换数据集进行二次训练,配套的LPRNet字符识别模块也为理解端到端识别原理提供了直观参考。整个包结构清晰,既是一套可直接演示的Demo,也可作为进一步优化车牌识别精度与速度的研究起点。

1. 一套能直接跑的车牌检测识别包:YOLOv5定位加LPRNet识别的完整资源

停车场道闸摄像头拍到的画面里,车牌往往只占整张图的百分之几,要在一帧内同时完成定位和字符读取,靠单一模型硬扛并不划算。这套 YOLOv5 车牌检测和识别资源包走的是双模块闭环:YOLOv5 负责拉出车牌边界框,LPRNet 负责把框内区域识别成字符文本,压缩包里从模型配置、权重文件、测试图片到训练脚本全部给齐,解压后就能在本地把整条推理链路跑起来。适合有 Python 和深度学习基础、想直接复现车牌识别效果的工程师,也适合做智能交通、安防道闸、出入口管理系统的人拿来做基线项目参考。

2. 双模块拆解:YOLOv5怎么定位车牌,LPRNet怎么读出字符

2.1 YOLOv5检测管线:三层特征图与四个模型配置

YOLOv5 的检测思路常被概括为 Unified Detection Network,把目标检测拆成边界框预测、类别概率预测和对象中心位置预测三个子任务。实际运行时,输入图像先经过 Backbone 提取特征,再通过 Neck 做多尺度特征融合,最后在 Head 的三个不同尺度特征图上输出预测结果。小尺寸特征图负责大目标,大尺寸特征图负责小目标,车牌这种画面占比不大的目标,主要靠中后层特征图去捕获。

资源包 models 目录下放着 yolov5s.yaml、yolov5m.yaml、yolov5l.yaml、yolov5x.yaml 四个配置文件,对应 YOLOv5 从小到大的四种模型规模。这四个文件的核心差异不在一行行网络结构写死,而是通过两个缩放参数控制整体深度和宽度:

配置名称depth_multiplewidth_multiple适用场景
yolov5s.yaml0.330.50实时性优先,CPU也能跑
yolov5m.yaml0.670.75精度速度均衡
yolov5l.yaml1.001.00精度优先,适合GPU
yolov5x.yaml1.331.25追求极限检测效果

depth_multiple 乘的是网络堆叠的 block 数量,width_multiple 乘的是每层通道数。比如 yolov5s 的通道数只有 yolov5l 的一半,所以参数量小、推理更快,代价是对小目标和模糊目标的特征表达能力弱一些。实际项目里如果检测端算力充足,我一般直接选 yolov5m 起步,车牌检测不需要上到 x 级别,s 和 m 之间的收益差最划算。

值得留意的是,配置里的 anchors 参数是在 COCO 等公共数据集上聚类出来的默认值,并不完全适配车牌这种长宽比极端的目标。如果后续要自己训练车牌数据集,建议先用 k-means 在车牌标注框上重新聚类 anchor,再写回 yaml 文件的 anchors 字段。这个包如果直接用预训练权重做检测,anchor 不匹配的影响还能被置信度阈值压住,但要微调训练,这一步省了效果会打折扣。

2.2 LPRNet车牌识别:从裁剪图到字符序列

YOLOv5 输出的是车牌的位置框,真正把车牌变成字符串的是 LPRNet。LPRNet 是轻量级车牌识别网络的代表结构,整体思路是用 CNN 提取视觉特征,接一层双向 LSTM 对特征序列做上下文建模,最后用 CTC 损失函数对齐字符序列。这种设计最大的优势是不需要先做字符分割,省去了定位每个字符位置的标注成本。

资源包中的 LPRNet.py 就是这一模块的实现,按常见结构简化后大致长这样:

# LPRNet.py 中的关键结构(按常见 LPRNet 实现简化说明) import torch import torch.nn as nn class LPRNet(nn.Module): def __init__(self, class_num=68): super(LPRNet, self).__init__() # CNN backbone:逐层提取车牌字符的视觉特征 self.backbone = nn.Sequential( nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1), nn.ReLU(inplace=True), nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=0), nn.MaxPool3d(kernel_size=(1, 2, 2)), # 中间还有若干卷积和池化层,负责下采样和特征抽象 nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1), nn.ReLU(inplace=True), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1), ) # LSTM 层:对 CNN 输出的特征序列做双向建模 self.rnn = nn.LSTM(128, 128, bidirectional=True, num_layers=2) def forward(self, x): x = self.backbone(x) # 经过 LSTM 后输出每个时间步的字符概率分布 return x

class_num 是字符类别总数,中国车牌的字符集大概由省份简称、字母和数字组成,常用字符表在 68 类左右,这就是 68 的由来。如果换到香港、澳门车牌或者新能源车牌的新字符,需要重新扩展字符表并对应修改 class_num。LSTM 双向建模的好处是字符之间有关联,比如省份简称后面基本跟字母,字母后面跟数字,这种上下文信息能明显提升第二位字符的识别准确率。后面的 CTC 解码会把网络输出转换成最终字符串,训练时用 CTC Loss 计算梯度,推理时用贪心解码或者 beam search 取概率最高的路径。使用 CTC 意味着标注只需要给出整个车牌文本,不用标每个字符的具体位置。

2.3 检测与识别串联:letterbox、NMS 与数据增强

这一段对接上整个流程:读取原始图,YOLOv5 输出若干边界框,NMS 去掉重复框,然后按坐标裁剪出车牌区域,缩放后送进 LPRNet。整套逻辑在 detect.py 中的串联方式大致是这样:

# detect.py 中检测分支与识别分支的串联逻辑(示意) for det in pred: # pred 是 YOLOv5 输出的检测结果张量 x1, y1, x2, y2 = det[:4].long() # 解析边界框四角坐标 # 按边界框从原始图像上裁剪车牌区域 plate_crop = img[y1:y2, x1:x2] # LPRNet 输入尺寸固定,需要先把裁剪图 resize 到 94x24 plate_resized = cv2.resize(plate_crop, (94, 24)) # 调整维度顺序:HWC -> CHW,并增加 batch 维度 plate_tensor = torch.from_numpy(plate_resized).permute(2, 0, 1).unsqueeze(0).float() # LPRNet 前向推理,输出每个时间步的字符概率 logits = lpr(plate_tensor) # 贪心解码:取每个时间步最大概率字符,合并重复项 text = greedy_decode(logits, class_map)

这段代码里有两个关键点。第一,YOLOv5 检测框的坐标必须在原始图像坐标系下裁剪,不能直接拿 resize 后的图像坐标去裁,否则框的位置会整体偏移。第二,LPRNet 推理前图像要做归一化处理,常规做法是把像素值除以 255 并缩放到 [-1, 1] 区间,很多初学者在这一步漏掉归一化,导致识别结果乱码。

提示:如果使用 YOLOv5 官方 detect.py,默认输入图像会经过 letterbox 等比缩放并在四周填充灰边。此时记录 padding 参数,裁剪时要把坐标还原到原图坐标系,这是检测框准确命中的关键。

数据增强在这套流程里也扮演重要角色。YOLOv5 训练时默认会做 mosaic 拼接、随机缩放、色彩空间扰动等增强,目的是扩大训练集多样性、防止模型过拟合。车牌识别任务中光照变化剧烈,白天逆光、夜晚车灯直射都会让车牌区域亮度分布差异很大,训练时如果不开色彩扰动,模型到了夜间场景基本会报废。资源包里的训练脚本继承了 YOLOv5 默认增强策略,自己采集数据时建议保留至少 20% 的夜间和逆光样本,增强数据多样性比单纯叠加增强策略更有效。

3. 从推理到训练:detect.py的参数边界与train.py自定义数据集

3.1 环境准备:依赖安装与容器化运行

资源包根目录下有 requirements.txt 和 Dockerfile,这是两套不同的环境准备路径。requirements.txt 适合本地 GPU 环境,内容涵盖 torch、torchvision、opencv-python、pyyaml、tqdm、matplotlib 这几个核心库。Dockerfile 则把运行环境固化到容器里,适合要交付给同事或部署到服务器上的场景。

# 解压资源包(rar 格式需要 unrar 或 7z 支持) 7z x yolov5-detect_car_plate.rar cd yolov5-detect_car_plate # 方式一:直接基于 requirements.txt 安装 pip install -r requirements.txt # 方式二:基于 Dockerfile 构建容器镜像 docker build -t yolov5_plate:latest .

我一般优先推荐 Docker 方式,尤其是前后换过几台机器、被 CUDA 版本问题折磨过的人,容器至少把依赖版本隔离这一步做掉了。requirements.txt 安装的 torch 版本如果和本机 CUDA 版本对不上,加载权重文件时经常会报莫名其妙的错误,比如在 GPU 机器上装到了 CPU 版 torch,跑推理时速度慢得离谱而且没有任何报错提示。装完依赖后先跑一条 CPU 推理命令验证环境,再切到 GPU,能省掉大量排查时间。

提示:requirements.txt 中的 torch 版本往往有下限要求,比如 torch>=1.7.0。高版本 torch 向下兼容性整体不错,但 2.x 版本对部分旧代码中的 API 做了调整,运行 detect.py 前最好先用python -c "import torch; print(torch.__version__)"确认实际版本。

3.2 跑通检测:detect.py 核心参数与输出目录

资源包根目录的 detect.py 是检测入口,weights 目录下存放训练好的权重文件,test1.jpg、test2.jpg 等测试图直接放在根目录。跑一条基础推理命令:

python detect.py --weights weights/best.pt --source test1.jpg --img-size 640 --conf-thres 0.5 --iou-thres 0.45

各参数含义如下:

参数取值示例作用与调整建议
--weightsweights/best.pt指定模型权重路径,加载 YOLOv5 检测模型
--sourcetest1.jpg推理输入,可以是单张图片、文件夹、视频流
--img-size640letterbox 缩放目标尺寸,需与训练尺寸一致
--conf-thres0.5置信度阈值,低于该值的检测结果被丢弃
--iou-thres0.45NMS 去重阈值,控制重叠框的保留策略

--img-size 是第一个要确认的参数。如果训练时用的 640,推理也必须是 640,不能为了提速减到 320,否则小目标在缩小后的特征图上信息丢失严重,车牌这种本就占比不大目标可能直接漏检。--conf-thres 的调整看场景:白天光照正常、车牌清晰,0.5 够用;夜间或者车牌模糊,我会把阈值降到 0.25,宁可多出几个误检框,也不能漏掉真实车牌。--iou-thres 一般维持 0.45 到 0.5 之间,这个参数决定 NMS 删除重叠框的力度,设太大会出现同一个车牌多个检测框,设太小会把旁边紧挨着的两个车牌误删成一个。

运行结束后,检测结果的标注图默认输出到 runs/detect/exp 目录。资源包里runs目录本身也存放了样例输出,可以对比自己的推理结果和作者跑出来的效果。如果输出目录里只有图没有识别出的车牌文本,说明 detect.py 默认只保存了检测框可视化结果,识别文本需要额外打开保存逻辑,把 LPRNet 的解码结果写入图片标题或同名 txt 文件。

3.3 训练自定义数据集:data.yaml 与超参数调节

如果要针对特定场景训练自己的车牌检测模型,核心是数据组织和 train.py。YOLOv5 的标签格式是每个图片对应一个 txt 文件,每行内容为类别编号、中心点 x 坐标、中心点 y 坐标、框宽、框高,坐标都归一化到 0 到 1。资源包 data 目录和 images 目录存放了样例图片,可以参照其目录结构组织自己的数据集:

data/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 └── labels/ ├── train/ # 训练集标签,与图片同名 txt └── val/ # 验证集标签

组织好后写一份 data.yaml,指定训练集、验证集路径和类别数:

# data/data.yaml train: data/images/train val: data/images/val nc: 1 names: ['license_plate']

然后运行训练命令:

python train.py --data data/data.yaml --cfg models/yolov5s.yaml --weights '' --batch-size 16 --epochs 100 --imgsz 640

这里 --weights 参数我留空了,表示从零训练,而不是加载预训练权重继续微调。如果想微调,可以改成--weights yolov5s.pt,这样会加载 COCO 预训练模型的前面层参数,在小数据集上收敛更快,也更不容易过拟合。--batch-size 受 GPU 显存限制,显存不够就调小 batch,同时配合梯度累积来弥补;--imgsz 建议和推理时的 --img-size 保持一致,这样训练和推理的尺度不会出现偏差。

train.py 里的超参数远不止这几个。初始学习率 lr0 默认 0.01,momentum 默认 0.937,weight_decay 默认 0.0005,这些是针对一般目标检测任务调出来的组合。车牌数据集通常规模不大,几百到几千张,如果出现训练震荡,优先把 lr0 降到 0.001,再把 mosaic 数据增强概率调低。mosaic 增强在数据集小的情况下反而会引入过多拼接痕迹,让模型学到不真实的特征。训练日志保存在 runs/train/exp 目录下,权重文件 best.pt 和 last.pt 都在该目录,推理时指定 best.pt 即可。

4. 避坑:环境、显存、识别乱码与部署精度问题排查

4.1 坑位一:加载权重时报 Torch not compiled with CUDA enabled

这是个非常典型的报错,现象是程序在model = DetectMultiBackend(...)这行直接崩溃,错误信息里明确写着Torch not compiled with CUDA enabled。很多人第一反应是去查代码里 device 参数,实际上问题根本不在这。

原因是 torch 装成了 CPU 版本。pip 默认安装 torch 时会根据当前 PyPI 上的 wheel 决定版本,国内很多镜像源里默认就是 CPU 版。解决方法是先检查本机显卡驱动和 CUDA 版本,再安装对应 CUDA 的 torch。推荐在 PyTorch 官网选择器里找到匹配的命令,比如 CUDA 11.8 对应:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

装完后用torch.cuda.is_available()验证,返回 True 说明 GPU 可用。从那以后每次在新机器配环境,我都强制先跑这一步,避免后面推理花了半天时间白等。

4.2 坑位二:LPRNet 识别结果乱码或整体错位

现象是检测框完全正常,但输出的字符既不匹配车牌,也不是常见字符,看起来完全是乱码。原因通常是字符集映射表不一致。训练 LPRNet 时的 class_num 和字符顺序映射存在一个固定的 class_map 字典,推理时如果代码里的字符顺序和训练时不一致,解码出的文本自然就乱了。资源包中训练脚本和识别脚本大概率共用同一份字符表,但自己改动过字符集、或者直接替换了权重文件后,这个映射关系容易悄悄错位。

第二种常见原因是图像预处理不一致。训练时做了归一化、均值方差标准化,推理时没做,网络输入分布对不上,输出概率分布被打乱。检查推理流程里的预处理函数是否和训练完全一致,尤其是归一化的数值范围。解决方法是把预处理逻辑统一封装成一个函数,训练和推理都调用它,不要在两个地方各写一份。

4.3 坑位三:检测框总是偏移半个车牌位置

现象是框能框住车牌附近区域,但总偏左上或偏右,裁出来的区域不是完整车牌。这种情况排查检测模型本身往往是徒劳,问题大多出在图像预处理阶段。YOLOv5 推理时会用 letterbox 把图像等比缩放到 640x640,不足的部分填充灰色像素。如果检测得到的坐标是在 letterbox 图上的坐标,直接套用到原始图上裁剪,不减去 padding 偏移,框的位置就会整体偏移。

解决方法是保留 letterbox 函数返回的 padding 参数,推理结束后把坐标减去 padding 再除以缩放比例,还原到原图坐标系。代码层面就是记录ratio, (dw, dh),然后对每个检测框做对应变换。这类问题在检测结果可视化时看不出来,因为画框是在同一张 letterbox 图上画的,一旦切图给 LPRNet 就露馅。

4.4 坑位四:训练时 GPU 显存不够直接 OOM

现象是训练刚开始就跑出RuntimeError: CUDA out of memory,甚至把显存占满后导致整个桌面卡死。原因多数是 batch-size 或者 imgsz 一开始设太高,少数情况是显卡本身显存就小,比如 4G 显存的入门卡跑默认 batch-size 16 和 640 分辨率,大概率直接炸。

解决路径有两条。第一条是把 batch-size 降到 4 或 8,同步把 imgsz 从 640 降到 416,这样显存占用会明显下降,代价是模型对细节特征的提取能力弱一点。第二条是开启梯度累积,比如显存只能跑 batch-size 4,但想模拟 batch-size 16 的效果,就在 train.py 里把累积步数设为 4,每 4 个 batch 更新一次权重。如果显存依然不够,最后手段是开启半精度训练 float16,显存占用直接砍半,但要留意模型精度可能轻微下降。

4.5 坑位五:模型量化后边缘设备识别精度暴跌

现象是训练好的模型在 GPU 上识别率很高,导出 ONNX 再转到 RK3568 或树莓派部署后,漏检明显增多,车牌字符错误率也上升。这个问题的根源往往不在设备,而在量化策略。直接使用神经网络编译器默认的动态量化,对 LPRNet 里的 LSTM 层效果尤其差,因为 LSTM 的循环结构对数值精度极其敏感,一旦权重被压缩到 int8,隐状态传递的误差会随时间步累积。

解决方法是区分量化对象。检测端 YOLOv5 用静态量化加校准数据集,校准数据选 100 张以上覆盖不同光照条件的车牌图片;识别端 LPRNet 优先保留 fp16 精度,不强行压到 int8。如果硬件只支持 int8,那就用感知量化训练 Quantization-Aware Training,让网络在训练阶段就适应低精度数值范围,而不是训练完再做后训练量化。这一步属于部署环节里最需要花时间的部分,没有捷径可走。

5. 进阶:模型导出与边缘部署验证

5.1 ONNX 导出与树莓派验证流程

模型训练收敛后,部署到边缘设备前应该先做一次完整性验证。标准流程是先把权重导出成 ONNX 格式,在树莓派等 ARM 设备上跑一遍 onnxruntime,确认输出与 GPU 推理基本一致,再做 RK3568 的 RKNN 转换。这样可以把问题分层排查,不至于直接跳到 RKNN 阶段,分不清是转换问题还是网络本身的问题。

# 导出 ONNX 模型 python export.py --weights weights/best.pt --img-size 640 --include onnx # 树莓派上安装 onnxruntime 后做推理验证 onnxruntime 推理脚本对比输出

量化这一步我会盯紧精度损失。一般建议以 GPU 上的 mAP 为基准,边缘设备量化后 mAP 下降控制在 2% 以内才判断为正常。超过 2%,先增加校准图片数量,再检查是否 LPRNet 被过度压缩。对 RK3568 这类 NPU 设备,YOLOv5 检测端走到 rknn-toolkit 转换时,注意设置正确的量化精度和输入格式,NCHW 和 NHWC 的转换容易在中间抠掉细节。

从那以后我每次做完训练,都会强制走一遍导出 ONNX、在 CPU 和 GPU 上对比精度、再量化部署的流程,这套流程帮我把边缘部署的返工率压到了很低。车牌检测识别这类任务对实时性和精度的双重需求,决定了理论和实际部署之间总有一段路要走,希望这套资料能帮你少走一段弯路,也希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询