简介:YOLOv5车牌识别Python毕业设计项目提供一套完整可运行的源码与配套资料,面向计算机视觉与深度学习方向的学生和开发者,用于解决车牌定位、字符识别等实际问题。压缩包共78个文件,包含Python训练与检测脚本、预训练权重(.pt/.pth)、YAML模型配置、测试图片与视频、运行说明及依赖清单等,整体约345MB,目录结构清晰,便于按模块查阅。已有1506人学习下载,内容系统覆盖卷积神经网络原理、YOLOv5的Backbone/Neck/Head结构、数据标注与数据集划分、模型微调、图像预处理、实时目标检测、OCR车牌识别、mAP指标评估以及结果可视化,完整呈现从原理到落地的实践路径。附带的预训练模型、测试样例和可迁移训练脚本能帮助读者快速复现识别效果,并将方案拓展至其他目标检测项目,是完成毕业设计或深入理解YOLOv5流程的高质量参考。
1. yolo5 车牌识别 python 项目:这套源码能把毕设做到哪一步
做车牌识别毕设最怕的不是看不懂论文,而是手里没有一套能直接跑起来的代码。这份基于 YOLOv5 的车牌定位与识别源码包,正好踩在这个痛点上:它把检测车牌位置、裁剪车牌区域、OCR 识别字符整条链路都串好了,自带训练脚本、推理脚本、预训练权重、8 张测试图和一个运行说明文档。对时间紧的学生来说,它是把深度学习理论落到 mAP 曲线和可视化边界框的捷径;对刚接触目标检测的从业者,它也是一份结构完整的 YOLOv5 工程范本。下面我会按先跑通、再训练、后避坑的顺序,把每个参数为什么这么设、坑在哪一层讲清楚,保证你照着走,当天就能看到自己的第一个车牌框。
2. 跑通环境与目录结构:先让 detect.py 吐出第一个车牌框
先给结论:这份源码包解压后,最快 10 分钟能跑出一张带边界框的检测图。前提是 Python 环境能装依赖、权重文件路径不被写死、工程目录别带中文。我拿到任何 YOLOv5 系源码的第一步永远是:把项目放到纯英文路径下,先读 requirements.txt,再检查权重文件,最后才碰推理命令。顺序反了,时间会全耗在定位环境问题上。
2.1 解压先看目录:文件清单里哪些是核心哪些可删
压缩包目录基本是 YOLOv5 官方骨架的裁剪版,我用表格把关键文件的分工列出来,避免你对着几十个文件发懵。
| 路径 | 作用 | 优先级 |
|---|---|---|
| detect.py / test.py | 推理入口,检测单张图或视频 | 必看 |
| train.py / train1.py | 训练入口,train1.py 是简化版 | 必看 |
| read_yaml.py | 读取数据配置 yaml 的辅助脚本 | 选看 |
| models/ | 网络结构定义 | 改结构才看 |
| utils/ | 训练与推理工具函数 | 选看 |
| weights/ | 预训练权重目录 | 必看,记住里面 .pt 的实际文件名 |
| data/ | 数据集配置与标注组织 | 训练必看 |
| runs/ | 训练与推理输出目录 | 看结果用 |
| test0.jpg ~ test7.jpg | 8 张冒烟测试图 | 先跑这批 |
| 运行说明.txt | 作者给的启动指引 | 必看 |
| simsun.ttc | 中文字体文件,画中文标签用 | 中文显示才用 |
| requirements.txt | 依赖包清单 | 必看 |
| tutorial.ipynb | Jupyter 交互教程 | 选看 |
| Dockerfile | 容器化部署配置 | 可选 |
这里面真正决定能不能跑起来的只有三样:detect.py、weights 目录里的 .pt、requirements.txt。Dockerfile 是给 Linux 服务端部署准备的,毕设答辩用不上,留着也不碍事。hubconf.py 是给 torch.hub 远程加载用的接口,本地推理可以忽略。test*.jpg 这 8 张图是冒烟测试集,先把它们全部跑出框,再谈后续训练。
我给个经验顺序:先跑 test0.jpg 这种车牌摆得正、光照正常的图,把整条链路打通;再换 test 系列里带倾斜角或强反光的难例看鲁棒性。一上来就去啃最难的那张,容易误判成「模型不行」,实际是链路没通。
2.2 依赖安装与版本匹配:Python 3.8~3.10 配 PyTorch 稳定版
requirements.txt 里最典型的组合是 torch、torchvision、opencv-python、numpy、matplotlib、PyYAML,再加 tqdm 和 seaborn 这类辅助库。YOLOv5 不同分支对 PyTorch 版本有最低要求,我的经验是用 Python 3.8~3.10 配 PyTorch 1.13 到 2.x 之间的稳定版,基本不会遇到算子不兼容。
# 用国内镜像安装依赖,避免默认源超时卡死 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 装完先自检版本,确保 torch 和 cv2 都能 import python -c "import torch, cv2; print(torch.__version__, cv2.__version__)"第一条命令的-i参数指定了清华 PyPI 镜像,国内网络环境下比官方源快几倍,否则pip install torch这一步就能卡半小时。第二条是版本自检,YOLOv5 的 focus 模块和 C3 模块内部依赖较新版本的卷积算子,torch 版本低于 1.8 直接报错;opencv-python 低于 4.5 时部分图像处理接口行为也不一致。
无 GPU 的机器不是不能跑,CPU 推理一张测试图大概要 2~5 秒,训练就非常慢了,一个 epoch 可能跑十几分钟,不建议用 CPU 训完整模型。Windows 用户尤其注意:pip install torch默认可能装成 CPU 版,torch.cuda.is_available()返回 False 就是这个原因。解决方式是到 PyTorch 官网按你的 CUDA 版本选对应 wheel 重新安装,而不是在环境变量上反复折腾。
注意:整个工程目录和测试图片路径不要出现中文或空格。
cv2.imread对中文路径支持极差,路径一杂就会静默读图失败,最终效果是模型正常加载但输入全黑,详见第 5 章。
2.3 快速推理:detect.py 参数表与第一张测试图
依赖装完,直接把 test0.jpg 喂给检测脚本:
python detect.py --weights weights/best.pt --source test0.jpg --conf-thres 0.4 --img 640这里--weights指向权重文件,实际文件名以你压缩包里 weights 目录的 .pt 名称为准,不要照抄我写的 best.pt。--source是输入,可以是图片、视频文件或摄像头设备号。--conf-thres 0.4是置信度阈值:低于 0.4 的检测框会被丢掉。阈值调低到 0.25 假框变多,调高到 0.6 容易漏框,车牌检测场景 0.4 起步比较稳。--img 640是推理分辨率,必须和训练时的输入尺寸一致,否则检测框的坐标会整体偏移。
跑完结果默认写到runs/detect/exp目录,打开里面的标注图,能看到车牌框和置信度分数。这一步通过,说明权重可加载、前向推理正常、绘图模块没报错。接着换 test1.jpg、test2.jpg 各跑一遍,确认不同角度的车牌都能被框住,再进入训练环节。
顺带提一句,如果权重里分了蓝牌、黄牌、新能源等多类,detect 输出会带类别名;如果训练时就只标了「车牌」一个类,输出就只有一个 label。跑一次看终端打印的 class 列表就知道是哪一种,这对后面第 3 章的数据配置很有参考价值。
3. 车牌定位训练:数据集组织与模型参数怎么设
检测框跑得漂亮,那是别人训练好的权重。毕设答辩老师大概率会追问「你自己的模型怎么训练的」,所以训练链路必须走一遍:准备数据集、写 yaml 配置、跑 train.py、盯 mAP 曲线。本章按这个顺序拆开讲。
3.1 YOLOv5 结构:CSPDarknet 与 PANet 怎么服务小目标车牌
YOLOv5 的主干是 CSPDarknet,Neck 是 PANet,Head 在三个不同尺度上输出预测。主干负责提取图像特征,Neck 做多尺度融合,Head 输出边界框坐标和类别概率。对车牌这个具体场景,最关键的是 PANet:它把浅层的高分辨率特征和深层的语义特征反复融合,让模型同时照顾大面积近景车牌和远处只占几十像素宽的小车牌。监控画面里的车牌往往是小目标,这个特性直接决定漏检率。
锚框(anchor)是另一个容易被忽略的变量。YOLOv5 默认锚框按 COCO 数据集统计,COCO 里物体普遍接近方形,而车牌宽高比在 3:1 到 4:1。直接拿默认锚框训练,前几个 epoch 的 loss 下降会很慢,而且框的宽度始终对不齐车牌边缘。train.py 默认会在训练启动时做一次 autoanchor 自适应聚类,终端日志里出现 "autoanchor" 字样就是它在重新算锚框,一般不用手动干预,但你要知道这个机制存在,别把训练前期的正常日志当成报错。
输入分辨率也影响定位精度。车牌的宽高比已经够极端,如果--img设成 320,窄长车牌在特征图上可能缩到 4×4 像素以下,特征直接被卷积稀释干净。我在实际项目里 640 起步,显存允许的话上 960 对窄长车牌更友好,代价是训练时间线性上涨。
3.2 数据集与 yaml 配置:YOLO 标注格式与 class 对不上就翻车
YOLOv5 训练数据按 images 和 labels 两个目录组织,每张图片对应一个同名 txt 标注文件。标注格式是 YOLO 归一化坐标,每行代表一个目标:
# labels/train/00001.txt,每行:class_id x_center y_center width height 0 0.4825 0.6388 0.1523 0.0417这行数据的含义是:类别 0(车牌),边界框中心点位于图片宽度的 48.25%、高度的 63.88%,框宽占整张图的 15.23%,框高占 4.17%。所有数值都归一化到 0~1,这是 YOLO 格式和 COCO 格式最核心的区别。标注工具推荐 labelImg,保存时选 YOLO 格式,它会自动生成同名 txt 到指定 labels 目录。
数据配置用一个 yaml 文件把路径和类别信息串起来:
train: data/plate/images/train val: data/plate/images/val nc: 1 names: ['plate']nc: 1表示训练时只有「车牌」一个类别。如果数据集同时标了蓝牌、黄牌、新能源三类,nc 就要改成 3,names 对应写成['blue', 'yellow', 'green']。最容易翻车的点在这里:标注文件里的 class id 从 0 开始,names 列表也从 0 开始对齐。names: ['blue', 'yellow']时,标注里 0 代表蓝牌、1 代表黄牌;一旦标反,训练不报错,但模型学出来的语义整个是错的。训练日志里样本数正常但 loss 不降,先回来查这一层。
数据集数量方面,纯车牌检测任务,干净标注的 3000~5000 张能训出可用的模型;毕设想稳一点,建议凑到 8000 张以上,覆盖白天、夜晚、倾斜、遮挡各种情况。数据增强交给 YOLOv5 内置的 mosaic、翻转、HSV 扰动就够了,不用自己写增强代码。
3.3 训练参数:epochs、batch、lr 怎么设,结果看哪张图
数据备好,跑训练:
python train.py --data data/plate.yaml --weights weights/yolov5s.pt --img 640 \ --batch 16 --epochs 100 --device 0 --patience 20参数按车牌场景给出建议值:
| 参数 | 建议值 | 依据 |
|---|---|---|
| --img | 640 | 兼顾小目标特征与显存占用 |
| --batch | 8~32 | 8G 显存建议 16,OOM 就降到 8 |
| --epochs | 100 | 车牌类别少,50 轮能收敛,100 轮更稳 |
| --device | 0 | 指定第一块 GPU,CPU 训练则留空 |
| --patience | 20 | 连续 20 轮验证集无提升就早停 |
| --lr0 | 默认 0.01 | 若 loss 震荡,手动降到 0.001 |
--weights weights/yolov5s.pt表示在 COCO 预训练权重上做迁移学习,这比从零随机初始化收敛快得多。--patience 20是早停策略:连续 20 个 epoch 验证集 mAP 没有提升,训练自动终止,省电省时间。训练开始后盯runs/train/exp下的 results.png,里面有 box_loss、obj_loss、cls_loss 和 mAP 四组曲线。正常情况 mAP 曲线在 30~60 个 epoch 之间会出现明显的爬升台阶,之后趋于平缓。如果 mAP 一直贴在 0 附近,回去查 data 配置的路径和标注格式,十有八九是 yaml 里的 train/val 路径没对上,或者标注 class 越界。
训练完成后选 best.pt,它是验证集上表现最好的权重,不是 last.pt(最后一个 epoch 的权重)。把 best.pt 复制回 weights 目录,重新跑第 2 章的 detect.py,对比一下训练前后同一张测试图上的框差异,这就是答辩时要展示的「训练效果」。
4. 车牌字符识别:OCR 衔接与预处理参数
检测框拿到的是「这里有块车牌」,毕设要交的是「京A12345」这串字符。这个源码包采用检测加识别两段式:先裁车牌,再识别字符。OCR 环节可以接 Tesseract,也可以接 PaddleOCR,但核心不在于选哪个引擎,而在于检测框到 OCR 之间的衔接处理和字符后处理。本章按可复现的纯 OpenCV 加 OCR 路线讲透。
4.1 从检测框到字符图:裁剪、放大与缓存
detect.py 在推理时已经拿到了边界框坐标,你需要做的第一步是把这块区域从原图裁出来,并适当放大。OCR 引擎对过小的字符图几乎无能为力,这是最容易忽略的环节。
import cv2 img = cv2.imread('test0.jpg') # 假设 detect 输出的框坐标为 x1, y1, x2, y2 x1, y1, x2, y2 = 100, 200, 340, 260 plate = img[y1:y2, x1:x2] # 放大 2 倍,字符间的像素距离变大,OCR 更容易切分字符 plate = cv2.resize(plate, None, fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC) cv2.imwrite('plate_crop.jpg', plate)切片[y1:y2, x1:x2]的顺序是先行后列,对应坐标系里是先 y 后 x,写反会裁出一块完全错误的区域。fx, fy放大 2 倍是经验值:放大太少字符间距不够,放大太多边缘出现锯齿反而干扰 OCR。INTER_CUBIC适合放大场景,比默认的 INTER_LINEAR 在处理弧形边缘时更平滑。
如果一次要处理几十张图,我不会把裁剪图临时落盘,直接以内存数组形式传给 OCR 函数,省掉磁盘 IO。落盘的唯一价值在调试阶段:把裁剪图保存出来看一眼,是倾斜、过曝还是字符粘连,能立刻判断问题出在检测段还是识别段。
4.2 透视矫正与二值化:倾斜车牌的 image 预处理
现实拍摄的车牌很少正对镜头,透视变形会让 OCR 把字符边缘误判成噪声。常见做法是先对裁剪区域做透视矫正。简单的场景用边缘检测加四边形近似就够,复杂背景还需要先定位车牌四角:
import cv2 import numpy as np plate = cv2.imread('plate_crop.jpg') gray = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY) # 高斯模糊去噪,小图核 (3,3) 够用,放大后可升到 (5,5) blur = cv2.GaussianBlur(gray, (3, 3), 0) # OTSU 自适应二值化,适用顺光逆光混合的数据 _, binary = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 找外轮廓,最大的近似四边形大概率是车牌区域 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * peri, True) if len(approx) == 4: cv2.drawContours(plate, [approx], -1, (0, 255, 0), 2)GaussianBlur的核大小与图像尺寸挂钩,小图用 (3,3),放大 2 倍后可以升到 (5,5),核太大会把字符边缘一并糊掉。THRESH_OTSU会自动计算最佳阈值,比固定 127 稳定,尤其是测试图中顺光和逆光同时存在时。approxPolyDP的 0.02 是逼近精度系数,系数太大四边形顶点会少算,系数太小边缘噪声会被当成顶点。拿到四个顶点后,用cv2.getPerspectiveTransform加cv2.warpPerspective做矫正,源点和目标点一一对应即可。这个步骤对倾斜车牌字符识别率的提升非常明显,值得花时间调。
4.3 字符后处理:易混淆字母映射与正则校验
OCR 输出天然带不确定性,形近字符是最典型的误差来源:0 与 O、1 与 I/l、8 与 B、Z 与 2。直接把原始输出当结果,字符级准确率能掉十几个百分点。后处理必须做字符归一化和格式校验。
import re char_map = {'O': '0', 'I': '1', 'l': '1', 'Z': '2', 'S': '5', 'B': '8'} text = ocr_result.strip().upper() for wrong, right in char_map.items(): text = text.replace(wrong, right) # 常见车牌格式:省份汉字 + 发牌机关字母 + 5~6 位数字字母 if re.fullmatch(r'[\u4e00-\u9fa5][A-Z0-9]{5,6}', text): print('识别成功:', text) else: print('疑似误识别,丢弃:', text).upper()统一大写,保证映射表只写大写字母就能覆盖所有输出。映射表的顺序有讲究:先替换 O→0,再处理 Z→2,彼此互不干扰;但如果把数字替换规则写在前面,后面 O→0 就会把原本正确的数字改坏。正则里\u4e00-\u9fa5匹配汉字省份简称,[A-Z0-9]{5,6}限定主体长度。这一步看似只有几行代码,实际是 OCR 准确率的最大杠杆,加了正则后,不合法的输出被直接丢弃,而不是当正确答案交出去。
5. 避坑排查:环境、尺寸、字符混淆五类常见翻车
这一章是我拆这套源码和做类似车牌项目时踩过的坑,按「现象 → 原因 → 解决」三层结构写。你跑这套项目遇到同类问题,可以对号入座。
5.1 环境类:显存 OOM 与 CUDA 版本不匹配
现象:训练刚跑两三个 step,终端直接报CUDA out of memory。
原因:batch size 或 img 尺寸超过显存上限,或者后台有多个进程抢占了 GPU 显存。8G 显存的卡跑 yolov5s,batch 16 很容易 OOM。
解决:先降 batch 再降 img。把--batch 16降到 8,还不行就把--img 640降到 512。这时用nvidia-smi看一眼显存占用,确认是不是有其他 Python 进程在占用。8G 显存跑 yolov5s 加 batch 8 加 img 640 是安全组合,再往上就属于硬扛了。
现象:装完 PyTorch 后torch.cuda.is_available()返回 False。
原因:大概率装成了 CPU 版 wheel,不是环境变量的问题。
解决:到 PyTorch 官网按 CUDA 版本选对应安装命令。比如 CUDA 11.8 就装torch==2.1.0和torchvision==0.16.0的 cu118 版本,装完重新跑自检。不要先怀疑 PATH 或环境变量配置,先把 wheel 选对,这个顺序能省大量时间。
5.2 检测类:框偏移、漏检与锚框不匹配
现象:训练时 loss 在降,但验证集 mAP 卡在 0.5 左右上不去,检测框要么偏大要么偏窄。
原因:最常见的是训练和推理的--img不一致,归一化坐标换算成实际像素时整体错位;另一原因是默认锚框与车牌长宽比差太远,模型前期学不到正确的框宽度。
解决:训练和推理统一--img 640;训练日志里确认 autoanchor 是否完成重聚类。如果还不行,手动在模型 yaml 里写一组适合车牌的细长锚值,比如[[12,4],[28,10],[45,18]]这种宽高比明显偏长的候选。
现象:测试图里某个车牌没有被框出来。
原因:置信度阈值设太高,或这块车牌处于遮挡、暗光区域,模型没有足够信心。
解决:把--conf-thres降到 0.25 看能否出框。如果降阈值后才出框,说明模型对该场景信心不足,正确做法是补充这类图片进训练集重训,而不是靠降阈值硬撑。--iou-thres 0.45控制 NMS 去重,一般不用动。
5.3 OCR 与显示类:字符混淆、中文乱码与路径中文
现象:OCR 把「京A12345」识别成「京A12O45」。
原因:O/I/l/Z/S 这些字符和数字在图像上高度相似,OCR 引擎本身难以区分。
解决:在后处理里加字符映射表和正则校验,即第 4 章 4.3 节的做法。这两步不加,字符级准确率会肉眼可见地掉。
现象:用 matplotlib 画检测图,中文标签显示成方块。
原因:Matplotlib 默认字体里没有中文字符,而源码包明明带了 simsun.ttc 却没有被加载。
解决:显式加载字体文件:
import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties font = FontProperties(fname='simsun.ttc') plt.text(x, y, label, fontproperties=font)fname指向资源包里的 simsun.ttc,路径要跟当前工作目录一致,否则一样报文件找不到。这个坑在英文标注时完全暴露不出来,一旦把类别名换成「车牌」两个字,框上的 label 立刻变成豆腐块。
现象:cv2.imread('测试图.jpg')返回 None。
原因:OpenCV 原生不支持中文路径。
解决:治本是把所有文件和路径改成英文;治标用np.fromfile绕过 OpenCV 的路径解析:
img = cv2.imdecode(np.fromfile('测试图.jpg', dtype=np.uint8), cv2.IMREAD_COLOR)imdecode的cv2.IMREAD_COLOR参数强制以 BGR 三通道读入,灰度图也不会出错。这条对 Windows 用户尤其关键,导出到「我的文档」的路径多半带着中文用户名,属于最容易踩却最难察觉的隐性坑。
6. 端到端批量验证:从单图推送到文件夹批处理
最后把前几章的链路压成一个批量脚本,对 test0.jpg 到 test7.jpg 一次性跑完检测、裁剪和识别,输出结果到 CSV,写毕设实验报告时直接引用这批数据。
import glob import os import cv2 from detect import run csv_lines = [] for img_path in sorted(glob.glob('test*.jpg')): # run 的返回结构按你 detect.py 的实际实现调整 results = run(weights='weights/best.pt', source=img_path, conf_thres=0.4, imgsz=640) name = os.path.basename(img_path) csv_lines.append(f'{name},{len(results)}') with open('result.csv', 'w', encoding='utf-8') as f: f.write('image,plate_count\n') f.write('\n'.join(csv_lines))这里有三个小技巧值得记住。第一,用glob('test*.jpg')配合sorted()保证 8 张图的处理顺序稳定,比手写 8 行命令省事得多。第二,如果 detect.py 没有暴露可调用的模块接口,最少的改动方式是在循环里用subprocess.run(['python', 'detect.py', ...])调命令行入口,虽然慢一点但完全不动源码,风险最小。第三,批量跑完不要覆盖上次的输出目录,给 detect 加--name exp_batch参数或每次指定独立输出目录,否则做 A/B 对比时新旧结果混在一起,根本分不清哪批是哪批。
把每张裁剪图和 OCR 结果也一并存档,失败样本一眼就能定位是检测漏了还是识别错了。拆这类项目,我记忆最深的教训是「单图跑通不算跑通,批量跑通才算」:单图链路上偶尔的侥幸成功,在 8 张图批量场景下会被放大成各种问题。从那以后,我每次拿到检测源码都会先做一次全量推理,把置信度分布和每类 mAP 拉出来看,再决定要不要继续往下改。这套习惯帮我避掉了很多返工,希望帮到你。
本文还有配套的精品资源,点击获取