基于YOLOv5的车牌识别系统:从模型训练到Web管理端完整实现
简介基于YOLOv5的深度学习车牌识别系统毕业设计论文面向计算机视觉、深度学习方向的本科生以及需要快速构建车牌识别方案的开发者。论文从智能交通背景下传统车牌识别效率低、环境适应性弱的痛点切入系统阐述基于YOLOv5的端到端识别流程覆盖图像采集与预处理、模型训练、车牌定位、字符分割与识别等核心环节并结合Python与OpenCV给出具体设计思路和实验过程。资源包共1个文件为一份1.14MB的docx文档文档包含中英文摘要、目录、绪论及正文章节结构完整可直接作为毕业设计撰写范式或实际项目开发的参考资料。该资源已有1006人学习下载适合用于理解深度学习目标检测技术在车牌识别中的落地方法也可为同类交通场景任务提供算法选型与工程实现参考。1. 基于 YOLOv5 的车牌识别系统一套从训练到后台管理的完整闭环拿手机拍一张带车牌的车辆照片丢进网页等两秒返回一串车牌号再把这条识别结果连同车辆类型、归属地写进 MySQL后台按地图、按图表去统计查询——这就是这份基于 YOLOv5 的深度学习车牌识别系统论文覆盖到的完整链路。它不是只讲算法的教案而是从图像采集、预处理、模型训练、车牌定位、字符分割、字符识别一路落到网页管理端的工程化项目。适合两类人一类是刚做完图像分类、想进阶目标检测的初学者需要一套能讲清毕设设计思路的完整源码和文档另一类是要把车牌识别能力接进现有业务系统的开发需要知道 YOLOv5 推理、OpenCV 后处理和页面展示之间怎么衔接。2. 技术选型与架构解析YOLOv5 负责看Python 和 MySQL 负责管2.1 为什么车牌识别选 YOLOv5 而不是传统识别方案车牌识别看起来是个“见字识牌”的问题但真正做起来要拆成检测和识别两步。传统方案先做边缘检测、颜色分割、投影分析来找车牌位置再到字符区域做模板匹配或分类器步骤多不说每换一个拍摄角度、光照条件中间某一环就会失效。YOLOv5 不一样它是单阶段目标检测模型一张图进去直接输出目标的边界框、类别和置信度端到端地把“车牌在哪”这个问题解决掉这也是最近几年大多数落地项目选择它的原因。具体到车牌这个场景车牌本身结构非常固定文字区域、底色、边框比例都相对规整属于比较典型的小目标检测。YOLOv5 的官方预训练权重就可以在这个任务上快速跑出候选框不需要像两阶段检测网络那样先做区域建议再做二次分类处理速度也有优势。对比一下实际体验同样一块显卡两阶段模型完成一次前向推理可能要上百毫秒YOLOv5s 可以在几十毫秒内完成这在高速出入口、停车场道闸这种需要快速响应的场景里差别很明显。这套毕设里真正值得复用的点在于先用 YOLOv5 完成定位再用传统图像处理做字符分割和识别而不是把整个车牌识别都塞给深度学习模型。这样做的工程意义很实在——训练一个能同时输出车牌位置和字符内容的端到端模型需要的数据量非常大而拆成两段之后检测模型只需要学会找车牌字符识别可以放在后处理里用轻量方式解决训练成本和出错排查成本都低很多。2.2 Python 生态与 MySQL为什么这套组合适合落地项目正文里反复强调 Python 在开发中的价值这句话放在车牌识别系统里不是套话。从图片处理到模型推理再到 Web 后端接口一套环境走到底不用切换语言这是最省事的地方。OpenCV 负责图像的读取、缩放、滤波和二值化Pandas 负责识别结果数据的整理和统计分析PyTorch 负责加载 YOLOv5 权重做前向推理。Python 的自动内存管理也省掉了手动释放缓存这类琐碎工作对毕设这种时间紧的任务来说很友好。数据库选 MySQL理由原文写得很清楚开源免费功能完备对没经历过大型项目的人来说简单易上手。实际开发中车牌识别系统要存的无非是用户账号、车辆图片路径、识别结果、上传时间这几类业务数据MySQL 的关系表完全够用。相比 Oracle 这类重型数据库部署成本低相比需要特定认证环境的国产数据库在没有信创要求的前提下更容易找到参考资料。表结构设计上只要能按 E-R 图把管理员、车辆信息、账号这几个实体关系理顺就不容易出现数据冗余和垃圾数据。2.3 系统功能模块与整体调用关系从原文的功能模块图看这套系统的主干拆成“首页、车牌识别、用户管理、统计分析、车牌地图、管理员、车牌管理”这几块。首页通常展示当前用户数和车牌数量概览车牌识别页负责上传图片并展示识别结果统计分析页把蓝牌、绿牌等不同车型的识别数量用图形呈现车牌地图页用中国地图展示不同区域的车牌分布车牌管理页则把历史识别记录以列表形式输出。模块之间的调用关系是典型的 B/S 结构。浏览器端上传一张车辆图片后端 Python 服务把它接住先调用 YOLOv5 模型完成车牌定位再交给 OpenCV 后处理做字符分割和识别最终把车牌号连同车辆类型、归属地、上传日期写入 MySQL。前端想看到的统计图表和地图数据也都是从数据库表里聚合查出来的。这样一来识别能力、数据存储、页面展示三层职责清楚出问题时按链路逐段排查就行。3. 核心模块拆解从图片上传到车牌号输出的完整识别链路3.1 识别链路里各环节的分工一条完整的识别链路大致是图像采集、预处理、模型检测定位、裁剪车牌、字符分割、字符识别、结果输出。图像采集环节依赖摄像头实际使用中不同光照、天气条件都要能稳定出图预处理环节主要是缩放、去噪、增强目的是减少图片噪声对检测的影响这一步做得够不够细直接决定后面识别率的上下限。模型训练环节是整套系统的核心需要有大量不同地区、不同车型、不同天气和光照条件下的车牌图片做数据集。YOLOv5 训练的本质是让模型自己学习“哪块区域是车牌”的特征而不是像传统方法那样人工设计边缘、颜色规则。训练完成后得到 best.pt 权重用于后续的定位推理。车牌定位确定车牌在整张图中的坐标字符分割把每个字符从背景中拆出来字符识别输出最终的车牌号码。3.2 车牌定位YOLOv5 推理封装代码定位这一步在工程上通常封装成一个函数。下面这段代码的思路是先加载训练好的自定义权重再把 OpenCV 读进来的 BGR 图片交给模型推理最后取出置信度最高的检测框import cv2 import torch def load_plate_model(weights_path: str): 加载训练好的 YOLOv5 车牌检测模型 model torch.hub.load(ultralytics/yolov5, custom, pathweights_path, force_reloadFalse) model.conf 0.45 # 置信度阈值低于 0.45 的检测框直接丢弃 model.iou 0.5 # NMS 的 IoU 阈值框重叠过大会被合并 return model def find_plate(model, frame_bgr): 输入 OpenCV 的 BGR 帧返回置信度最高的车牌框 results model(frame_bgr, size640) # 内部自动做 letterbox 缩放 df results.pandas().xyxy[0] if df.empty: return None, 0.0 # 同一辆车可能检测出多个框这里只取置信度最高的一条 row df.loc[df[confidence].idxmax()] x1, y1, x2, y2 ( int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) ) return (x1, y1, x2, y2), float(row[confidence])这段代码有三个关键参数需要关注。size640 是推理输入尺寸YOLOv5 会把图片等比缩放并填充黑边保证目标比例不被拉伸变形。conf 是置信度阈值调高可以减少误检但也会漏掉模糊车牌调低则容易把车头其他区域框进来实际调试时我会先从 0.45 开始在后台页面上对比误检和漏检的比例再微调。iou 是 NMS 抑制参数多辆车并排时如果框重叠严重适当降低这个值能减少重复框。提示如果本地没有独立显卡先把推理尺寸从 640 降到 416 再跑一遍测试集视觉上精度损失并不大但响应速度快非常多。find_plate 拿到的是车牌在模型输入图像里的坐标由于内部做了 letterbox如果原图不是标准 640 边长要对应到原图坐标时需要按缩放比例换算。常见的做法是放在识别服务里检测完马上裁切裁出来的车牌区域直接传给字符分割函数减少一次磁盘读写。3.3 字符分割与识别OpenCV 后处理实现定位到车牌区域后字符分割常见思路是用灰度化、滤波和二值化把字符从底色里分离出来再靠轮廓检测找出每个字符的包围盒。下面是核心实现def split_plate_chars(plate_img): 从彩色车牌区域中分割出单个字符 # 转灰度并做中值滤波去掉小颗粒噪声 gray cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY) gray cv2.medianBlur(gray, 3) # CLAHE 局部直方图均衡解决暗光下车牌对比度不足的问题 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) gray clahe.apply(gray) # Otsu 自动阈值比固定阈值更能适应不同光照 _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 用外轮廓找字符候选块宽高比过滤掉边框和噪声 contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) chars [] h_plate plate_img.shape[0] for c in contours: x, y, w, h cv2.boundingRect(c) # 高必须占车牌高度的四成以上宽要大于高的十分之一 if h h_plate * 0.4 and w h * 0.1: chars.append((x, y, w, h)) # 按横坐标排序保证从左到右排列 chars.sort(keylambda box: box[0]) return binary, chars这段代码里最容易踩坑的是二值化的阈值选择。直接用固定阈值白天和傍晚的识别结果会差很多所以用 Otsu 自动计算。轮廓过滤条件也不是乱写的车牌字符的高度一般占车牌高度的一半左右如果 h 太小很可能把铆钉、边框噪声当成字符如果 w 太宽又可能把两个字符连在一起的区域当成一个字符。这里的 0.4 和 0.1 是我在多种车牌图片上试出来的经验值换用不同比例车牌时需要重新验证。字符分割完成之后每个字符块可以再输入一个轻量分类器做 OCR或者直接和模板库比对。如果效果不够好再对车牌区域做一次透视矫正会更稳根据车牌的四个角点做透视变换把倾斜车牌拉正再去做字符分割角度导致的丢字问题能大幅减少。4. 页面与数据落地登录、管理、统计和地图背后的数据表4.1 登录页与后台首页的交互逻辑系统的前端交互从登录页开始。登录页在视觉上保持整个系统的背景色和主题色一致流程上也做了相应的账号密码校验。用户输入账号密码后后端先查库确认账号存在再把密码用 MD5 加密后与库中值比对比对通过才进入后台首页。原文在系统设计原则里提到敏感字段用了 MD5 加密手机号字段做了正则校验这套组合虽然简单但对毕设项目来说已经有了基本的安全意识。后台首页展示的是管理视角的概览信息当前注册用户数、当前已识别的车牌数量、年月时间以及系统的基础说明。这些数据来自数据库表的聚合查询比如用户数来自 user 表的 count 统计车牌数来自车辆信息表的 count 统计。页面本身承担的角色是信息汇总展示核心逻辑仍然在后端和数据库。4.2 数据库表设计user 表与 news 表怎么理解文档里的数据表设计直接反映了业务边界。user 表管理用户账号信息字段覆盖用户名、密码、姓名、性别、出生年月、QQ、邮箱、电话、内容、添加人news 表偏向系统内容展示包含标题、内容、创建人和备注。两张表合起来支撑了登录、后台首页和用户管理三个页面。字段名描述类型长度是否主键idid 号int11是yonghuming用户名varchar50否mima密码varchar50否xingming姓名varchar50否xingbie性别varchar50否chushengnianyue出生年月varchar50否youxiang邮箱varchar50否dianhua电话varchar50否字段名描述类型长度是否主键idid 号int11是title标题名varchar50否Note内容varchar500否Creater创建人varchar50否Node备注varchar100否第一眼看这两张表的字段名会发现用的是拼音而不是英文比如 yonghuming、mima。这在很多中文项目里是常态优点是文档对照方便缺点是一旦表数量增加可读性会变差。如果你要在这个项目上做二次开发我建议至少新增一张车牌记录表把车牌号、车辆类型、上传日期、归属地、图片路径作为字段放进去因为 news 表显然不适合承载大量识别记录。4.3 车牌管理、统计分析与地图页面的实现思路车牌管理页面的作用是把识别过的车牌以列表形式输出管理者可以按条件查询某段时间、某个区域的车牌记录。列表页的实现逻辑通常是前端把查询参数传给后端后端拼 SQL 查询车辆信息表再把结果组织成 JSON 返回前端渲染成表格。识别记录多了以后还要考虑分页否则一次性拉取上万条数据页面会卡顿。统计分析页面对接的是管理者对业务分布的需求对不同车型、不同车牌颜色的识别数量做汇总。蓝牌、绿牌的数量可以用一张车型统计表配合聚合查询实现前端使用图表库把聚合结果渲染成柱状图或饼图。原文提到“通过不同的图形将不同维度的信息进行统计”这就是聚合查询加图表展示的组合。车牌地图页更有看头它把中国地图嵌入页面用地图形式展示不同地域的车牌数量分布风格偏向 BI 大屏。实现上后端需要给每个车牌记录维护归属地字段前端再按省份对数量分组最后用地图组件绑定数据。5. 避坑记录从训练权重到网页识别最容易翻车的地方5.1 用官方预训练权重直接推理结果什么都检不出来现象加载 yolov5s.pt 跑图片输出结果里根本没有车牌类别。原因官方预训练权重是在 COCO 数据集上训练的COCO 的类别里没有车牌模型可以把车、人、红绿灯框出来但没有学习过车牌长什么样。解决必须使用自己标注的数据集重新训练得到自定义 best.pt 后替换路径再用 torch.hub.load 按 custom 模式加载。这个坑在初学阶段很常见也是换掉权重后立刻就能解决的问题。5.2 训练集只有一个来源换个地区识别率就掉现象拿一批同城道路拍摄照片训练后测试集上准确率很高部署到另一个城市发现很多车牌漏检。原因训练数据分布太单一都是同一个摄像头角度、同一种背景环境下的样本模型没有见过不同光照、不同倾斜角度、不同车牌底色组合。解决训练数据里混合多个地区、多种车型、白天夜晚和阴晴天气的图片并且把随机旋转、亮度调整、饱和度扰动做成在线增强。识别率下降通常不是什么玄学基本都是数据分布差异的问题。5.3 CPU 上跑推理太慢网页端请求直接超时现象浏览器上传一张图片识别接口迟迟不返回页面转圈十几秒后报超时。原因本地开发环境通常没有独立显卡YOLOv5 在 CPU 上的前向推理速度远低于 GPU特别是加载大模型权重时单张 640 尺寸图片推理可能耗时数秒。解决先确认服务端有没有可用的 CUDA 设备再考虑两个更省算力的方向一是换小体积模型 yolov5s二是把推理尺寸从 640 降到 416。两者都会损失一点精度但网页端的实时响应比离线测试的 mAP 数字更值得优先保证。5.4 中文省份汉字、新能源绿牌把字符纠错难住现象识别结果里省份汉字偶尔变成相似字符新能源绿色车牌的号码位数和普通蓝牌不一样分割结果少一位或多一位。原因YOLOv5 检测出的是目标框字符识别阶段需要把框内字符逐一分类中文车牌里省份汉字类别有几十个通用英文数字字符集覆盖不了而新能源牌照是 8 位字符和传统蓝牌的 7 位结构不同。解决字符分类阶段单独维护一个包含省份汉字和数字字母的自定义字符集映射分割逻辑针对绿牌做好位数处理对字符数量做校验不符合 7 位或 8 位规则时重新进行分割。这一步是识别准确率提升最立竿见影的优化点。6. 二次开发思路从论文项目改造成可交付的识别服务6.1 先把识别能力包成 HTTP 接口网页端目前拿到结果的方式大概率是“上传图片、同步识别、返回结果”这种同步调用在图片大或没有 GPU 时会拖垮页面。我一般会先把 find_plate 和 split_plate_chars 包成一个识别服务进程对外提供 HTTP 接口前端只负责上传文件后端任务做完再返回 JSON。这样做的好处是识别服务可以单独部署在一台有显卡的机器上不受 Web 应用性能影响。app.route(/api/recognize, methods[POST]) def recognize_api(): f request.files[image] img_bgr cv2.imdecode(np.frombuffer(f.read(), np.uint8), cv2.IMREAD_COLOR) box, conf find_plate(model, img_bgr) if box is None: return jsonify({plate: , confidence: 0.0}) x1, y1, x2, y2 box crop img_bgr[y1:y2, x1:x2] binary, chars split_plate_chars(crop) return jsonify({plate: recognize_chars(chars), confidence: conf})这段接口代码的要点在于把检测、分割、识别串成一条线。recognize_chars 还没实现的话可以用训练好的字符分类模型替换占位逻辑保证返回的是可扩展的 JSON 结构方便后续加字段。6.2 改造建议与验证方法三个优先级最高的改造方向新增车牌记录表并把识别结果落库把统计页面从改动前的固定数据改成直接基于数据库聚合查询车牌地图的归属地字段在识别后自动关联省份。每次改动前先准备一个小批量的验证集截图当前效果改完再跑同一批图片对比识别率和漏检率差异。以前我调试类似项目时总觉得模型输出不可复现后来养成一个习惯每次改动必须同步记录数据集版本和权重文件版本识别问题先看是不是数据分布变了再看是不是权重没更新。这套设计能跑通最该感谢的就是那些能反复对照的测试图片批次。从那以后我每次跑车牌识别改代码都会先跑测试集、再对比前后效果、最后再动权重顺序乱一步心里都不踏实。希望帮到你。本文还有配套的精品资源点击获取