news 2026/10/4 4:48:16

YOLOv5车牌检测与OCR识别两段式架构实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5车牌检测与OCR识别两段式架构实战指南

简介:面向计算机视觉开发者和深度学习初学者,这是一份以YOLOv5为检测框架的车牌检测与识别一体化实现方案,适用于智能交通、停车场管理等场景下的车牌定位与字符识别。资源共84个文件,压缩包约78.16MB,内容涵盖Python训练与推理脚本、YOLOv5s/m/l等模型配置、预训练权重、大批量JPG车拍照、XML标注文件以及字符识别网络LPRNet权重,从目标检测到车牌字符识别均有可运行代码支撑。包内目录结构清晰,权重、配置、数据、脚本分层存放,并附有Dockerfile和requirements.txt,便于复现环境和二次开发。当前已有2057人学习下载,适合希望快速跑通YOLOv5车牌识别全流程、学习模型训练与部署细节的开发者。

1. 车牌识别项目里,YOLOv5只解决检测这一半,别让它硬扛识别

停车场出入口、小区门禁、高速卡口的摄像头画面,最终要的不是一个“框”,而是“粤B12345”这样的字符串。YOLOv5车牌检测和识别这类需求,工程上最稳妥的拆法是把任务分成两段:YOLOv5先把车牌区域从画面里检测出来,再交给一个OCR网络把框里的字符读成文本。适合正在做安防、智慧停车、卡口系统的开发者,也适合想拿yolov5训练自己的数据集练手的人。核心结论是:检测和识别解耦,数据好标、问题好定位、部署好替换,一段式端到端模型在车牌这种小目标场景里性价比反而更低。

2. 先检测再识别:两段式架构为什么是车牌落地的默认解

2.1 检测与识别解耦,换来的是三个实际好处

第一,数据集不用混着标。检测模型只需要把车牌外边框标成一个类别,不管车牌是蓝牌、绿牌、黄牌,也不管上面几个字符;识别模型拿到的是裁切好的车牌小图,只需要关心字符内容是不是清晰。两个模型的训练数据可以分开迭代,哪一侧指标差就补哪一侧。

第二,问题定位快。车牌号码识别错,先看检测框是不是准。如果YOLOv5输出框把车牌的左右边界吃掉了半个字符,那就是检测模型的标签或者样本问题;如果框得严丝合缝但OCR还是读错,基本可以断定是识别模型的事,不用在两个模型之间来回猜。这种隔离在项目交付阶段尤其省时间。

第三,部署替换灵活。同样是YOLOv5检测,前端低算力设备换yolov5n,后端服务器用yolov5m,识别模型也可以单独换成更轻量的LPRNet或者更重的CRNN,互不影响。很多工业项目里甚至直接沿用YOLOv5官方仓库,只改数据集和超参数,就能快速出效果。

如果你想先看yolov5网络结构图再动手,官方仓库的yolov5s.yaml里写得很清楚:Backbone从6x6卷积到C3模块,再到SPPF,Neck用PANet把三个尺度的特征图送到Head。车牌检测一般关注中等和较深的两个尺度,最浅的尺度负责大目标,对车牌这种小目标帮助有限,这也是后面调输入分辨率时优先动640和960的原因。

2.2 一段式端到端车牌的适用边界

市面上也有一体化方案,输入整张图,输出车牌字符串。这类端到端模型把检测和识别放进同一个网络里,省掉了中间的检测框传递。实际用下来它有两个不好绕的坎:一是训练数据要求极高,同一种车牌要覆盖各种角度、光照、模糊等级,否则字符错一个很难定位是检测错了还是识别错了;二是小目标车牌在画面里只有几十个像素时,端到端模型的召回率明显不如两段式——检测模型至少能把框画出来,OCR再想办法,而端到端模型一旦漏检,整张图就废了。

一段式方案在车牌领域并不是新东西,LPRNet本身就能端到端识别字符序列,但它的输入是已经裁好的车牌图,不是整张场景图。真正从整图直接到字符串的模型,在实际项目里要处理大量“没有车牌的车”这种负样本,训练成本远高于两段式。所以在车牌识别这个方向上,两段式是默认选择。

2.3 OCR选型:LPRNet、CRNN还是PaddleOCR

检测框拿到以后,识别车牌字符有几个常见选择。小项目里用LPRNet比较多,它轻量、序列长度固定,适合蓝牌和单行车牌;CRNN更适合字符长度不固定的场景,绿牌八位字符也能处理;PaddleOCR这类通用OCR对车牌这种长宽比极端的文字区域反而不占优势,它更适合版面识别。选型时可以按表来:

方案输入优点注意点
LPRNet裁切后的车牌小图模型小、推理快对倾斜车牌敏感
CRNN定宽车牌图字符序列建模好需要更多训练数据
PaddleOCR整图或裁切图通用文本识别强车牌场景需微调

我一般会把LPRNet当主力,因为车牌字符数有限,中文省份简称加字母数字,固定长度的序列建模足够,部署时省掉动态序列解码那一大堆依赖。

OCR训练数据来自检测模型裁出的车牌小图,注意字符类别设计:省份简称(京、粤、沪)和字母数字要分开定义,别把“京”和“9”混成一个类。很多识别翻车案例都是字符类别数定义错了,一个类别里塞了两个长相接近的字符,模型再怎么调也学不出来。

2.4 最小推理流程:用官方权重先跑通检测

动手训练之前,先把官方流程跑一遍。

python detect.py --weights yolov5s.pt --source test.jpg --conf 0.5

这个命令的含义是:加载yolov5s预训练权重,对test.jpg做推理,置信度阈值取0.5。--conf是后处理阶段的关键参数,低于0.5的检测框会被过滤掉;--iou 0.45控制NMS阶段两个重叠框的合并程度。第一次跑通的目的不是检测车牌——官方权重里没有车牌类别——而是确认环境、权重和推理链路是通的。

如果推理结果里出现大量重叠框,多半是NMS的iou阈值设得太大;如果该检出的车牌没框出来,先把conf调低到0.25试,别一上来就动模型。后处理在整个检测链路里经常被当成黑匣子,其实就三个参数:conf、iou、max_det。max_det限制单张图最多输出几个框,停车场多车场景建议设成不低于10。

3. 准备车牌训练集:从数据来源到VOC转YOLO格式

3.1 数据来源与挑选标准

先明确一点:车牌检测模型训练只需要把牌框出来,类别就一个plate。数据集可以用公开的车牌数据集(比如CCPD这类停车场场景采集的),也可以自己拍或从项目现场抽帧。我见过不少项目直接用现场摄像头抽帧,选帧时注意不要连续抽同一个车位的同一辆车,要把场景多样性拉起来。

挑选标准按优先级排:一是车牌类型覆盖,蓝牌、绿牌、黄牌至少各占一批,单层蓝牌和双层黄牌最好都有;二是距离和角度,画面里车牌大小从40像素到200像素都要有,侧倾角,光照分白天、黄昏、夜间;三是模糊和遮挡留一部分,但比例控制在10%以内,否则模型学不到清晰特征。这里有个容易翻车的点:不要只挑正对着的清晰车牌,那种数据集训练出来的模型一到出入口道闸斜拍角度就漏检。

3.2 标注格式与VOC转YOLO的批量脚本

常见标注工具导出的是VOC或COCO格式。YOLOv5训练需要的是每张图对应一个txt文件,每行是“class x_center y_center width height”,坐标经过归一化,单位是0到1的小数。

以下脚本把VOC XML批量转成YOLO txt,适用于单类别车牌检测:

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, img_width, img_height, class_id=0): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.iter("object"): name = obj.findtext("name") if name != "plate": # 只保留车牌类别,其它标签忽略 continue bndbox = obj.find("bndbox") x1 = float(bndbox.findtext("xmin")) y1 = float(bndbox.findtext("ymin")) x2 = float(bndbox.findtext("xmax")) y2 = float(bndbox.findtext("ymax")) # 转成YOLO要求的中心点加宽高,并除以图像宽高做归一化 x_center = (x1 + x2) / 2 / img_width y_center = (y1 + y2) / 2 / img_height w = (x2 - x1) / img_width h = (y2 - y1) / img_height lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") if lines: out_path = os.path.join(out_dir, os.path.basename(xml_path).replace(".xml", ".txt")) with open(out_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) if __name__ == "__main__": voc_to_yolo("annotations/1.xml", "labels", 1920, 1080)

脚本里三个参数要说清楚。第一是img_width和img_height必须和图片实际尺寸一致,用训练图片的宽高而不是XML里写死的resize尺寸;第二是class_id这里写0,如果后续想区分蓝牌、绿牌、黄牌三个类别,就改成不同的id,同时训练配置里的nc要跟着改;第三是过滤逻辑,名单类别里只认plate,其它标注框直接跳过,避免把车身或车灯也当作车牌框进去。坐标归一化要理解:YOLO的框是用中心点和宽高表示的,不能用角点直接写进txt。

3.3 目录结构与data.yaml的写法

YOLOv5训练时读的是data.yaml指定的路径,目录结构我一般这样组织:

datasets/plate/ images/ train/ val/ labels/ train/ val/ data.yaml

data.yaml内容是:

train: /absolute/path/to/datasets/plate/images/train val: /absolute/path/to/datasets/plate/images/val nc: 1 names: ["plate"]

train和val路径填的是images目录,YOLOv5会自动去同级labels目录找对应的txt。这里建议用绝对路径,相对路径容易在切换工作目录后翻车。常见坑是路径写错后训练能开始但recall一直是0,因为标签文件一个都没读进去。检查方法:训练启动日志里会有dataset统计信息,看instance的数量,如果为0就是标签路径没对上。

提示:训练日志如果出现“train and val are the same dataset”这类提示,不要慌,回去检查data.yaml里的val路径,验证集必须和训练集分开。

3.4 数据增强的取舍:Mosaic与旋转别乱开

yolov5默认开Mosaic增强,它对小目标检测很有帮助——车牌在画面里经常是小目标,四张图拼一张能提高模型对多尺度车牌的学习。但车牌要求字符边缘锐利,Mosaic拼图会让车牌边缘被切碎,所以训练后期建议把mosaic关掉,在超参数文件里把mosaic设为0.0再用前几十轮的结果继续finetune。

旋转增强也要克制。车牌检测需要容忍一定角度的倾斜,但过大的旋转会让检测框变成倾斜框——注意YOLOv5输出的是水平矩形框,没法直接输出带角度的框。旋转增强开的度数越大,模型预测的水平框和真实标注框的IoU越容易被拉低。一般hyp文件里degrees设5到10就够,不要超过15。yolov5超参数这个话题热度一直高,其实对车牌这种长宽比固定的目标,动mosaic、degrees、hsv这三项就够了,别再叠一堆花活。

4. 用yolov5训练自己的车牌数据集:超参数与训练命令拆解

4.1 预训练权重和网络结构怎么选

YOLOv5官方仓库提供了n/s/m/l/x几个尺寸的预训练权重。车牌检测场景里,yolov5n适合树莓派和嵌入式设备,yolov5s是通用首选,yolov5m适合对召回率要求高的卡口项目。判断标准是显存和帧率:6G显存以下用s,10G显存想刷精度用m。网络结构上,Backbone用CSP结构的C3模块,Neck用SPPF和PANet做多尺度融合,Head输出三个尺度的预测框。训练车牌这种小目标,输入分辨率比模型尺寸更敏感,640起步,车牌普遍偏小的场景用960。

4.2 训练命令逐段拆解:img、batch、epochs、hyp

python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --hyp hyp.scratch-low.yaml --name plate_exp

参数含义:--data指向data.yaml;--weights用COCO预训练权重做迁移学习,比从零训练收敛快得多;--img 640是训练时输入分辨率;--batch 16是每轮迭代的样本数;--epochs 100对车牌这种单类别数据集通常足够;--hyp选择官方自带的超参数文件,基础配置用hyp.scratch-low.yaml就行。

batch大小的选择直接决定训练能不能跑起来。显存不足时优先调小batch而不是调小img,batch低于8时BatchNorm统计会变得不稳定,loss曲线会抖得厉害。如果12G显存用yolov5s,batch 16是比较稳的组合。超参数文件里的lr0初始学习率默认0.01,小数据集可以降到0.005,避免前期震荡。这里有个经验:车牌数据集一般几千张到一两万张就够,epochs不用学别人跑300轮,100轮足够收敛,跑太多反而记住背景。

4.3 训练中看哪些指标:P、R、mAP@0.5与loss曲线

训练日志里重点看几个值:P(精确率)、R(召回率)、mAP@0.5、mAP@0.5:0.95。车牌检测项目里mAP@0.5比mAP@0.5:0.95更贴近实际,因为部署时NMS的IoU阈值通常就是0.5左右。val_box_mAP这个值稳定上升说明模型在学;如果P很高但R低,说明检出来都准但漏检多,需要补样本或调低conf阈值;如果R高但P低,说明误检多,常见原因是把车身反光、车灯也框了进来,要检查标注集里是不是漏标了大量车牌导致负样本把模型带偏。

loss曲线方面,box_loss代表边框回归损失,obj_loss代表目标置信度损失,cls_loss单类别模型里参考价值不大。训练到60轮后loss曲线进入平台期但mAP还在涨,属于正常现象,继续跑完即可。真正要警惕的是val loss在80轮后反弹,那就是过拟合的开始,这时候best.pt的保存轮数往往在70轮附近,直接用best.pt就行,不要用last.pt。

4.4 用detect.py验证:conf与后处理怎么调

python detect.py --weights runs/train/plate_exp/weights/best.pt --source test.jpg --conf 0.5 --iou 0.45 --max_det 10

训练刚结束的模型先别急着集成。拿现场照片跑一遍detect.py,看三类错误:漏检、误检、框偏移。漏检就调低--conf到0.25看能不能框出来;误检多就调高到0.6;框偏移严重(比如框只盖住车牌的一半字符)说明训练数据里标注框不紧,回到标注环节修正。

这里还要说一句后处理的事。yolov5后处理包含置信度过滤、NMS去重、类别过滤三个步骤,很多新手只调conf不调iou。实际上车位上相邻车辆的车牌不会重叠,iou对车牌场景影响不大,真正关键的是conf。我在项目里会把conf从0.25到0.7做一次扫描,挑出召回和误检平衡的那个点固化进部署代码。

5. 车牌检测落地避坑:五个常见的翻车现场与排查顺序

5.1 蓝牌和绿牌混训导致漏检:拆类别比调参有用

现象:单独训练蓝牌模型准确率很高,把新能源绿牌加进去一起训之后,蓝牌漏检率反而上升。

原因:蓝牌和绿牌的宽度、字符间距差异较大,绿牌照搬到蓝牌的深层特征上,模型在同一个类别里学到互相冲突的分布。如果数据量不平衡,模型会偏向样本多的一类。

解决:先做类别区分。把plate类别拆成blue_plate和green_plate两个类,nc改成2,names对应改,然后重新训练。检测框类别分开后,OCR那边只需要按框的内容读字符,不需要管类别,两个类别在识别阶段共用同一个OCR模型。这个做法比硬调超参数有效,属于把问题交给数据组织而不是交给训练玄学。

5.2 夜间和强曝光下检测框抖动:预处理优先

现象:同一辆车停在原地,视频连续推理时检测框在车牌的上下边界来回跳,OCR偶尔读错一个字符。

原因:车牌的边缘在夜间受车灯和地面反光影响,像素对比度不稳定;强光下车牌反光区域过曝,YOLOv5提取到的目标特征在前景和背景之间摇摆。

解决:先做图像预处理再进模型,夜间用CLAHE(限制对比度自适应直方图均衡)增强车牌区域纹理,比直接换模型参数见效快。推理链路里加一步cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))。如果现场是固定摄像头,更直接的办法是调整相机曝光参数,让车牌区域不要过曝,这比任何后处理都省事。这一条血泪经验来自一个地下车库项目,我们调了三天模型,最后发现是相机自动曝光把车牌拍糊了。

5.3 倾斜车牌识别率低:先查检测框再查OCR

现象:检测框位置准,但OCR读出来的字符错,尤其倾斜超过30度的车牌。

原因:OCR训练数据里倾斜样本太少,或者OCR输入图没有做透视校正。YOLOv5输出的水平框对倾斜车牌来说会包含大量背景,字符被压扁。

解决:按顺序排查。第一步,看检测框是否完整包含车牌四角;第二步,对框内图像做透视校正,用OpenCV的getPerspectiveTransform把车牌区域拉正,然后再送OCR;第三步,如果还不行,给OCR训练集加旋转样本,旋转范围正负15度起步。这里经常有人把顺序反过来,先去调OCR模型,结果是白调——问题在检测框传入的图就没拉正。

5.4 显存不足:batch、分辨率与模型尺寸的取舍顺序

现象:batch 16 + yolov5s + 640分辨率在12G显卡上报CUDA out of memory。

原因:显存被中间特征图占满,batch一次性载入的样本太多。

解决:按优先级试。先把batch降到4,能跑但BatchNorm统计容易不稳;再把--img从640降到512,车牌分辨率低一些但训练目标本身就是小目标,分辨率降低对最终精度影响有限;最后才考虑换yolov5n或开启梯度累积。YOLOv5的命令行里有--workers和--cache,显存不足时不要开--cache ram,那会把数据集预载到显存里。12G以下显存老老实实用yolov5s + batch 8 + img 640,这是最稳的组合。

5.5 远距离小目标漏检:分辨率、P2层与镜头三选一

现象:摄像头安装高度高、视场角大,15米外的车牌在画面里只有30像素宽,检测召回率明显下降。

原因:小目标在骨干网络下采样过程中特征被稀释,三个检测尺度里最小的输出层对30像素的目标依然不敏感。

解决:最直接的手段是把输入分辨率提高到960甚至1280,代价是推理变慢;第二个手段是修改模型结构开启P2输出层,对小车牌很有帮助;第三个手段是硬件上换长焦镜头,缩小视场角,让车牌在画面里占更多像素。部署阶段如果已经定死相机位置,我的习惯是先用分辨率提升,如果帧率不达标再退回640并用裁剪区域二次检测。

6. 检测框外扩、视频回放验证与轻量部署:交付前值得做的三件事

6.1 检测框外扩10%再交给OCR

车牌检测模型输出的框紧贴车牌边缘,直接裁切会让字符顶到图像边界,OCR对这类输入的鲁棒性差。推理代码里我会把框向外扩10%,再把坐标裁剪回图像范围内。这一小步能明显降低字符漏读。代码上就是检测后处理里加两步:

x1 = max(0, int(x1 - 0.1 * (x2 - x1))) y1 = max(0, int(y1 - 0.1 * (y2 - y1))) x2 = min(W, int(x2 + 0.1 * (x2 - x1))) y2 = min(H, int(y2 + 0.1 * (y2 - y1)))

框外扩的逻辑:车牌字符识别需要一点边距,OCR卷积核才能在字符和背景之间学到过渡特征,紧贴的框等于把边距信息裁掉了。这个技巧在树莓派和RK3568这类边缘设备上尤其管用,不增加算力成本。

6.2 用视频回放验证端到端稳定性

单张图验证容易产生幸存者偏差,我一般会让现场录一段10分钟的视频,跑完推理后用脚本统计每帧的检测框中心点坐标变化。车牌检测框在视频里应该稳定平滑,如果框的宽度在连续帧里忽大忽小,多半是样本里倾斜和远近变化不够,需要回训练环节补数据。视频回放验证还能顺便测OCR的帧级错误率,比人工盯屏幕靠谱。

6.3 树莓派与RK3568上的轻量化导出

树莓派和RK3568这类设备上,导出ONNX后做INT8量化,车牌检测这类单类别任务精度损失通常可接受。模型尺寸上优先yolov5n,实测帧率比yolov5s高一倍以上。导出命令:

python export.py --weights best.pt --include onnx --simplify --opset 12

之后用onnxruntime推理,conf阈值沿用训练完扫描出来的值,不要重新拍脑袋设。ROS无人小车和锥桶检测项目里也都是同样的套路:先验证模型,再谈部署优化。

写到这我想起一个教训:第一次做车牌识别时我把全部精力花在调YOLOv5超参数上,结果现场漏检的根因是摄像头安装仰角太大,车牌在画面里一直是侧倾状态。后来把预处理和检测框外扩加上,识别率才真正上去。希望帮到你。

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

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

长沙曾食坊小吃培训的果茶与糖水:饮品线的产品结构

本篇要点: 1. 茶底与果料搭配;2. 糖度分级与冰量;3. 糖水熬煮与冷藏。果茶和糖水是饮品线里的两类货,结构搭好才能既好看又走量。本文补的是冷饮在产品结构上的那一层:从茶底怎么选、糖度怎么分级,到糖水熬…

作者头像 李华
网站建设 2026/10/4 4:43:57

华为硬件逻辑岗笔试核心考点解析:数字电路与Verilog

经常有准备校招的同学跑过来问我:华为硬件逻辑岗的笔试到底考什么?怎么准备?说实话,这个岗位的笔试范围其实没有外界传的那么玄乎,核心就三条线:数字电路基础、Verilog代码能力、时序与CDC概念。往深了说&a…

作者头像 李华
网站建设 2026/10/4 4:43:02

从Agent失忆到语义记忆:如何构建企业级Agent记忆层?

最近在复盘几个已经跑完或者半路夭折的AI Agent项目时,我越来越清晰地看到一个扎心的规律:大家刚开始拼的都是模型选型、Agent框架、Prompt工程,但拉到三个月、半年甚至一年的时间维度上,真正把项目拖垮的,往往不是模型…

作者头像 李华