ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于深度学习的车牌识别源码:Python毕业设计实战与调优

基于深度学习的车牌识别源码:Python毕业设计实战与调优 简介这份资源是面向计算机相关专业学生与项目实战学习者的深度学习车牌识别完整源码适用于毕业设计、课程大作业及算法练手场景难度适中可帮助读者快速搭建一套可运行的车牌检测与识别系统。压缩包共约2000个文件整体约265.65MB其中1943个jpg与40个png为车牌样本图像用于模型训练与测试7个py脚本承载核心识别逻辑7个xml提供标注数据另有txt说明、gitignore与iml等工程配置文件目录结构清晰便于按模块查阅与二次开发。目前已有245人学习下载说明该方案在同类选题中具备一定参考价值。源码经本地编译调试确保可运行读者可据此理解数据组织、模型训练与推理流程并在此基础上调整网络结构或扩充数据集完成自己的设计任务。1. 车牌识别源码到底能跑出什么从一张车位照片说起地下车库出口手机随手拍一张车尾照光线偏暗、车牌带点倾斜用一套基于深度学习的车牌识别源码能不能在本地把「京A·12345」这七个字符稳定吐出来这是很多人搜「基于深度学习的车牌识别源码Python毕业设计」时真正想验证的事。它本质是一条两阶段流水线先用目标检测把车牌区域框出来再用字符识别把框里的内容读成文本。整套东西用 Python 写依赖 PyTorch 或 PaddlePaddle跑在一张普通显卡甚至 CPU 上。适合两类人一是要交毕业设计、需要一份能讲清原理又能演示的完整工程二是想入门计算机视觉、拿一个端到端小项目练手的开发者。它不解决「识别一切模糊车牌」这种玄学问题但能把标准场景下的准确率做到可用并且每一步都看得见、改得动。2. 检测加识别两阶段为什么车牌识别源码普遍这么拆2.1 单阶段端到端为什么在车牌上不划算理论上可以用一个网络直接从原图回归出车牌字符串省掉中间框。但车牌识别有个特点字符小、排列规整、背景干扰强。端到端模型要同时学「在哪」和「是什么」训练数据需求量大收敛慢而且一旦识别错你很难判断是定位偏了还是字符分类错了。两阶段把问题拆开检测只管找矩形框识别只管读框内内容两个子任务各自有成熟方案调试时能分别定位。常见做法是检测用 YOLO 系列或轻量 SSD识别用 CRNN 加 CTC 损失。这样拆的另一个好处是检测模型可以换成任意你熟悉的框架识别部分不用动接口就是「裁剪出的车牌小图」。2.2 检测分支把车牌框出来的最小实现检测部分我一般直接用 YOLOv5 或 YOLOv8 的官方仓库把车牌当作单一类别训练。数据标注用 LabelImg 画框导出 YOLO 格式的 txt。下面是一个把标注转成训练所需格式、并启动训练的最小命令序列。# 假设数据已按 images/ 和 labels/ 放好且写了 data.yaml # data.yaml 内容示例 # train: ./images/train # val: ./images/val # nc: 1 # names: [plate] # 用 YOLOv8 训练输入尺寸 640批次 16训练 100 轮 yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16这段命令里modelyolov8n.pt用的是 nano 版本参数量小毕业设计演示足够imgsz640是输入分辨率车牌在原图里占比小的话可以提到 960但显存占用会上升epochs100是轮数数据量少于两千张时容易过拟合建议配合早停。训练完在runs/detect/train/weights/best.pt拿到权重。检测阶段输出的是若干矩形框和置信度后续只保留置信度高于 0.5 且宽高比接近 3:1 到 5:1 的框这一步能滤掉大量误检。2.3 识别分支CRNN 加 CTC 把框内字符读出来识别网络输入是归一化到 32×100 左右的灰度或彩色车牌小图输出是字符序列。CRNN 的结构是 CNN 提特征、RNN 建模序列、CTC 做对齐。下面是一个 PyTorch 版 CRNN 的核心定义字符集用常见的 65 类数字、字母、省份简称。import torch import torch.nn as nn class CRNN(nn.Module): def __init__(self, num_classes65, hidden256): super().__init__() # CNN 部分5 层卷积逐步下采样输出高度为 1 的特征图 self.cnn nn.Sequential( nn.Conv2d(3, 64, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(2, 2), nn.Conv2d(64, 128, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(2, 2), nn.Conv2d(128, 256, 3, 1, 1), nn.ReLU(), nn.Conv2d(256, 256, 3, 1, 1), nn.ReLU(), nn.MaxPool2d((2, 1)), nn.Conv2d(256, 512, 3, 1, 1), nn.ReLU(), nn.MaxPool2d((2, 1)), ) # RNN 部分双向 LSTM 建模字符间依赖 self.rnn nn.LSTM(512, hidden, bidirectionalTrue, batch_firstTrue) self.fc nn.Linear(hidden * 2, num_classes) def forward(self, x): # x: [B, 3, 32, 100] feat self.cnn(x) # [B, 512, 1, W] feat feat.squeeze(2) # [B, 512, W] feat feat.permute(0, 2, 1) # [B, W, 512] out, _ self.rnn(feat) # [B, W, 2*hidden] return self.fc(out) # [B, W, num_classes]num_classes65是字符集大小实际要按你的数据集调整CTC 需要一个空白符所以类别数等于字符数加一。hidden256是 LSTM 隐藏单元显存紧张可以降到 128。前向输出形状是[批次, 时间步, 类别]配合nn.CTCLoss训练。训练时输入图像统一缩放到高 32、宽 100宽度不够的补白这一步不做的话 CTC 对齐会乱。识别阶段对输出做贪心解码或束搜索得到最终字符串。2.4 两个分支怎么串成一条推理流水线推理时先读原图送检测模型拿到框按框坐标裁剪并做透视校正再送识别模型。下面是一段串起来的推理代码骨架。import cv2 import torch from crnn import CRNN # 上面定义的模型 det torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) rec CRNN(num_classes65).eval() rec.load_state_dict(torch.load(crnn_best.pth, map_locationcpu)) img cv2.imread(car.jpg) results det(img).xyxy[0] # [x1, y1, x2, y2, conf, cls] for *box, conf, cls in results: if conf 0.5: continue x1, y1, x2, y2 map(int, box) plate img[y1:y2, x1:x2] plate cv2.resize(plate, (100, 32)) plate torch.tensor(plate).permute(2, 0, 1).float().unsqueeze(0) / 255.0 pred rec(plate).argmax(dim2) # 贪心解码 print(pred)conf 0.5是置信度阈值漏检多就降到 0.3误检多就提到 0.6。裁剪后统一 resize 到 100×32和训练输入保持一致否则识别准确率会掉。argmax(dim2)是最简单的贪心解码实际用的时候要去掉重复字符和空白符束搜索能再提一两个点。这条流水线在 1080Ti 上单张图大约 30 毫秒CPU 上 200 毫秒左右毕业设计演示完全够用。3. 数据集从哪来、怎么标、怎么划分才不翻车3.1 公开数据集和自采数据的取舍车牌识别公开数据集常见的有 CCPD 和中科大的数据集CCPD 包含几十万张带标注的车牌图覆盖多种天气和角度适合直接拿来训练。但毕业设计如果只用公开数据集答辩时容易被问「你自己做了什么」。我的建议是公开数据集打底自己用手机在停车场、小区门口拍两三百张补充重点补你学校所在地的车牌样式和光照条件。自采数据不需要多但要有代表性比如白天、夜晚、逆光、倾斜各来一些。标注时检测框要贴紧车牌边缘留白太多会让识别阶段引入背景噪声。3.2 标注格式转换和训练集验证集划分检测标注用 YOLO 格式每张图对应一个 txt每行是类别 x_center y_center width height坐标都归一化到 0 到 1。识别标注是每张裁剪图对应一个字符串标签。划分比例一般 8:1:1但要注意同一辆车的多张图不能同时出现在训练集和验证集否则验证准确率虚高。下面是一个按文件名前缀分组的划分脚本。import os import random from collections import defaultdict files [f for f in os.listdir(images) if f.endswith(.jpg)] groups defaultdict(list) for f in files: # 假设文件名前缀是车辆 ID如 car001_1.jpg groups[f.split(_)[0]].append(f) keys list(groups.keys()) random.shuffle(keys) n len(keys) train_keys keys[:int(n * 0.8)] val_keys keys[int(n * 0.8):int(n * 0.9)] test_keys keys[int(n * 0.9):] for name, ks in [(train, train_keys), (val, val_keys), (test, test_keys)]: with open(f{name}.txt, w) as fp: for k in ks: for f in groups[k]: fp.write(f \n)这段脚本按车辆 ID 分组保证同一辆车的图只进一个集合。0.8/0.1/0.1是常见比例数据量小于一千张时可以改成 7:1.5:1.5让验证集更有统计意义。划分完要检查一下每个集合的字符分布如果某个省份简称只在训练集出现验证集上必然识别不了这种长尾问题在毕业设计里很常见提前发现比答辩时被问住好。3.3 数据增强里哪些有用、哪些是负优化车牌识别的数据增强要克制。随机旋转正负 5 度、亮度对比度微调、轻微高斯噪声这三样基本都有正收益。但随机裁剪要小心裁掉车牌边缘会让 CTC 对齐困难颜色抖动幅度太大蓝色车牌变紫色识别模型会学偏。我一般用 Albumentations 配一组保守的增强训练时在线做验证集不做增强。下面是一个配置片段。import albumentations as A train_tf A.Compose([ A.Rotate(limit5, p0.5), A.RandomBrightnessContrast(0.2, 0.2, p0.5), A.GaussNoise(var_limit(5, 20), p0.3), A.Resize(32, 100), ])limit5是旋转角度上限超过 10 度车牌字符会明显变形RandomBrightnessContrast的 0.2 是幅度再大就失真GaussNoise的方差上限 20 是经验值再高会淹没字符边缘。增强只作用于训练集验证和测试保持原图这样评估出来的指标才反映真实场景。4. 训练参数怎么设检测和识别两套配置的踩坑记录4.1 检测模型的学习率和锚框调整YOLO 训练默认学习率 0.01但车牌数据集通常只有几千张这个值偏大容易在前期震荡。我一般降到 0.001配合余弦退火。锚框方面YOLOv5 默认锚框是针对 COCO 的车牌宽高比集中在 3:1 到 5:1直接用手动聚类出来的锚框更稳。下面是用 k-means 聚自己数据锚框的命令。# 用 YOLOv5 自带的聚类脚本输入标注路径和锚框数量 python utils/autanchor.py --data data.yaml --n 9 --img-size 640--n 9是锚框数量和 YOLO 头部的输出层数对应--img-size 640要和训练输入一致。聚类完把结果填进模型配置的 anchors 字段。这一步不做的话检测框回归会慢很多尤其是小车牌。学习率调度用--cos-lr开启余弦退火训练后期更稳。4.2 识别模型的 CTC 损失和批次组织CRNN 训练用nn.CTCLoss关键是输入长度和标签长度的对齐。批次里每张图的宽度可能不同统一 resize 到 100 后时间步固定但标签长度不一CTC 会自动处理。下面是一个训练循环的核心片段。import torch.nn as nn ctc nn.CTCLoss(blank0, zero_infinityTrue) optimizer torch.optim.Adam(rec.parameters(), lr1e-3) for imgs, labels, label_lens in loader: logits rec(imgs) # [B, T, C] log_probs logits.log_softmax(2).permute(1, 0, 2) # [T, B, C] input_lens torch.full((imgs.size(0),), logits.size(1), dtypetorch.long) loss ctc(log_probs, labels, input_lens, label_lens) optimizer.zero_grad() loss.backward() optimizer.step()blank0指定空白符索引要和字符集定义一致zero_infinityTrue防止某些样本长度不匹配导致 loss 无穷大这个参数不加的话训练中途会突然崩掉。log_probs的维度顺序必须是[时间步, 批次, 类别]CTC 要求这样写反了 loss 不降反升。学习率 1e-3 配 Adam 是识别任务的常用起点训练不收敛就降到 5e-4。4.3 两个模型分开训还是一起训常见做法是分开训先训检测冻结检测权重用检测框裁剪出的图训识别。一起训理论上能端到端优化但检测框在训练初期抖动大识别分支拿到的输入不稳定收敛慢。毕业设计时间有限分开训更可控而且两个模型可以独立替换。如果非要一起训建议先各自训到收敛再用小学习率联合微调几轮别从零开始联合训练。5. 推理部署和常见问题排查那些让准确率掉一半的坑5.1 检测框偏一点识别就全错现象检测框把车牌上下边缘切掉一点识别结果缺字或错字。原因检测模型回归的框不够紧或者透视校正没做。解决训练检测时把标注框贴紧车牌推理时对检测框向外扩 5% 再裁剪给识别留余量车牌倾斜明显时加一步透视变换用框的四个角点做校正。这一步不做倾斜车牌的识别率会掉三成以上。5.2 字符集顺序和标签对不上现象训练 loss 正常下降但推理输出全是乱码。原因字符集字典的顺序和标签编码时的顺序不一致比如训练时用0-9A-Z推理时用A-Z0-9。解决把字符集写成一个固定的列表文件训练和推理都从这个文件读别在两处各写一份。这个坑我踩过排查了一下午才发现是字典顺序问题。5.3 验证集准确率高实际图片一塌糊涂现象验证集上识别准确率 95%拿自己拍的照片测试只有一半对。原因验证集和训练集同分布都是公开数据集而自拍照片的光照、角度、分辨率不同。解决划分验证集时混入自采数据或者单独留一批自拍图做测试集。评估指标要按场景分开看别只看一个总数。5.4 CPU 推理慢到没法演示现象答辩现场没显卡CPU 上单张图要一两秒。解决检测模型换成 YOLOv8n 或更小的版本识别模型把 LSTM 隐藏单元降到 128输入宽度从 100 降到 80。还可以用 ONNX Runtime 做推理加速把 PyTorch 模型导出成 ONNXCPU 上能快两到三倍。导出命令是torch.onnx.export注意动态轴要设对否则批次维度写死。5.5 中文省份简称识别不出来现象数字和字母都对就是第一个汉字错。原因省份简称样本少字符集里虽然有一类但训练时出现次数远低于数字字母模型偏向多数类。解决对省份简称做过采样或者在 loss 里给这类字符更高权重。数据增强时对含省份简称的图多复制几份简单但有效。6. 把准确率再往上推一个可复现的调优技巧如果基础版本已经跑通想再提几个点我一般从识别分支的束搜索和解码后处理入手。贪心解码每个时间步取最大概率容易在相似字符上出错比如0和O、1和I。束搜索保留前 k 个候选路径最后选总概率最高的。下面是一个简单的束搜索实现。def beam_search(log_probs, beam_width5, blank0): # log_probs: [T, C] beams [([], 0.0)] # (路径, 对数概率) for t in range(log_probs.size(0)): new_beams [] for path, score in beams: topk log_probs[t].topk(beam_width) for idx, logp in zip(topk.indices, topk.values): idx idx.item() new_path path ([idx] if idx ! blank else []) new_beams.append((new_path, score logp.item())) # 合并相同路径保留概率最高的 beam_width 个 merged {} for p, s in new_beams: key tuple(p) if key not in merged or merged[key] s: merged[key] s beams sorted(merged.items(), keylambda x: -x[1])[:beam_width] return beams[0][0]beam_width5是保留的候选数再大收益递减且变慢blank0要和训练时一致。束搜索之后再做一层规则后处理车牌第一个字符必须是省份简称第二位必须是字母后面五位是字母数字混合。按这个规则过滤掉不合法的候选准确率还能再提一点。这个规则因车牌类型而异新能源车牌是八位写死之前先确认你的数据里有没有。调优这件事没有银弹我自己的习惯是每次只改一个变量改完在固定测试集上跑一遍记录指标。改两个地方一起上涨了不知道哪个起作用跌了不知道哪个背锅。这套源码的价值不在于它开箱即用而在于每个环节你都能拆开看、动手改改完能看到反馈。希望帮到你。本文还有配套的精品资源点击获取
返回列表