简介:基于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', path=weights_path, force_reload=False) 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, size=640) # 内部自动做 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'])这段代码有三个关键参数需要关注。size=640 是推理输入尺寸,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(clipLimit=2.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(key=lambda 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 表偏向系统内容展示,包含标题、内容、创建人和备注。两张表合起来支撑了登录、后台首页和用户管理三个页面。
| 字段名 | 描述 | 类型 | 长度 | 是否主键 |
|---|---|---|---|---|
| id | id 号 | int | 11 | 是 |
| yonghuming | 用户名 | varchar | 50 | 否 |
| mima | 密码 | varchar | 50 | 否 |
| xingming | 姓名 | varchar | 50 | 否 |
| xingbie | 性别 | varchar | 50 | 否 |
| chushengnianyue | 出生年月 | varchar | 50 | 否 |
| youxiang | 邮箱 | varchar | 50 | 否 |
| dianhua | 电话 | varchar | 50 | 否 |
| 字段名 | 描述 | 类型 | 长度 | 是否主键 |
|---|---|---|---|---|
| id | id 号 | int | 11 | 是 |
| title | 标题名 | varchar | 50 | 否 |
| Note | 内容 | varchar | 500 | 否 |
| Creater | 创建人 | varchar | 50 | 否 |
| Node | 备注 | varchar | 100 | 否 |
第一眼看这两张表的字段名,会发现用的是拼音而不是英文,比如 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 改造建议与验证方法
三个优先级最高的改造方向:新增车牌记录表并把识别结果落库;把统计页面从改动前的固定数据改成直接基于数据库聚合查询;车牌地图的归属地字段在识别后自动关联省份。每次改动前先准备一个小批量的验证集,截图当前效果,改完再跑同一批图片,对比识别率和漏检率差异。
以前我调试类似项目时,总觉得模型输出不可复现,后来养成一个习惯:每次改动必须同步记录数据集版本和权重文件版本,识别问题先看是不是数据分布变了,再看是不是权重没更新。这套设计能跑通,最该感谢的就是那些能反复对照的测试图片批次。从那以后,我每次跑车牌识别改代码,都会先跑测试集、再对比前后效果、最后再动权重,顺序乱一步心里都不踏实。希望帮到你。
本文还有配套的精品资源,点击获取