资讯动态

YOLOv5深度学习车牌识别系统:从检测分割到落库统计

发布时间:2026/10/25 1:34:50 来源:尧图企业网站定制
简介面向本科毕业设计与课程设计场景的深度学习车牌识别论文参考文档适合正在选题、搭建系统方案或撰写论文的学生使用。内容围绕YOLOv5实时目标检测展开覆盖图像采集与预处理、模型训练、车牌定位、字符分割和字符识别等关键环节并结合Python、OpenCV等技术说明系统实现思路对交通监控、车辆进出管理等应用背景也有交代。压缩包内为1个docx文件约1.14MB包含摘要、关键词、目录及绪论等论文主体结构可作为毕业设计写作框架与技术路线参考。已有1006人学习下载便于读者借鉴论文组织方式、梳理YOLOv5车牌识别流程并补充实验设计与系统实现部分的表述。1. 一张夜间抓拍图进来系统要在 300 毫秒内吐出车牌号停车场闸机前一辆车停下摄像头抓拍一张略微欠曝的图系统要在 300 毫秒内吐出「京A·88888」并抬杆。这套基于 YOLOv5 的深度学习车牌识别系统干的正是这件事用 YOLOv5 做车牌定位再走字符分割与字符识别最后把结果写进 MySQL并提供后台管理、统计分析、车牌地图等页面。它不是跑个模型就完事的玩具 Demo——论文里把登录、后台、车牌识别、统计分析、车牌地图、车牌管理六个页面都设计到了技术栈是 Python YOLOv5 OpenCV MySQL 的 B/S 结构。适合两类人毕业设计需要一套完整可写的系统骨架的同学以及想拿一套现成的「检测 识别」链路做二次开发的工程师。下面从选型开始把这份论文背后的工程讲透。2. 技术选型与整体骨架YOLOv5 只管定位剩下的活谁干一份论文最容易被人忽略的地方是它把「研究内容」和「工程职责」混在一起写。翻到系统总体设计那几页功能模块划分其实已经写得很清楚首页、车牌识别、用户管理、统计分析、车牌地图、车牌管理管理员和普通用户分开。真正落地时你得先把这条链路拆成可独立验证的几段否则模型跑通了却不知道怎么接页面。2.1 从一张抓拍图到车牌号码系统实际要拆成五步按论文摘要里的描述一次完整识别包括图像采集、预处理、模型训练、车牌定位、字符分割、字符识别几个环节。放到工程视角模型训练是离线做的线上跑的是后面四步。我一般会把它细分成五段第一段是图像采集与上传。摄像头抓拍或者用户手动上传统一转成 OpenCV 能读的格式处理成统一尺寸。第二段是预处理缩放、去噪、增强目的是降低光照和天气带来的干扰——这一步在论文摘要里点得很明确。第三段是 YOLOv5 车牌定位输出车牌在图里的边界框坐标。第四段是字符分割把车牌上的每个字符从背景里抠出来。第五段是字符识别把分割出的字符逐个识别拼成最终号码写库并回传前端。这五段任何一段出问题最终结果都是错的所以调试时一定要能把中间结果打出来看。很多新手直接端到端跑失败了根本不知道是检测框歪了还是字符分割阈值选错了。2.2 为什么检测选 YOLOv5、后端选 Python、数据落 MySQL论文里对 YOLOv5 的描述是「处理速度快、准确性高、实时性强」这不是套话。车牌识别对实时性有硬要求——高速口一辆车通过也就几百毫秒两阶段检测器先区域提议再分类在这种场景里很难压住延迟。YOLO 系列把检测当成回归问题一次前向就出框工程上更稳。后端选 Python 不是因为它「火」而是生态闭环OpenCV 负责图像读写和预处理PyTorch 负责加载 YOLOv5 权重Pandas 负责统计分析的数据整理Flask/Django 负责页面和接口。换成 Java 或 Go每一样都要重新找轮子毕业设计的时间预算根本撑不住。MySQL 的选择论文里讲得很实在开源免费、简单易上手、功能完备。车牌识别系统的数据量在毕设级别也就几万条记录用不上 Oracle 那种重型方案但它需要事务和结构化查询比如按车牌号、按时间段统计NoSQL 反而不合适。所以 MySQL 是「长板突出、短板不短」的均衡选择。2.3 B/S 结构下的完整数据流这套系统是 B/S 结构浏览器负责展示和上传后端负责识别和落库。数据流大致是这样用户在识别页上传一张图 → 前端把图片 POST 到后端接口 → 后端先做预处理再调 YOLOv5 推理拿到车牌框 → 用框裁剪出车牌区域走字符分割和识别 → 识别结果连同原图、时间、归属地等字段写进 MySQL 车牌表 → 前端拿返回的车牌号展示同时统计分析页和车牌地图页从数据库里聚合数据出图。论文里提到的统计分析和车牌地图本质都是对车牌表做聚合查询。统计分析按车型、车牌颜色蓝牌/绿牌、时间维度分组计数车牌地图按车牌归属地聚合然后在页面上的地图组件里按区域染色。这两块看起来是「大屏」实现上就是几条 SQL 加上前端图表库别被唬住。提示把「模型推理」和「数据聚合」拆成两个独立服务或两个独立蓝图后面换模型或加统计维度时改动面能小很多。3. 目录结构与数据表设计先看骨架再动代码论文第 4 章给了 E-R 图和数据表设计这是整份文档里信息密度最高的部分——它决定了你后面写接口时字段能不能对得上。很多同学拿到论文直接照着往代码里塞结果发现字段长度不够、类型不对识别出来的车牌号被截断排查半天才发现是 varchar 长度写死了。3.1 工程目录怎么分层论文没给完整代码目录但按它的功能模块和 B/S 结构合格的工程分层基本是这几个文件夹license_plate_system/ ├── app.py # 启动入口Flask 应用实例 ├── config.py # 数据库连接、模型路径、上传目录配置 ├── models/ │ ├── plate_yolov5.pt # YOLOv5 训练好的车牌检测权重 │ └── char_recognizer.pth # 字符识别模型权重 ├── core/ │ ├── detector.py # YOLOv5 推理封装 │ ├── preprocess.py # 图像缩放、去噪、增强 │ ├── segment.py # 字符分割投影法/连通域 │ └── recognizer.py # 字符识别与号码组装 ├── routes/ │ ├── auth.py # 登录、用户管理 │ ├── recognize.py # 车牌识别接口 │ ├── stats.py # 统计分析聚合 │ ├── map_view.py # 车牌地图数据 │ └── plate_admin.py # 车牌管理列表、删除、导出 ├── static/ # 前端 js/css、图表库、地图组件 ├── templates/ # 登录、后台、识别、统计、地图、管理六个页面 └── db/ └── schema.sql # 建库建表脚本core/目录是关键——它把模型相关的逻辑全收进去路由层只负责收请求、调core、返回 JSON。这样后面把 YOLOv5 换成 YOLOv8改的只有core/detector.py一个文件路由和前端全不用动。这是我在多个图像项目里踩过坑后固定下来的分层方式。3.2 三张关键表用户、车牌记录、新闻/公告论文给了user和news两张表。user表字段包括 id、用户名、密码、姓名、性别、出生年月、QQ、邮箱、电话、内容、添加人news表包括 id、标题、内容、创建人、备注。但车牌识别的核心——车牌记录表——论文里没有单独给建表语句只在 E-R 图里画了车辆名称、车辆信息、车辆类型、车辆地图、上传日期这几个属性。按这套逻辑补全我一般会建这么一张表CREATE TABLE plate_record ( id INT(11) NOT NULL AUTO_INCREMENT COMMENT 主键, plate_no VARCHAR(16) NOT NULL COMMENT 识别出的车牌号如京A88888, plate_color VARCHAR(8) DEFAULT blue COMMENT 车牌颜色blue/green/yellow, vehicle_type VARCHAR(32) DEFAULT NULL COMMENT 车型可空, region VARCHAR(32) DEFAULT NULL COMMENT 归属地用于地图聚合, image_path VARCHAR(255) DEFAULT NULL COMMENT 原图存储路径, confidence DECIMAL(5,4) DEFAULT NULL COMMENT 检测置信度, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 识别时间, create_by VARCHAR(50) DEFAULT NULL COMMENT 操作人, PRIMARY KEY (id), KEY idx_plate_no (plate_no), KEY idx_create_time (create_time), KEY idx_region (region) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT车牌识别记录表;几个字段说明一下。plate_no给了 16 个字符普通车牌 7 位加新能源 8 位够用留余量是因为有些识别结果可能带上分隔符。字符集必须是utf8mb4不然中文归属地存进去会变问号——这是血泪经验utf8在 MySQL 里只支持 3 字节遇到一些生僻字和 emoji 直接翻车。region和create_time单独建索引是因为统计分析和地图页的聚合查询全打在这两个字段上不建索引数据量一上万就明显卡。3.3 E-R 关系与冗余规避论文 4.3 节的 E-R 图把车辆信息和车牌地图画在一起实际建模时要注意车牌记录和用户是多对一一个操作员录多条记录车牌记录和地图区域也是多对一多个车牌同一个归属地。如果把归属地名称直接冗余进每条记录车牌地图聚合时容易因为命名不一致「北京」和「北京市」分裂成两个区域。常见的做法是单独建一张区域字典表plate_record.region存区域编码而不是中文名前端展示时再关联字典表取名字。这样后面区域改名、合并只要改字典表一处历史数据不用动。这是数据冗余规避里最容易被忽略的一条也是我在做统计类项目时每次都会先确认的点。4. 核心模块拆解检测、分割、识别、落库一条龙论文第 5 章用六张页面截图讲了系统实现但从截图看不出代码长什么样。这一章把识别链路上最关键的几段代码补出来每一段都能单独跑通、单独验证。4.1 YOLOv5 模型加载与车牌检测推理YOLOv5 的推理可以用官方ultralytics包也可以用torch.hub直接拉。工程里我一般用ultralytics因为版本和接口更可控# core/detector.py import cv2 import torch from ultralytics import YOLO class PlateDetector: def __init__(self, weight_pathmodels/plate_yolov5.pt, conf0.25): # 加载训练好的 YOLOv5 车牌检测权重 self.model YOLO(weight_path) self.conf conf # 置信度阈值低于此值的框丢弃 def detect(self, image): 输入 BGR 图像返回车牌框列表 [(x1,y1,x2,y2,score), ...] results self.model(image, confself.conf, verboseFalse) boxes [] for r in results: for box in r.boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) score float(box.conf[0]) boxes.append((x1, y1, x2, y2, score)) return boxesconf0.25是检测置信度阈值调低会检出更多候选框但误检增加调高会漏掉模糊车牌。默认 0.25 是车牌场景比较稳的起点如果夜间图漏检多可以降到 0.15 再配合后处理。verboseFalse关掉日志输出线上跑的时候日志会刷屏。返回的框坐标要转成整数因为后面 OpenCV 裁剪需要。实际使用时检测出来的框可能不止一个——一张图里有多辆车的时候。策略是取置信度最高的那个或者按面积从大到小排取最靠前的一个作为主车牌。多车牌场景需要额外加车辆跟踪把框和车对应起来这在后面的改造章会说到。4.2 车牌图像预处理与字符分割拿到车牌框之后裁剪出来的区域往往倾斜、光照不均直接送识别准确率上不去。预处理先做两步灰度化和直方图均衡然后把车牌区域按宽高比做透视校正。字符分割用垂直投影法对二值化后的车牌图做列方向的像素统计字符位置会形成明显的峰谷# core/segment.py import cv2 import numpy as np def preprocess_plate(plate_img): 车牌区域预处理灰度化 去噪 直方图均衡 gray cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY) gray cv2.bilateralFilter(gray, 9, 75, 75) # 保边去噪 gray cv2.equalizeHist(gray) return gray def segment_chars(gray): 垂直投影法切分字符返回字符图列表 # OTSU 自适应二值化车牌字符与背景对比度较高时效果稳 _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 每列白色像素个数 col_sum np.sum(binary 255, axis0) # 阈值判定列像素少于 5% 高度视为间隔 threshold binary.shape[0] * 0.05 chars, start [], None for i, v in enumerate(col_sum): if v threshold and start is None: start i elif v threshold and start is not None: if i - start 5: # 过滤过窄的噪声列 chars.append(binary[:, start:i]) start None return charsbilateralFilter的第三个参数是颜色空间 sigma第四个是坐标空间 sigma车牌这种小区域用 75 够用太大边缘会被抹掉。OTSU二值化不需要手动给阈值会自动根据图像灰度分布找分割点但前提是车牌字符和底色的对比度足够——绿牌和蓝牌的底色不同如果识别准确率普遍偏低可以按颜色先分桶再用不同的预处理参数。i - start 5这个过滤很关键。车牌上可能有螺丝孔、污渍形成的窄条不滤掉会被当成字符送进识别最终号码里多出一位。这个 5 是经验值如果分割出的字符宽度普遍偏大说明阈值太高可以往下调。4.3 字符识别与结果组装字符识别有两种路线一是用训练好的 CNN 分类器对每个字符图单独分类再拼接二是直接用 CRNN 做序列识别。论文摘要里写的是「字符分割 字符识别」所以走第一条路线更符合原始设计。分类器可以用简单的 CNN输出层维度等于字符集大小数字 0-9 字母 省份汉字# core/recognizer.py import torch import torch.nn as nn class CharCNN(nn.Module): def __init__(self, num_classes65): super().__init__() self.features nn.Sequential( nn.Conv2d(1, 32, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding1), nn.ReLU(), nn.AdaptiveAvgPool2d((4, 4)), ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(128 * 4 * 4, 256), nn.ReLU(), nn.Dropout(0.3), nn.Linear(256, num_classes), ) def forward(self, x): return self.classifier(self.features(x)) def recognize_plate(chars, model, idx2char, devicecpu): 对分割出的字符图逐个识别拼成车牌号字符串 result [] for c in chars: # 统一缩放到 20x40 并归一化 img cv2.resize(c, (20, 40)).astype(float32) / 255.0 tensor torch.from_numpy(img).unsqueeze(0).unsqueeze(0).to(device) with torch.no_grad(): pred model(tensor).argmax(dim1).item() result.append(idx2char[pred]) return .join(result)num_classes65是常见设定10 个数字 24 个英文字母去掉 I、O 避免和 1、0 混淆 31 个省份简称汉字。字符集小时减少类别数能降低混淆概率。Dropout(0.3)是防止小数据集过拟合省份汉字样本往往只有几千张不加 dropout 训练集准确率能到 99%测试集掉到 80% 都正常。组装结果后要做一次格式校验普通车牌是「省份汉字 字母 5 位字母数字」新能源是 8 位。用正则过滤不符合格式的标记为低置信度入库方便后面人工复核。4.4 后端接口与统计/地图数据聚合识别链路写好后用一个 Flask 接口串起来。统计分析页和车牌地图页的数据直接从plate_record表聚合# routes/stats.py from flask import Blueprint, jsonify from db import get_conn stats_bp Blueprint(stats, __name__) stats_bp.route(/api/stats/summary) def summary(): conn get_conn() cur conn.cursor() # 按车牌颜色分组统计供柱状图使用 cur.execute( SELECT plate_color, COUNT(*) AS cnt FROM plate_record GROUP BY plate_color ) color_rows [{color: r[0], count: r[1]} for r in cur.fetchall()] # 按归属地聚合供车牌地图染色使用 cur.execute( SELECT region, COUNT(*) AS cnt FROM plate_record WHERE region IS NOT NULL GROUP BY region ) region_rows [{region: r[0], count: r[1]} for r in cur.fetchall()] cur.close(); conn.close() return jsonify({by_color: color_rows, by_region: region_rows})这段代码有两个实际点。一是连接用完必须closeFlask 默认单进程处理连接不还回去的话跑一天就能把 MySQL 连接数耗光。二是两条聚合查询各自独立不要写成一条多表 join因为by_color和by_region的维度不一样join 会产生笛卡尔积放大。前端拿这个 JSON柱状图和地图组件直接绑定字段就行。5. 常见问题与排查部署和调参最容易翻车的五处5.1 现象torch和ultralytics版本冲突模型加载不出权重原因YOLOv5 的权重文件对 PyTorch 版本有要求用pip install ultralytics默认装最新版和本地已有的老版本 PyTorch 经常打架报KeyError或AttributeError。解决先固定 PyTorch 版本比如 2.0.x 配 ultralytics 8.0.x装之前pip list | grep torch看当前版本别盲目升级整个环境。虚拟环境里跑不要在系统 Python 里混装。5.2 现象cv2.imread返回None后续代码全崩原因OpenCV 的imread不支持带中文的路径图片放在「识别测试图/车牌.jpg」这种目录下直接读不到返回 None 不报错。解决用np.fromfile cv2.imdecode替代imread或者把上传目录固定成纯英文路径再处理。这是踩坑最多的一条新手排查半天以为是模型问题。5.3 现象车牌号入库后中文变问号统计地图区域全乱原因建库时字符集用了utf8而不是utf8mb4或者连接串没加charsetutf8mb4MySQL 默认字符集截断多字节字符。解决建库建表统一utf8mb4连接串显式声明字符集Python 侧确保pymysql.connect(charsetutf8mb4)。已经存坏的数据只能重建表重导。5.4 现象上传大图后接口超时前端一直转圈原因手机拍的图动辄 4000×3000预处理和推理都在原图上做单张耗时从 300ms 涨到 3 秒以上Flask 默认超时直接断连。解决上传时不压缩到了后端在preprocess第一步先按长边 1280 缩放再做后续处理同时接口里加超时上限超时返回明确错误码别让前端干等。这是用户体验相关的坑不处理会被当成「系统不稳定」。5.5 现象统计图表每次刷新数据不变原因聚合接口加了缓存没清或者统计 SQL 只查了历史表没查最新记录数据延迟。解决确认统计查询有没有时间范围过滤别写成全表扫描带缓存前端请求加时间戳参数绕过缓存数据量过万后考虑定时任务预聚合到中间表而不是每次请求都实时 group by。6. 二次开发改造换数据集、换模型、加功能的实测路径把这套系统跑起来之后真正有价值的改造是大数据和模型这两块。我先说换数据集——论文里没用真实大规模数据集很多毕设直接拿几百张图训练模型泛化能力一塌糊涂。我的做法是先用开源车牌数据集比如 CCPD 这类有公开标注的跑一遍训练确认 YOLOv5 的标注格式txt 一行一框和类别 id 对得上再逐步替换成自己场景的图。训练时把--img 640 --batch 16 --epochs 100作为起点训完用val.py看 mAP0.5低于 0.85 说明数据或标注有问题别急着换模型。换模型是第二个高频需求。YOLOv5 换成 YOLOv8接口基本兼容core/detector.py里只需要把YOLO(weight_path)换成对应的版本返回的boxes结构略有差异字段名从xyxy到boxes.xyxy有变化对着官方文档改两行即可。要注意的是 YOLOv8 的默认conf阈值和 v5 不同换完必须在验证集上重新调不然会误以为模型退化了。加功能最多的是车牌跟踪和超分。前者是为了解决一段视频里同一辆车反复识别的问题——给每个检出框分配 track id同一 id 只入库一次后者是对模糊图先做超分再检测代价是延迟翻倍只适合离线批处理不适合闸机实时场景。加功能时我习惯先用一个小脚本单独验证新模块再合并到core/里避免改一处崩全局。改造方向改动文件验证方式换检测模型core/detector.pyval.py 跑 mAP 真实图抽检换字符识别core/recognizer.py字符级准确率 整牌通过率加跟踪core/detector.py 路由视频抽帧看 track id 是否稳定加超分core/preprocess.py对比单张延迟与识别提升从那以后我每次接手图像识别项目都强制先把「检测—分割—识别—落库」四段拆开单独测一遍再往一起拼。这套流程没让我少写代码但让我少熬了很多夜。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑