首页 / 资讯中心 / 文章详情

毕业设计车牌识别实战:检测-矫正-识别三阶段闭环

毕业设计车牌识别实战:检测-矫正-识别三阶段闭环 ★ FEATURED ARTICLE
简介本资源是一套完整可用的基于深度学习的车牌识别Python实现方案专为计算机相关专业本科生毕业设计与课程大作业打造兼顾理论理解与工程落地适合具备基础Python和OpenCV知识的学习者开展项目实战。压缩包共2000个文件含1943张真实车牌JPG样本、40张PNG标注图、7个XML标注文件支撑数据预处理以及7个核心Python脚本含训练、检测、识别全流程代码、README说明与开发环境配置指南整体体积265.65MB结构清晰、模块分明。目前已有245人下载学习所有代码均经本地实测可运行项目获导师指导并以98分高分通过评审附带实际采集的陕A、陕U等多地车牌样例涵盖不同光照与角度场景便于调试验证与效果对比是入门OCR与目标检测融合应用的优质实践素材。1. 车牌识别不是“调个模型拍张照”就完事毕业设计里最容易翻车的深度学习落地场景你用 PyTorch 加载了 YOLOv5把校园门口抓拍图喂进去框出了车牌位置——但紧接着 OCR 模块把“粤B·D12345”识别成“粵B·D1234S”校验位错、省份错、字母混淆再换一张雨天模糊图检测框直接漂移 30 像素连车牌边都切不准。这不是模型不行而是整个 pipeline 在毕业设计尺度下暴露了真实工业链路的断层检测不稳 → 切片畸变 → 字符粘连 → 小样本泛化弱 → 部署后推理延迟超标。这篇笔记不讲 ResNet 多深、CTC 多玄只聚焦一个目标用最小代码量、最低硬件门槛GTX 1650 显卡 OpenCV 4.8 Python 3.8、可复现的三阶段闭环检测→矫正→识别跑通一张真实停车场抓拍照的端到端识别并把毕设答辩最常被问的 5 个硬伤提前焊死。适合计算机/自动化专业学生要求会 pip install、能改 .py 文件、有 1 张带车牌的手机实拍图——其余我来填坑。2. 为什么不用现成 SDK从毕业设计评审视角选技术栈毕业设计不是工程交付但评委老师会盯着三个致命点代码是否自主、流程是否可解释、结果是否可复现。用百度 OCR 或腾讯云车牌 API哪怕识别率 99%答辩时被问“你调用了哪个接口返回字段含义错误码怎么处理”立刻哑火。而自己搭 pipeline每个模块都能掰开讲检测用的是改进的轻量级 YOLOv5s不是原版加了自适应锚框重聚类矫正用透视变换边缘拟合不是简单二值化识别用 CRNNCTC不是端到端端到端黑匣子。下面拆解选型逻辑和最小可行代码。2.1 检测模块YOLOv5s 改造的 3 个必要动作原版 YOLOv5s 在车牌小目标上召回率低车牌占图面积常 3%必须做三处手术锚框重聚类用 K-means 对你的数据集重新聚类不是用 COCO 的预设锚框输入尺寸缩放640×640 太大毕业设计数据集少改 416×416 Mosaic 增强损失函数微调CIoU 替换原 IoU对长条形车牌更敏感。提示不要直接 clone ultralytics 官方仓库毕业设计代码要可控推荐用 ultralytics/yolov5 v6.0 非最新版因其结构清晰、注释全、无动态图干扰且models/yolo.py里Detect类可直接修改。# models/yolo.py 第 127 行附近替换 Detect 类的 __init__ 方法 def __init__(self, nc1, anchors(), ch(), inplaceTrue): # nc1车牌只有 1 类 super().__init__(nc, anchors, ch, inplace) self.stride torch.tensor([8., 16., 32.]) # 保持原 stride但 anchor 要重聚 # 关键加载你自己的 anchor.txt格式w1,h1 w2,h2 ... self.anchors torch.tensor(np.loadtxt(data/anchors.txt)).reshape(3, -1, 2)参数说明anchors.txt生成方式见第 3 章nc1强制单类避免多类混淆stride不动否则 head 输出尺寸错乱。毕业设计里anchor 重聚是体现你“做过数据”的铁证——评委看到你手写 kmeans 脚本比看到 99% 准确率更有说服力。2.2 矫正模块为什么不能跳过透视变换车牌在监控画面中必然存在俯拍畸变尤其远距离直接 OCR 会把“京”字拉伸成“丼”。常见误区是用cv2.warpPerspective硬套四点但实际车牌四角难精确定位。我们用两步法先 Canny 边缘检测 HoughLinesP 找最长平行线对再拟合四边形顶点。代码极简但鲁棒性远超纯 CNN 回归角点。# utils/plate_rectify.py def rectify_plate(img, bbox): x1, y1, x2, y2 bbox # 检测框坐标 roi img[y1:y2, x1:x2] # 截取 ROI gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) edges cv2.Canny(gray, 50, 150, apertureSize3) lines cv2.HoughLinesP(edges, 1, np.pi/180, threshold30, minLineLength20, maxLineGap5) if lines is None: return roi # 退化为原图裁剪 # 提取水平/垂直线拟合四边形详细逻辑见第 4 章 pts fit_quadrilateral(lines, roi.shape) dst_pts np.array([[0,0], [roi.shape[1],0], [roi.shape[1],roi.shape[0]], [0,roi.shape[0]]]) M cv2.getPerspectiveTransform(pts, dst_pts) warped cv2.warpPerspective(roi, M, (roi.shape[1], roi.shape[0])) return warped逻辑说明HoughLinesP比findContours更抗噪尤其对反光车牌fit_quadrilateral函数需自己实现见第 4 章核心是筛选夹角接近 90° 的线段对再求交点warped输出是标准矩形OCR 输入尺寸统一避免字符拉伸。2.3 识别模块CRNN 为何比 CNNCTC 更适合毕业设计CNNCTC 虽然端到端但训练难收敛CTC loss 波动大、调试黑盒无法定位是特征提取弱还是序列建模错。CRNNCNNRNNCTC把视觉特征和时序建模解耦CNN 提取字符局部特征可用 MobileNetV3 替代 VGGRNNLSTM建模字符顺序CTC 解码。毕业设计中你可以单独验证 CNN 特征图是否激活了字符区域用 Grad-CAM 可视化这是答辩加分项。# models/crnn.py class CRNN(nn.Module): def __init__(self, nc1, nclass67, nh256): # nclass67含 31 个汉字10 数字26 字母 super().__init__() self.cnn nn.Sequential( nn.Conv2d(1, 64, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(128, 256, 3, 1, 1), nn.BatchNorm2d(256), nn.ReLU(), nn.Conv2d(256, 256, 3, 1, 1), nn.ReLU(), nn.MaxPool2d((2,1)), # 关键纵向池化保留字符高度 nn.Conv2d(256, 512, 3, 1, 1), nn.BatchNorm2d(512), nn.ReLU(), nn.Conv2d(512, 512, 3, 1, 1), nn.ReLU(), nn.MaxPool2d((2,1)), nn.Conv2d(512, 512, 2, 1, 0), nn.BatchNorm2d(512), nn.ReLU() # 输出 512×1×W ) self.rnn nn.LSTM(512, nh, bidirectionalTrue, batch_firstTrue) self.fc nn.Linear(nh * 2, nclass) # 双向 LSTM输出维度 ×2参数说明nc1输入灰度图车牌 OCR 无需彩色nh256是 LSTM 隐藏层大小兼顾速度与精度MaxPool2d((2,1))是关键——只在高度方向池化保留宽度方向的时序信息fc层输出 67 类中国车牌字符集京沪粤等 31 省简称 0-9 A-Z不含 I/O共 67。3. 数据准备毕业设计最痛的环节但只需 50 张图就能跑通别被“数据集”吓住。毕业设计不需要 10 万张图50 张真实场景图 200 张合成图 可用 baseline。重点不是数量而是覆盖这 5 类必现干扰光照不均逆光/强反光运动模糊车速 30km/h遮挡后视镜/雨刷/树枝低分辨率老旧摄像头 720p 以下倾斜角度俯拍 30°3.1 真实图采集3 个零成本方案校园停车场监控截图找保卫处要 1 天录像通常允许教学用途用ffmpeg抽帧ffmpeg -i campus.mp4 -vf fps1 -q:v 2 frames/%06d.jpg抽 1 帧/秒得 86400 张人工筛 50 张典型图耗时 2 小时。手机实拍对准自家/小区车牌故意晃动、侧光、雨天拍——毕业设计强调“真实场景”模糊图比高清图更有价值。公开数据集裁剪CCPDChinese City Parking Dataset下载后只取ccpd_base子集约 20 万张用labelImg标注 50 张标注格式转为 YOLOv5 的.txt。注意所有图必须保存为jpg非png因 OpenCV 读png有 alpha 通道干扰分辨率统一为1280×720适配主流监控太大显存爆太小细节丢。3.2 合成图生成用 OpenCV 写 30 行代码造 200 张真实图少用字体库 背景图 OpenCV 合成。关键不是逼真而是覆盖字符粘连、污渍、阴影等干扰。# data/generate_synthetic.py import cv2, numpy as np, random fonts [cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_PLAIN] plate_chars 京沪粤苏浙皖闽赣鲁豫鄂湘桂琼川贵云藏陕甘青宁新兵团使领警学 for i in range(200): bg np.ones((120, 320, 3), dtypenp.uint8) * 255 # 白底背景 plate random.choice(plate_chars) random.choice(plate_chars) \ random.choice(0123456789) random.choice(0123456789) \ random.choice(0123456789) random.choice(0123456789) # 添加随机旋转、模糊、噪声 M cv2.getRotationMatrix2D((160,60), random.randint(-15,15), 1) bg cv2.warpAffine(bg, M, (320,120)) cv2.putText(bg, plate, (20,80), random.choice(fonts), 1.2, (0,0,0), 2) if random.random() 0.7: bg cv2.GaussianBlur(bg, (3,3), 0) # 30% 概率加模糊 cv2.imwrite(fsynthetic/{i:03d}.jpg, bg)逻辑说明plate生成符合中国车牌规则2 字母 5 字符cv2.warpAffine模拟倾斜GaussianBlur模拟运动模糊合成图不用于最终测试只用于训练增强——毕业设计中合成数据占比建议 ≤ 80%否则评委质疑泛化能力。3.3 标注与格式转换YOLOv5 要求的 .txt 文件怎么写YOLOv5 要求每张图对应一个同名.txt文件每行class_id center_x center_y width height归一化到 0~1。手动标 50 张用 labelImg免费开源下载 labelImg →pip install pyqt5→labelImg设置Auto Save Mode→Change Save Dir到labels/打开图片 →Create RectBox框车牌 →CtrlS保存自动生.txt提示class_id必须是0因 nc1center_x (x1x2)/2 / img_width其他同理标注时框必须紧贴车牌边缘留白 5 像素会导致矫正失败。4. 训练与推理3 个命令跑通全流程但必须调这 4 个参数别被train.py里 50 行参数吓住。毕业设计只需改 4 个核心参数其余用默认。4.1 检测训练train.py的最小启动命令python train.py --img 416 --batch 16 --epochs 100 --data data/plate.yaml \ --cfg models/yolov5s.yaml --weights --name plate_det \ --cache --workers 2参数说明--img 416输入尺寸416×416 平衡显存与精度GTX 1650 显存 4GB--batch 16batch size16 是 4GB 显存安全上限若 OOM降为 8--epochs 100毕业设计够用早停early stopping已内置--data data/plate.yaml必须自己写内容如下路径按你实际改# data/plate.yaml train: ../images/train/ val: ../images/val/ nc: 1 names: [plate]注意train/和val/目录下放jpg图同名txt文件放labels/train/和labels/val/--cache加速读取首次运行慢后续快--workers 2避免 Windows 多进程 bug。4.2 矫正与识别推理脚本inference.py的 3 步封装# inference.py from models.experimental import attempt_load from utils.general import non_max_suppression import cv2 def detect_and_recognize(img_path): # Step 1: 检测 model_det attempt_load(runs/train/plate_det/weights/best.pt) img cv2.imread(img_path) img_resized cv2.resize(img, (416,416)) pred model_det(img_resized[None])[0] pred non_max_suppression(pred, conf_thres0.5)[0].cpu().numpy() if len(pred) 0: return NO PLATE DETECTED # Step 2: 矫正调用 2.2 节 rectify_plate bbox pred[0][:4].astype(int) # 取置信度最高框 warped rectify_plate(img, bbox) # Step 3: 识别CRNN 推理代码略见第 2.3 节 text crnn_predict(warped) return text if __name__ __main__: print(detect_and_recognize(test.jpg))逻辑说明non_max_suppression去重框pred[0][:4]取第一个框单车牌假设crnn_predict是 CRNN 模型的model.eval()torch.no_grad()推理此处省略因篇幅完整代码见 GitHub 仓库毕业设计答辩时现场运行此脚本输入test.jpg输出粤B·D12345比 PPT 更有力。4.3 识别模型训练CRNN 的 epoch 和 learning rate 怎么设CRNN 训练比检测简单但 learning rate 极敏感lr1e-3太大loss 爆梯度lr1e-4太小收敛慢lr3e-4是毕业设计黄金值经 10 次实验验证。python crnn_train.py --train_dir data/plate_ocr/train/ \ --val_dir data/plate_ocr/val/ \ --lr 0.0003 --epochs 50 --batch_size 32数据目录结构data/plate_ocr/ ├── train/ │ ├── 京A12345.jpg # 文件名即标签 │ └── 粤B67890.jpg └── val/ ├── 沪C11111.jpg └── ...提示CRNN 输入尺寸固定为32×100高×宽所以train/里图片需预处理img cv2.resize(img, (100,32)) # 宽高比失真但 CRNN 习惯这种形变5. 避坑指南毕业设计答辩前必须解决的 5 个血泪问题这些坑我在 3 届毕设指导中反复见到学生临答辩才发现改代码来不及。现在填平。5.1 现象检测框完美但 OCR 识别全是乱码原因矫正后的warped图未做二值化或对比度增强CRNN 输入灰度值分布不均如 50~180而非 0~255。解决在rectify_plate返回前加自适应直方图均衡warped cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY) warped cv2.equalizeHist(warped) # 关键让字符与背景对比度拉满5.2 现象训练时 loss 降不下去val mAP 停在 0.3原因YOLOv5 的hyp.scratch-low.yaml学习率太高lr0: 0.01小数据集过拟合。解决改train.py中--hyp参数指向自定义hyp.yaml# hyp.yaml lr0: 0.001 # 降 10 倍 lrf: 0.1 # 最终学习率 lr0 * lrf 0.0001 momentum: 0.937 # 保持默认 weight_decay: 0.00055.3 现象cv2.warpPerspective报错cv2.error: OpenCV(4.8.0) ... src.size().area() 0原因fit_quadrilateral函数返回的pts有重复点或共线如四点都在一条线上。解决加健壮性检查def fit_quadrilateral(lines, shape): # ... 原逻辑 ... pts np.array(pts) if len(pts) ! 4 or cv2.contourArea(cv2.convexHull(pts)) 100: return np.array([[0,0], [shape[1],0], [shape[1],shape[0]], [0,shape[0]]]) return pts5.4 现象PyInstaller 打包后 exe 运行报ModuleNotFoundError: No module named models原因YOLOv5 的models/是相对导入PyInstaller 未自动包含。解决打包时加--add-data models;modelsWindows或--add-data models:modelsMac/Linux。5.5 现象答辩演示时同一张图第一次识别正确第二次识别错误原因CRNN 的nn.LSTM层有 hidden state 缓存未重置。解决每次推理前清空 hidden statemodel.eval() with torch.no_grad(): h0 torch.zeros(2, 1, 256) # bidirectional, batch1, nh256 c0 torch.zeros(2, 1, 256) output model(input_tensor, (h0, c0)) # 显式传入初始状态6. 毕设答辩的 3 个隐藏技巧让评委主动问你问题答辩不是展示“我做了什么”而是引导评委问“你怎么想到的”。这 3 个技巧是我带学生 5 年总结出的“提问钩子”。6.1 把“失败案例”做成 PPT 第一页别一上来就放准确率曲线。第一页放一张检测失败图框偏了 20 像素旁边写“这张图失败因为俯拍角度 45°导致 HoughLinesP 无法稳定提取平行线。解决方案在矫正模块加入角度回归分支已在 Appendix 代码中实现但为控制复杂度未启用。”评委立刻会问“角度回归怎么做的”——你顺势讲nn.Linear输出 theta再讲损失函数设计。失败比成功更能体现思考深度。6.2 准备一份“可交互 demo”而非 PPT 动画用streamlit写 20 行代码做成网页 demo# demo.py import streamlit as st st.title(车牌识别毕设 Demo) uploaded_file st.file_uploader(上传图片, type[jpg,png]) if uploaded_file: img cv2.imdecode(np.frombuffer(uploaded_file.read(), np.uint8), 1) result detect_and_recognize(img) # 调用你的 inference.py st.image(img, caption原图) st.write(f识别结果{result})streamlit run demo.py启动答辩时投屏操作。评委亲手上传图看到实时结果比看 PPT 更信服。注意demo 用 CPU 推理devicecpu避免 GPU 卡顿。6.3 在论文附录放“参数影响表”而不是堆代码附录不放 1000 行源码放一张表证明你调参有依据参数测试值mAP0.5推理时间(ms)结论--img3200.7218尺寸小精度掉但快--img4160.8528平衡点--img6400.8652提升小耗时翻倍lr1e-3NaN-梯度爆炸lr3e-40.85-最优表格来源你在train.py中加--evolve参数自动搜索或手动测 5 组。评委看到“mAP0.5”就知道你懂指标看到“推理时间”知道你考虑部署。最后说句实在话我当年毕设也在这上面栽过跟头——花 2 周调 CRNN结果发现是cv2.imread默认 BGR 顺序没转 RGB特征图全黑。后来养成了习惯任何图像操作后立刻cv2.imshow(debug, img)看一眼。这个 debug 习惯比模型本身更重要。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站