资讯动态

YOLOv5+PPOCR两段式车牌识别系统落地实战

发布时间:2026/10/18 20:18:34 来源:尧图企业网站定制
直接把车牌识别系统塞进业务流里这事儿最近算是摸出了点门道。用YOLOv5抓车牌的位置再用PPOCR抠文字这种组合拳在实战中确实够顶。咱们先看个直观的例子一辆车从进停车场到抬杆放行从摄像头抓拍到文字输出整个过程能控制在300毫秒以内而且不是实验室里的漂亮数据是那种刮风下雨、车牌歪斜、泥点子糊半边的情况下跑出来的真实表现。这套流程我梳理过好几遍也帮朋友调过几个项目从选型到落地踩了不少坑今天干脆把整个实现思路和关键细节摊开聊一聊给打算往业务里塞识别能力的你一个参考。先说结论YOLOv5负责的是“车牌在哪”PPOCR负责的是“车牌是啥”两者各干各的活儿再用一层薄薄的调度代码把它们串起来。这种分工方式最大的好处是不用死磕一个模型去同时解决检测和识别两个问题训练压力小、出错的概率也低出了问题还好排查——识别结果不对就单测OCR位置框歪了就单查检测器不会两头抓瞎。这套方案适合谁主要是那些想在自己的管理系统里加一个车牌识别入口的开发者不管是停车管理、门禁通道、物流园区的车辆登记还是单纯的车辆进出记录都可以用这套逻辑快速搭起来。哪怕你之前只写过业务代码没正经训练过深度学习模型也可以按下面的步骤跑出一个能用的版本。1. 方案选型与整体架构拆解1.1 为什么是“检测识别”两段式而不是一个端到端模型最开始我也图省事想找个模型直接输入图片、输出车牌文字一步到位。但试过几个号称端到端的方案之后发现现实场景里的问题多得让人头皮发麻车牌角度偏了识别不出来、夜间反光直接翻车、新能源车牌那8位字符经常被吞尾数。后来回头用“检测识别”的两段式一下就舒展了。两段式架构的核心逻辑是把任务拆成两个独立的子问题检测阶段YOLOv5在整张图中找到车牌所在的矩形区域输出坐标框。它不管框里是什么字只负责定位。识别阶段PPOCR把检测框裁剪出来的小图进一步识别成具体的字符序列。它不关心车牌原来在画面中的哪个位置。这个拆法在工程上有几个实打实的好处。第一两个模型可以独立迭代——现场发现定位不准就只调检测模型发现字符认错就只调识别模型互不干扰。第二调试省力输出结果可以直接可视化分阶段查看哪一环出了问题一目了然。第三性能好优化检测模型跑整图但网络轻快识别模型只跑小图但精度要求高两个模型各自憋大招比混在一起容易调得多。做个简单类比这就好比公司门口的保安和前台。保安YOLOv5不需要认识每个访客是谁他只需要判断“这人是不是该拦住问一下”把人带到一个位置真正核对身份、做登记的活儿是前台PPOCR干的。如果让保安既认脸又查名单他肯定忙不过来而且容易出错。1.2 模型选型对比YOLOv5系列哪个版本够用YOLOv5本身按规模分成n/s/m/l/x几个版本核心区别是网络的深度和宽度。在车牌识别这个场景里没必要一上来就上最大号的x模型很多情况下s或者m已经绰绰有余。在GPU服务器上做推理我实测过一组数据基于同样的训练集和测试集模型版本推理耗时单张1080PmAP0.5显存占用适用场景YOLOv5n约8ms中等极低嵌入式设备、树莓派YOLOv5s约12ms高低普通CPU/低端GPU、实时性要求高YOLOv5m约18ms很高中等通用服务器精度优先YOLOv5l约30ms很高较高离线处理、复杂场景车牌检测本身目标较大、特征明确蓝底白字、绿底黑字、黄底黑字颜色对比强烈不需要特别深的网络去提取细粒度特征。用s版本在1080P图上跑单张推理也就12毫秒左右精度已经非常能打了。如果只需要识别出入口的单车道、单摄像头场景s版本可以无脑冲要是大门口多车道、多车辆同时入画可以上m版本增加一点容错。注意别迷信“模型越大越好”。大模型训练成本高、推理慢而且在车牌这种特征相对规整的任务上提升可能就一两个百分点但对硬件的要求翻一倍不划算。1.3 PPOCR为什么适合做车牌文字识别PPOCR是开源OCR工具包它内部的识别模型基于CRNN结构卷积循环神经网络CTC解码对不定长的文字序列识别是看家本领。车牌字符虽然长度固定蓝牌7位、绿牌8位但字体、排列、脏污情况多种多样CRNN的容错性比纯CNN分类器好得多。另外PPOCR现成的中英文模型已经涵盖数字、字母和常用汉字车牌里的省份简称京、沪、粤、苏这些大多数都在基础词表里不用额外训练也能识别出来。除非你的场景里有特殊字符或者生僻字否则可以直接拿预训练模型用。PPOCR还提供了可调节的推理参数比如文本框角度校正适用于歪斜车牌、置信度阈值过滤丢掉识别置信度太低的框等可以配合业务逻辑做二次校验。我在实际项目中就试过用PPOCR识别一张倾斜30度的车牌配合角度分类和矫正之后准确率大幅提升。2. 环境准备与数据构建2.1 环境搭建清单环境这块其实不用太纠结只要保证版本兼容就行。我个人的推荐组合是Python 3.8 或 3.10不建议太新部分框架对3.11的支持还不稳PyTorch 1.10 以上YOLOv5官方代码对PyTorch版本有要求直接装最新稳定版即可PaddleOCR直接pip安装注意它依赖的PaddlePaddle版本选CPU版还是GPU版根据机器定OpenCV图像读写和预处理必备安装的时候有个容易踩的坑PaddlePaddle和PyTorch对CUDA版本的要求可能冲突。如果你一块GPU上既要跑YOLO又要跑Paddle建议把两个框架的CUDA版本统一。我遇到过一次PyTorch要CUDA 11.3、Paddle要CUDA 11.2结果两个框架轮番报错最后只能重新建虚拟环境分开装才消停。数据这块车牌数据的来源有几种公开数据集网上有一些车牌图片数据集但质量参差不齐需要自己清洗。自己拍摄/采集在停车场、园区门口架个摄像头录一段视频再抽帧比公开数据集更贴合自己的场景。合成数据用程序生成车牌图片白底黑字、透明背景、随机角度、随机亮度用来做预训练补充。我自己的经验是公开数据打底自己采集的数据做微调合成数据做增强补充三种混着用效果最好。只靠公开数据集有个问题拍摄角度、光照条件和你的现场差别很大模型泛化能力不够只靠自己采集数据量又不够多容易过拟合。2.2 标注格式与数据增强YOLOv5的标注格式是TXT文件每行一个目标格式为类别id 中心点x坐标 中心点y坐标 框宽 框高。坐标都是归一化到0~1之间的浮点数。标注工具可以直接用LabelImg或者现成的在线标注平台。标注的时候有个容易忽略的点车牌框一定要紧贴车牌边缘不要留太多背景更不要框小了截掉边角字符。我刚开始标注的时候对这个不敏感框得松松垮垮的结果训练出来的检测框要么偏大要么偏小裁出来的图喂给OCR各种识别错误。后来重新拉了一遍标注数据把边框贴着车牌四边收紧检测框的稳定性和后续OCR正确率都上来了。数据增强这边YOLOv5自带增强策略mosaic、翻转、色彩空间变换等在超参数文件里可以直接调。对于车牌场景额外的增强还有几种高斯模糊模拟雨天或车速快时画面糊掉的情况。随机亮度调整模拟顺光、逆光、夜间补光。随机透视变换模拟不同角度拍摄的车牌倾斜。提示增强不是越多越好适度增强能让模型见多识广但过度扭曲会让模型学到错误的特征。我一般模糊强度上限控制在3像素以内透视角度最大不超过30度。3. 核心实现步骤与关键参数3.1 YOLOv5车牌检测模型训练下载YOLOv5官方代码仓库把数据集的配置文件和标注准备好之后训练命令基本是这样的python train.py --img 640 --batch 16 --epochs 100 --data plate.yaml --weights yolov5s.pt--img 640训练时把输入图缩放到640x640。车牌是小目标不过也不算极小640的输入足够。--batch 16看显存大小调显存不够就调小到8或者4。--epochs 100其实不需要死板的100可以看训练曲线早停一般60~80轮就收敛了。--weights yolov5s.pt用预训练权重做迁移学习起点收敛更快。训练过程中有几个关键指标要盯着box loss位置回归损失和cls loss分类损失平稳下降如果训练后期还在剧烈震荡大概率是学习率没调好。mAP0.5和mAP0.5:0.95前者关注框定位是否大概正确后者更严格要求框定位精确。车牌场景下mAP0.5达到0.98以上才算过关mAP0.5:0.95能到0.9以上就很好了。训练完之后导出的模型有两种用法一种是直接Python调用另一种是导出为ONNX或者TensorRT格式部署到生产环境。如果你只是自己写个demo接接口直接用PyTorch权重就够了如果要做高并发推理我建议导出TensorRT在GPU上的推理速度能再提升一倍。3.2 PPOCR车牌文字识别集成PPOCR的调用方式很简洁from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) result ocr.ocr(img_cropped, clsTrue)这里use_angle_clsTrue是关键它会额外启用一个角度分类器判断文本是否倒置必要时做180度旋转矫正。车牌照片如果是从闸机俯拍下来的基本不存在倒置问题但如果你从不同方向拍摄比如移动端巡检这个参数建议保持开启。PPOCR输出结果的格式是嵌套列表每个元素包含文本框坐标、识别文本、置信度。在车牌场景中我们需要提取的是整张裁剪图里置信度最高的那一条文本因为车牌区域理论上只应该有一行文字。PPOCR有一个重要调参点text_threshold文本检测置信度阈值和low_text低文本阈值。默认参数是通用场景的配置车牌这种文字少但结构固定的图像可以把text_threshold从默认的0.5调到0.7以上减少误检如果发现车牌上的字被漏掉再把low_text从默认的0.4往下调到0.3。3.3 检测结果裁剪与识别联动整个串联流程的伪代码逻辑是这样的import cv2 import torch # 加载YOLOv5模型 model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) # 加载PPOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) def recognize_plate(frame): # 1. 检测 results model(frame) boxes results.xyxy[0].cpu().numpy() # 坐标 置信度 plates [] for box in boxes: x1, y1, x2, y2, conf, cls box if conf 0.5: continue # 2. 裁剪 crop frame[int(y1):int(y2), int(x1):int(x2)] # 3. 识别 ocr_result ocr.ocr(crop, clsTrue) if ocr_result and ocr_result[0]: texts [line[1][0] for line in ocr_result[0]] plates.append((texts, conf)) return plates这个串联流程看起来简单但有两个容易被忽视的坑第一个坑裁剪图的尺寸太小。如果原图里车牌只有30x10像素裁剪出来直接丢给PPOCR识别效果会很差。正确的做法是先把裁剪区域resize到一个合理尺寸比如宽度拉到200像素保持长宽比再做识别。PPOCR内部会再做归一化但一个有足够像素的输入还是能显著提高准确率。第二个坑YOLOv5模型默认输出的坐标可能是float。裁剪的时候如果直接用float切片OpenCV会报错或者裁出奇怪的结果必须转成int再切。3.4 业务流集成与前后端通信识别结果拿到之后怎么塞进业务流程里是个容易“想简单了”的环节。我见过不少人把识别代码直接写在业务接口里每次请求都现场推理结果接口响应速度被拖垮。正确的做法是把识别服务独立出来做成常驻进程或独立服务业务侧通过HTTP或消息队列调用。大致架构是这样入口摄像头或图片上传接口把抓拍图片发给识别服务。识别服务内部维持YOLOv5和PPOCR两个模型的常驻实例接收图片后执行检测识别返回JSON结果。业务系统拿到车牌字符串后做自己的逻辑查数据库、比对黑名单、计算停车时长、控制道闸开关。这样做的好处有三个模型加载只需要一次不用每个请求都去加载权重识别服务可以单独扩容OCR处理不过来就多起几个实例业务系统升级不用碰识别代码解耦干净。我在实际项目里做的一个接口响应时间统计基于一台普通GPU服务器型号就不具体说了阶段耗时图片传输局域网10~20msYOLOv5检测12~15msPPOCR识别25~40ms数据回传5~10ms总计约50~85ms这个速度对于停车场的道闸控制来说绰绰有余实际体验是“摄像头抓拍到抬杆”几乎没有延迟感。4. 常见问题与排查技巧实录4.1 车牌检测不到或漏检这里说的“漏检”指的是YOLOv5没有输出车牌框。常见原因和排查思路如下原因一训练数据里缺某种类型。比如你的训练集全是蓝牌现场突然来了一辆新能源绿牌车模型可能就傻眼了。排查方式是打印检测结果的置信度看是不是绿牌车置信度极低比如0.1以下。解决办法是在训练数据里加大绿牌、黄牌、白牌警车的比例。原因二输入分辨率太低。YOLOv5默认推理尺寸是640x640但如果摄像头画面里车牌离得远车牌在画面里只占了几十个像素模型根本看不清。解决方法是把推理尺寸提高到960甚至1280或者在上游对画面中车辆区域做一次ROI放大后再送检测。这个也要提醒一下提高推理尺寸会带来推理耗时增加如果对速度敏感可以考虑在画面中检测“车辆”这个大目标然后只对车辆区域放大做“车牌”检测形成两级检测速度和精度都能兼顾。原因三图像过于模糊。运动模糊、夜间噪声、雨天水滴都可能导致检测不到。最简单有效的办法是在送入模型之前做一次轻度锐化比如OpenCV的filter2D加一个锐化核大部分情况能救回来。4.2 PPOCR识别结果错字或漏字识别这一环的坑比检测更多具体有几种典型表现。漏掉首字符或尾字符一般是边缘字符被裁掉。这个问题的根子在YOLOv5的检测框不够紧贴把车牌边缘的边框或螺丝钉也框进来了裁剪图的边缘噪点过多PPOCR把边缘字符当噪声丢弃了。解决办法优化检测框紧密度重新标注数据是最根本的或者对裁剪图先做去边框预处理——简单判断像素颜色把外围一圈非车牌底色的边框裁掉。省份汉字识别错误。PPOCR的基础中文模型能识别常见汉字但对于“皖”“赣”“渝”这类省简称偶尔会认成形状相近的字比如“皖”认成“晚”。遇到这种情况可以在业务逻辑里做一个车牌类型白名单校验识别结果的前缀必须是已知的省份简称集合如果不是再用图像相似度匹配或者直接映射到最接近的省份简称。这是成本最低、效果最明显的后处理技巧。数字和字母混淆比如0和O、1和I。车牌字符集本身就存在相似字形OCR模型难免混淆。解决思路是引入车牌字符集约束在识别结果的后处理中把不合法的字符替换为最相近的合法字符比如车牌里一般只有数字0、没有大写O字母I通常也不会出现在车牌里。PPOCR本身不提供这个逻辑需要自己在业务代码里写一层正则替换。4.3 推理速度慢达不到实时要求如果推理速度不达标优先检查瓶颈在检测还是识别。简单的方法是分别打印两个模型的耗时然后对症下药。检测慢的优化思路换更小的模型从m换到s从s换到n。降低推理尺寸从960降到640但注意精度损失。导出TensorRT引擎用半精度FP16推理可以获得显著加速。识别慢的优化思路PPOCR的检测模型DB和识别模型CRNN可以分离使用如果确认车牌子图已经是干净的裁剪图可以跳过文本检测只跑识别模型速度能提升40%以上。减少PPOCR内部的多尺度检测直接固定输入尺寸。批量推理如果有多个车牌要识别攒一批再一起送入模型GPU利用率更高。4.4 夜间或逆光场景效果差夜间场景是车牌识别的老大难。常见问题包括车牌区域过暗、车灯直射导致曝光过强、图像噪点多。我试过的有效方案有图像预处理做自适应直方图均衡化CLAHE可以明显提升暗部细节。注意别用全局直方图均衡化会把亮部洗白。多帧融合连续抓拍3~5帧取车牌区域亮度和清晰度最高的那一帧做识别比单帧识别稳得多。偏振摄像头如果现场环境固定、灯光可控加装偏振镜头可以有效消除车灯反光。这个属于硬件层面的优化效果一劳永逸。4.5 业务集成时的接口稳定性问题识别服务作为独立进程长期运行会遇到显存泄漏、CUDA错误等问题。我自己的运维经验是定期重启策略每天凌晨低峰期自动重启一次识别服务可以避免长时运行后显存碎片化导致OOM。设置超时和重试业务侧调用识别接口时必须有超时限制和重试机制。识别服务偶尔会因为并发过高返回超时不能因为一次失败就导致整个道闸流程卡死。记录原始图片在识别失败时保存原始帧到本地方便事后定位问题。我一般会定期翻看失败样本发现数据分布变化了就重新微调模型。5. 经验总结与扩展方向5.1 实际落地时的一个隐性成本数据维护前面讲了很多技术实现但真正让系统长期稳定运行的关键其实是数据闭环。模型在初期训练之后到了现场跑一段时间一定会遇到训练数据里没见过的场景新角度、新光照、新装饰框。如果你不持续收集这些失败样本并回流到训练集系统的识别率会随着场景变化不断下滑。我的做法是搭一个简单的“失败样本自动收集”流程识别置信度低于某个阈值比如0.6就把图片存到一个特定文件夹每周人工过一眼筛出有价值的数据补充标注、增量训练。这个流程看起来不起眼但能让你识别系统的准确率越用越高而不是越用越差。5.2 还能往哪些方向扩展这套检测识别的架构其实不只是车牌能用。思路完全一样换一下训练数据就能迁移到别的场景集装箱箱号识别物流园区里集卡进闸识别箱号。车架号/VIN码识别车辆检测站或二手车平台。道路指示牌识别巡检车自动采集道路设施信息。托盘码/物料码识别工厂内部物流自动化。这些场景的共性都是“一个明显的标志物一段有规则的文字”YOLOv5定位PPOCR识别的组合都能套用。5.3 一个细节建议输出结果的置信度分层业务对接时别只把“识别出来的字符串”传给上层建议把置信度、识别耗时、裁剪图大小这些元数据一起传。这样做的好处是上层可以做差异化处理高置信度结果直接放行低置信度结果推送给人工复核而不是把所有结果一视同仁。在停车场的实际运营中人工复核的量大概只有1%不到但能兜住那最后的0.5%的极端情况。这套方案本身不神秘难的是把每个环节都做到位数据标得准、模型调得稳、服务接得顺、数据回流做成习惯。YOLOv5和PPOCR都是成熟的工具哪一步出了问题回头按照这篇文章里的排查思路捋一遍基本都能救回来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑