十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于YOLOv8+LPRNet的中文车牌识别停车场管理系统

基于YOLOv8+LPRNet的中文车牌识别停车场管理系统 简介基于深度学习的中文车牌识别与管理系统完整代码包面向需要快速搭建车牌检测识别功能的开发者、研究人员及课程设计学生。项目以Python和PyQt5实现桌面端图形界面支持上传图片、视频、批量图片以及调用电脑摄像头进行实时检测并配套历史识别记录管理模块界面美观易操作。压缩包共333个文件大小约422.4MB以234张jpg测试图片、38个whl依赖包、6个py源码文件为主同时包含UI界面文件、模型配置文件及测试视频等资源涵盖从环境配置、模型运行到界面交互的完整链路开箱即可使用。目前已有5016人浏览学习代码结构清晰适合新手逐行阅读也便于在此基础上扩展优化是实战入门与毕设参考的优质素材。1. 为什么停车场场景的车牌识别比想象中难一个数量级1.1 从一次误锁车事件说起识别错一个字符的代价我第一次把基于深度学习的中车牌识别项目从 demo 搬到真实停车场时最让我意外的不是模型精度而是用户对错误车牌号的反应。停车场入口明明抓拍得很清楚的车牌系统却把“苏B·5E30M”看成了“苏B·5E30W”车主当场就找物业投诉。后来我复盘才发现真正麻烦的不是识别算法本身而是如何把深度学习模型的输出变成一套稳定、可用、有管理后台支撑的业务系统。这件事给我上了很深刻的一课车牌识别项目里1% 的识别错误率不是 1% 的代价而是 99% 的信任崩塌。用户不关心你的模型在 10000 张测试集上做到多高的准确率他们只关心为什么自己那辆车被系统认错了。所以做这类管理系统模型精度只是起点业务逻辑、容错机制和数据闭环才是真正拉开差距的地方。这也是为什么标题里把“识别”和“管理系统”放在一起而不仅仅是讲一个算法模型。1.2 中文车牌识别到底难在哪里单看“识别车牌号”这个动作好像只要把图片丢给一个现成模型就行。但中文车牌和欧美车牌有一个本质差别字符集里的内容是高度结构化、有国标约束的同时又允许出现颜色、字体、规格上的巨大变化。国内常见的车牌至少包括蓝牌、新能源绿牌、黄牌、白牌等底色而双层黄牌和单层蓝牌的高度比例完全不一样这会直接影响图像预处理和模型输出序列的设计。更重要的是中文车牌里的汉字是省份简称总共涉及 31 个省级行政区笔画复杂度远高于英文字母字母和数字里又有“O”和“0”、“I”和“1”这类容易混淆的形态国标里不允许它们出现在车牌上但现场拍到的图像里什么情况都会发生。这意味着你不能把车牌识别简单当成一个普通 OCR 任务得结合格式校验、字符约束和颜色判断来设计一套完整方案。理解了这个前提你才能明白为什么整个系统要拆成多个环节协作而不是一个模型“一把梭”。2. 整体架构与技术选型识别能力与管理后台解耦2.1 一条识别流水线 一个管理系统的模块划分我最初做这个项目时把识别模型直接写在 Flask 路由里管理后台也放在同一个进程里跑结果模型推理稍一卡顿后台接口就跟着超时整个演示当场翻车。后来我按照比较稳妥的分层思路把项目拆成两大部分识别服务和管理系统。识别服务只负责接收图像、执行识别、输出车牌号管理系统负责车辆档案、进出记录、黑白名单和异常处理通过 HTTP 接口调用识别服务。这两部分从代码层面完全解耦意味着识别模型可以随时替换。今天用 YOLOv5明天换 YOLOv8后天换成更快的 ONNX Runtime 版本只要识别接口的返回格式不变管理系统一行都不用改。反过来说管理系统的业务规则再复杂也不会影响模型训练和推理性能。对多人协作的项目或者需要当毕业设计演示的项目这种分层也方便分工有人专注模型训练有人专注后台开发互不阻塞。2.2 为什么选 YOLOv8 LPRNet而不是一步到位识别这里先把核心结论放出来识别流水线采用“YOLOv8 检测车牌区域 透视校正 LPRNet 端到端字符识别”管理后端使用 FastAPI 提供接口数据库用 MySQL后台前端用 Vue3 Element Plus队列用 Redis。这套组合不是唯一解但我在实际对比后它是在效果、开发速度和部署成本之间最平衡的方案。为什么不直接用一个大模型完成检测和识别市面上确实存在端到端的车牌识别模型但很多在处理中文车牌字符顺序时容易出错更关键的是检测和识别耦合在一起后没办法针对倾斜车牌单独做透视校正。把流水线拆开每个环节都能单独调试、单独替换。出现识别错误时也能快速定位是检测框不准、校正失败还是字符识别错乱。下面这张表大概列出了两种路线的差异对比维度一步到位识别模型检测校正识别流水线开发效率无需组装开箱即用需要串联三个模块倾斜车牌处理依赖模型硬学透视校正后识别更稳错误定位只能整体调模型分环节独立调优替换成本换模型风险大单模块可替换最终效果依赖单一模型上限每步可控上限更高综合下来我推荐流水线方案。它看起来多两步但每一步都在降低系统的不可控因素。少踩坑的快乐等你部署到真实场景就会体会到了。2.3 项目目录结构设计为了让模块边界更清晰我在工程里按下面的方式组织目录。这个结构不复杂但每个人接手项目时都能一眼看出哪个目录对应什么职责。license-plate-system/ ├── detection/ # YOLOv8 车牌检测训练与推理 │ ├── train.py │ └── infer.py ├── recognition/ # LPRNet 字符识别训练与推理 │ ├── data_preprocess.py │ ├── lprnet.py │ └── train.py ├── server/ # FastAPI 识别服务 │ ├── main.py │ ├── pipeline.py # 检测-校正-识别串联 │ └── onnx_export.py ├── management/ # 管理后台Vue3 Element Plus │ └── src/ ├── mysql/ # 建表 SQL 与数据访问层 │ └── init.sql └── docs/这样的目录划分回头维护的时候特别省心。如果只想跑通识别效果可以先不看 management 目录如果只做后台功能可以 mock 掉识别接口返回的数据。3. 识别流水线的三个关键环节检测、校正、识别3.1 用 YOLOv8 定位车牌是整套系统的第一步在整帧图像里直接做字符识别不现实车牌通常只占图像很小一块背景干扰又多。所以第一步必须在整幅图里找到车牌位置。这里用 YOLOv8是因为它在目标检测任务里对中小目标的表现足够稳定而且训练、导出和部署都比较方便。训练阶段我把数据集里的车牌统一标注成单个类别 license_plate。推理时从模型输出里筛选置信度大于 0.45 的框再把框内图像裁剪出来作为后续字符识别的输入。核心代码大概长这样from ultralytics import YOLO det_model YOLO(weights/plate_det.pt) results det_model(frame, conf0.45, verboseFalse) for box in results[0].boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) conf float(box.conf[0]) plate_crop frame[y1:y2, x1:x2]这个阶段最容易被忽略的细节是检测框包住的应该是“车牌整个区域”而不是仅仅压住字符。现场摄像头角度五花八门检测框里常常会带上保险杠、螺丝、车漆反光这些背景。框略大一点问题不大但略小就会切掉车牌边角直接影响后面的字符识别。所以我的标注规范是“框要完整包住车牌四周边框宁大勿小”。3.2 透视校正把歪斜的车牌“扶正”车牌不是总对着摄像头正面的。停车场入口多少都有角度高速卡口更是俯拍加斜拍如果不做校正直接把倾斜图像缩放到 94x24 送进识别模型字符会被压得畸变识别率下降非常明显。我处理倾斜的方式是让检测模型在输出车牌包围框的同时也输出车牌四个角点位置再根据角点做透视变换。这里补充一点有的项目偷懒直接把检测框当成矩形车牌裁剪再 resize 到固定尺寸。这种做法在偏角小于 15 度时勉强能用一旦车头斜着停、摄像头装在侧面结果会非常差。比较稳的做法是在检测分支上增加车牌四角点回归拿到角点后构造原图坐标和目标正矩形坐标的映射用 OpenCV 的透视变换把它们投影到规整视角。import cv2 import numpy as np src_pts np.array(pts, dtypenp.float32) # 四角点按左上、右上、右下、左下 dst_pts np.array([[0, 0], [440, 0], [440, 140], [0, 140]], dtypenp.float32) matrix cv2.getPerspectiveTransform(src_pts, dst_pts) warped cv2.warpPerspective(plate_crop, matrix, (440, 140))透视校正之后车牌图像基本变成“正对镜头”的矩形后面的识别模型只需要处理正常的字符形态不需要用大量算力去学习不同视角下同一个字符的长相。这一步在我自己的验证集上大约提升了 3-5 个百分点的识别准确率投入产出比相当高。3.3 LPRNet 端到端字符识别把车牌字符串读出来校正后的车牌图像会统一缩放到 94x24然后送入 LPRNet。LPRNet 的全称是 License Plate Recognition Network它不是先把字符切开再逐个识别而是把整张车牌图像当作一个序列去解码配合 CTC Loss 输出字符串。为什么这里不用 YOLO 继续做字符检测因为车牌字符数量固定、排列紧密逐个标注字符的边界框成本很高字符彼此粘连又多检测时很容易漏框。LPRNet 这种端到端序列识别的方式更贴合车牌场景输入规整车牌图经过 CNN 提取特征再用序列建模输出每个时间步的字符概率分布最后用 CTC 解码出最终字符串。训练时只需要车牌级别标注不需要手动框每个字符省了不少功夫。训练时使用的字符集决定了模型能识别什么内容。我的字符类别设计是省份汉字 31 个、大写字母 24 个、数字 10 个共 65 类。新能源车牌的字符位比普通蓝牌多一位所以我把模型输出序列长度设置为 8让 7 位蓝牌和 8 位绿牌都能兼容。CTC 解码时会出现重复字符之间的空白分隔问题训练样本里要保留字符自然间隔不能把字符压得太满。4. 训练数据和字符集的准备决定模型上限的体力活4.1 CCPD 公开数据集与自采数据的互补模型训练最缺的始终是数据。中文车牌识别领域有一个公开度很高的数据集 CCPD包含大约 25 万张真实场景下的车牌图片覆盖了不同光照、角度、模糊程度。不过 CCPD 有一个明显局限车牌所在地基本是安徽省份汉字只有“皖”如果模型只用它训练识别“苏”“沪”“京”这些汉字时几乎等于没见过。所以实际训练时我采用“CCPD 自采数据 数据增强”的组合。自采数据不一定要去马路上蹲点拍一整天可以把自己所在城市的停车场出入口视频抽帧后筛出车牌也可以从合规开源渠道收集一些不同省份的车牌图片。筛选原则很简单人工都看不太清的图片直接删掉车牌只露出一半的也删掉避免给模型传递错误信号。数据按 8:1:1 划分训练集、验证集、测试集验证集和测试集尽量保证省份汉字分布和真实场景一致。训练时我会用到一组比较成熟的超参数输入高度 24、宽度 94批大小 64初始学习率 1e-3训练 100 轮左右。下面是一个简化版的训练入口示意# 训练关键配置 config { input_height: 24, input_width: 94, max_seq_len: 8, batch_size: 64, epochs: 100, lr: 1e-3, train_dir: datasets/train, val_dir: datasets/val, }模型训练到后期重点观察验证集上每个字符类别的准确率而不仅仅是整串车牌的准确率。因为整串准确率会被高频字符主导低频汉字一旦出问题很容易被掩盖。4.2 字符类别设计省份汉字、字母、数字的取舍LPRNet 这类识别模型的字符集设计直接决定最后一层全连接的输出维度。字符集不能拍脑袋写必须和真实车牌国标对齐。中国车牌第一位是汉字省份简称第二位是发证机关字母后面是字母和数字混合新能源绿牌比普通蓝牌多一位且末尾通常是 D 或 F 字母。因此我的字符集设计如下类别内容数量省份汉字京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新31英文字母ABCDEFGHJKLMNPQRSTUVWXYZ去掉 I、O24数字0-910空白符CTC Blank1总计66这里有个很重要的设计决策字母集合里故意去掉 I 和 O。因为国标车牌不允许出现这两个字母而它们在图像上和数字 1、0 几乎无法可靠区分。与其让模型反复纠结不如把 I 归入 1、把 O 归入 0后续再用业务规则兜底。同样的新能源末尾的 D、F 一定要出现在字符集里否则 8 位车牌识别时末尾字符天然会出问题。4.3 混淆字符的预防与规则校验兜底模型在模糊、过暗、强反光情况下最容易把“B”认成“8”把“2”认成“Z”把字形接近的“渝”和“沪”弄混。增加训练样本能改善但做不到 100% 消除所以系统上要加一层规则校验。规则校验的逻辑很简单车牌字符串不是随意生成的它有严格格式。第一位必须是省份汉字第二位必须是大写字母后续普通蓝牌是 5 位字母数字新能源绿牌是 6 位且末位多为 D 或 F。用正则表达式可以快速检查import re pattern re.compile( r^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新] r[A-Z][A-Z0-9]{5}[DF0-9]?$ ) def is_valid_plate(s: str) - bool: return bool(pattern.match(s))一旦校验不通过我不会直接把结果写进数据库而是触发“二次处理”把原图按不同亮度、对比度各处理一遍再识别或者直接标记成待人工复核。这套规则校验在真实项目里能拦截掉约 2% 的明显错误识别结果。比例看着不高但在真实业务里它省掉了大量和车主扯皮的售后成本很值得做。5. 管理系统落地从模型推理到业务闭环5.1 模型导出与推理服务封装训练好的 PyTorch 模型不能直接扔给生产环境Python 环境依赖重、启动慢、并发能力也一般。通常做法是先导出成 ONNX 格式再用 ONNX Runtime 加载推理如果要跑到 Jetson 这类边缘设备再进一步转成 TensorRT 引擎。我在项目里先把检测模型和识别模型都导出为 ONNX然后用 FastAPI 封装一个独立识别服务。服务对外只需要暴露两个接口单张图像识别接口接收上传图片返回车牌号、置信度、检测框坐标健康检查接口用于管理后台探活。下面是接口框架from fastapi import FastAPI, UploadFile import onnxruntime as ort app FastAPI() det_session ort.InferenceSession(plate_det.onnx) rec_session ort.InferenceSession(lprnet.onnx) app.post(/api/recognize) async def recognize(file: UploadFile): data await file.read() # 这里串联检测、透视校正、字符识别 return {plate: result, confidence: conf, box: [x1, y1, x2, y2]}把识别服务独立出来后管理后台只关心业务逻辑完全不需要感知模型细节。后续模型迭代时只需要重新导出 ONNX 文件并替换服务代码一行不用改。5.2 管理后台功能车辆建档、进出记录、黑白名单管理系统的核心功能在我看来有三块。第一块是车辆档案管理员可以手工录入车牌号、车主姓名、手机号、车位号第二块是进出记录每次识别结果和现场抓拍图都落库支持按车牌号、时间范围查询第三块是黑白名单黑名单车辆识别后只记录不放行白名单车辆直接放行并推送通知。数据库表我设计得尽量简单三张主表加一张用户表就够跑完整流程表名关键字段作用tb_vehicle_infoplate_no, owner_name, phone, parking_no车辆档案tb_access_recordplate_no, access_type, confidence, image_path, create_time出入场记录tb_blacklistplate_no, reason, create_time黑名单tb_userusername, password_hash, role后台登录账号这里有个关键联动点识别结果回来后系统不能只当日志存起来还要根据车牌去查车辆是否已登记、是否在黑名单再决定道闸要不要抬。所以我在业务层做了“入场登记”和“出场结算”的逻辑识别服务返回车牌字符串业务层再查 MySQL把判定结果返回给前端和硬件控制端整个闭环才算打通。5.3 高并发下的处理队列设计最开始我的识别接口是同步处理的结果车辆高峰时几台车同时涌入一个请求处理 300 毫秒后端线程池一满其他请求就开始排队超时。后来我在中间加了一层 Redis 队列抓拍机把图像推入队列识别服务作为消费者异步处理处理完再把结果写回 MySQL同时通过 WebSocket 推送给前端显示。这个设计让系统吞吐量提升了一个量级识别服务也不会被突发流量打挂。如果后续车辆规模更大可以把 Redis 队列换成更正式的消息队列不过停车场出入口的并发规模Redis 已经非常够用。用队列的一个额外好处是识别服务如果暂时故障图像请求还在队列里等着服务恢复后可以继续处理不会直接丢失数据。6. 部署过程中踩过的坑与性能调优记录6.1 新能源车牌长度不一致带来的识别错位问题最早训练模型时我只考虑了普通蓝牌的 7 位字符序列长度固定为 7结果前端一接入新能源绿牌就频繁识别错乱最典型的是把 8 位车牌识别成 7 位末尾的 D 或 F 被吞掉。这个坑比较隐蔽因为单独看中间几个字符模型输出是对的但整体车牌串就是错位了。解决思路有两层。第一层训练阶段把输出序列长度放宽到 8样本里同时包含 7 位蓝牌和 8 位绿牌让模型学会用 CTC 的空白符来兼容不同长度。第二层在业务规则里加一个前置判断先通过车牌区域的颜色特征区分是蓝牌还是绿牌再决定后续字符校验位数。颜色判断对绿牌特别有效绿色背景的 HSV 特征很稳定几行代码就能完成不需要引入额外模型。6.2 夜间和逆光场景下的图像质量处理停车场夜间场景基本靠出入口的补光灯。补光灯太强车牌反光导致字符被削掉补光灯太弱整幅图黑成一片。我在实际项目里没有过度依赖图像增强算法而是把重点放在数据增强上训练时对车牌图随机调整亮度、对比度、饱和度并加入高斯模糊模拟失焦让模型自己去适应这些情况。如果遇到极端逆光我会在识别前做一个限制对比度自适应直方图均衡化也就是 CLAHE。它对暗部细节的恢复效果很明显但参数不能太激进否则车牌背景会被过度增强产生大量噪点反而让识别率下降。建议把 clipLimit 设置在 2.0 左右效果比较稳。我实测的情况是做了这层增强之后夜间识别率大约提升了 2 个百分点但也不是所有场景都适合加最好通过测试集验证再加进流水线。6.3 让单帧识别从 500ms 降到 100ms 的优化路线最后说性能。初始版本在 CPU 上跑 PyTorch单帧识别耗时稳定在 500ms 以上几乎没有实用价值。我逐步做了三件事把两个模型从 PyTorch 导出成 ONNX用 ONNX Runtime 替代原始推理把图像预处理从 Python 循环改成 NumPy 向量化操作。这一套组合下来单帧耗时直接降到 80-120ms在出入口场景完全够用。如果继续优化优先可以考虑把检测模型输出分辨率从 640x640 降到 480x480减少计算量校正和缩放统一用固定分辨率避免重复申请内存在 Jetson 或 NVIDIA GPU 设备上转 TensorRT推理精度降到 FP16。不过优化的前提是先做性能分析搞清楚瓶颈到底在预处理、检测还是识别上别一上来就盲目换框架否则容易白忙活。最后分享一个我踩过几次坑之后比较深的体会这类系统上线后维护成本最大的往往不是模型本身而是“识别结果不可信时该怎么办”的业务闭环。多留一条人工复核的通道多给系统加几层规则兜底远比单纯把模型指标刷到小数点后第四位有用。车牌识别和管理系统从来不是只跑一个模型那么简单整个链路的稳定性才是它能不能真正落地的关键。本文还有配套的精品资源点击获取
返回列表