简介:这份资源面向计算机、人工智能及相关专业的本科生与初学者,提供一套基于CNN与YOLOv5的车牌检测与识别完整工程,数据集采用CCPD官方数据集,可用于毕业设计、课程设计、大作业、工程实训及学科竞赛等场景,帮助读者快速搭建并复现车牌识别流程。压缩包共10个文件,约44.33MB,包含Python脚本、模型权重文件、YAML配置、Keras与TensorFlow模型文件、Jupyter Notebook及README说明文档,覆盖训练、检测、识别与模型加载等环节,结构清晰便于按模块查阅。目前已有78人学习下载。资源经过测试运行,功能完整,读者可参照说明文件复现项目,也可在此基础上修改扩展,实现其他检测识别功能,适合作为学习练手与项目立项的参考。
1. 车牌检测识别项目拆包:CNN 加 YOLOv5 这套组合拳到底能不能打
车牌识别这个方向,每年毕业设计和课程设计都有人做,但真正能跑通、能复现、答辩不被问穿的并不多。我拿到这个包的第一反应是看目录结构——plate_rec.py、plate.yaml、best.pt、train.ipynb、plateRec_model、plate_dec.py、README.md,外加 Keras 的saved_model.pb和keras_metadata.pb。这个组合说明它不是单纯丢一个 YOLOv5 权重让你自己猜,而是把检测和识别拆成了两段:YOLOv5 负责从整图里框出车牌位置,CNN 分类网络负责把框出来的车牌字符逐个认出来。数据集用的是 CCPD 官方数据集,国内车牌场景覆盖比较全,蓝牌、绿牌、黄牌都有,光照和倾斜角度也够杂。适合谁?手上有毕设或课设任务、需要一套能跑通的全流程代码、又不想从零标注数据的人。不适合谁?想直接拿去做商业级车牌识别、要求毫秒级响应和极端天气鲁棒性的人——这套代码的定位是教学和复现,不是产线部署。
2. 环境搭建与 CCPD 数据集处理:从 conda 到 YOLO 格式的完整链路
2.1 环境依赖与版本选择
这个包用的是 YOLOv5 做检测,所以环境配置绕不开 PyTorch 和 ultralytics 那套依赖。我一般会先建一个干净的 conda 环境,避免和系统里已有的 torch 版本打架。常见做法是 Python 3.8 或 3.9,PyTorch 选 1.10 到 1.13 之间的版本,太新的版本有时候和 YOLOv5 的某些算子不兼容。
conda create -n plate_rec python=3.9 -y conda activate plate_rec pip install torch==1.13.1 torchvision==0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -r requirements.txt这里requirements.txt里通常包含opencv-python、numpy、matplotlib、pyyaml、tqdm这些。如果你的机器没有 GPU,把cu117换成cpu就行,但训练速度会差很多。plate.yaml是 YOLOv5 的数据配置文件,里面定义了训练集、验证集路径和类别数。这个文件不要随便改路径,除非你确定自己的数据集放在哪。
2.2 CCPD 数据集的结构与转换
CCPD 官方数据集下载下来是一堆以文件名编码信息的图片,比如025-95_113-226&469_448&522-444&524_232&521_228&469_447&472-0_0_22_27_27_33_16-64-88.jpg。文件名里包含了车牌框坐标、四个角点、车牌号码等信息。YOLOv5 需要的是每张图对应一个.txt标注文件,格式是class x_center y_center width height,归一化到 0 到 1。
import os import cv2 import numpy as np def ccpd_to_yolo(img_dir, label_dir): os.makedirs(label_dir, exist_ok=True) for fname in os.listdir(img_dir): if not fname.endswith('.jpg'): continue parts = fname.split('-') # 车牌框坐标在第三段,格式为 x1&y1_x2&y2 bbox = parts[2].split('_') x1, y1 = map(int, bbox[0].split('&')) x2, y2 = map(int, bbox[1].split('&')) img_path = os.path.join(img_dir, fname) img = cv2.imread(img_path) h, w = img.shape[:2] x_center = (x1 + x2) / 2.0 / w y_center = (y1 + y2) / 2.0 / h bw = (x2 - x1) / w bh = (y2 - y1) / h label_path = os.path.join(label_dir, fname.replace('.jpg', '.txt')) with open(label_path, 'w') as f: f.write(f"0 {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}\n")这段代码的逻辑是从文件名里解析出车牌框的左上角和右下角坐标,然后转成 YOLO 需要的中心点加宽高格式。参数说明:img_dir是 CCPD 图片目录,label_dir是输出标注目录。注意 CCPD 有些图片文件名格式不完全一致,比如新能源车牌长度不同,解析的时候要加异常处理,不然会翻车。转换完之后,在plate.yaml里把train和val指向对应的图片目录,nc设为 1,因为只有车牌一个类别。
2.3 训练配置与启动
train.ipynb里一般会调用 YOLOv5 的训练脚本。如果你习惯命令行,也可以直接跑:
python train.py --img 640 --batch 16 --epochs 100 --data plate.yaml --weights yolov5s.pt --project runs/train --name plate_exp--img 640是输入分辨率,CCPD 图片普遍偏大,640 够用;--batch 16看显存,8G 显存跑 16 差不多,不够就降到 8;--epochs 100是训练轮数,一般 50 到 100 轮就能收敛;--weights yolov5s.pt是预训练权重,用官方在 COCO 上训过的模型做迁移学习,比从零训快得多。训练过程中重点看mAP@0.5和box_loss,如果mAP一直不涨,先检查标注文件有没有写错,尤其是归一化坐标有没有超过 1。
3. CNN 识别网络与检测后处理:从车牌框到字符序列
3.1 plateRec_model 的结构与加载
plateRec_model目录下是 Keras 保存的模型文件,saved_model.pb是模型结构,keras_metadata.pb是元数据,variables里是权重。这个 CNN 网络的作用是输入一张裁剪好的车牌图,输出每个字符的概率分布。国内蓝牌是 7 个字符,新能源是 8 个,所以输出层通常是 7 或 8 个 softmax 分支,每个分支对应一个字符位置。
import tensorflow as tf model = tf.keras.models.load_model('plateRec_model') print(model.summary())加载之后先看summary(),确认输入尺寸和输出维度。常见输入是(80, 240, 3)或者(64, 128, 3),输出是(7, 65)这种形状,65 表示字符类别数(数字、字母、省份简称加起来)。如果加载报错,大概率是 TensorFlow 版本不匹配,这个包一般用 TF 2.x,别用 TF 1.x 去加载。
3.2 plate_rec.py 的推理流程
plate_rec.py是整个项目的入口脚本,逻辑一般是:读图 → YOLOv5 检测车牌框 → 裁剪车牌区域 → 送进 CNN 识别 → 输出车牌号。我拆过类似的代码,核心步骤大概是这样:
import cv2 import numpy as np import torch # 加载 YOLOv5 模型 yolo_model = torch.hub.load('ultralytics/yolov5', 'custom', path='best.pt') yolo_model.conf = 0.5 # 置信度阈值 def detect_and_recognize(img_path): img = cv2.imread(img_path) results = yolo_model(img) boxes = results.xyxy[0].cpu().numpy() for box in boxes: x1, y1, x2, y2, conf, cls = box plate_img = img[int(y1):int(y2), int(x1):int(x2)] plate_img = cv2.resize(plate_img, (240, 80)) plate_img = plate_img.astype('float32') / 255.0 plate_img = np.expand_dims(plate_img, axis=0) preds = cnn_model.predict(plate_img) plate_number = decode_preds(preds) print(f"车牌号: {plate_number}, 置信度: {conf:.2f}")conf = 0.5是检测置信度阈值,调高会漏检,调低会误检,CCPD 场景下 0.5 到 0.6 比较稳。cv2.resize的尺寸要和 CNN 训练时的输入尺寸一致,不一致会报维度错误。decode_preds是自定义的解码函数,把 softmax 输出映射回字符,这个函数一般在plate_dec.py里。
3.3 plate_dec.py 的解码逻辑
plate_dec.py负责把 CNN 输出的概率矩阵转成可读的车牌字符串。常见做法是每个位置取 argmax,然后查字符表。字符表一般包含省份简称、数字 0-9、字母 A-Z,去掉容易混淆的 I 和 O。
CHARS = ['京', '沪', '粤', '津', '冀', '晋', '蒙', '辽', '吉', '黑', '苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '桂', '琼', '渝', '川', '贵', '云', '藏', '陕', '甘', '青', '宁', '新', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] def decode_preds(preds): indices = np.argmax(preds[0], axis=-1) return ''.join([CHARS[i] for i in indices])这里preds[0]是 batch 里第一张图的输出,形状是(7, 65)。np.argmax取每个位置概率最大的索引,然后查CHARS表。注意字符表的顺序必须和训练时一致,不然识别结果会全错。这个坑我踩过,当时字符表顺序换了,结果车牌号全是乱码。
4. 避坑与常见问题排查:那些让你怀疑人生的报错
4.1 现象:训练 loss 不下降,mAP 一直是 0
原因:标注文件格式不对,最常见的是坐标没有归一化,或者类别索引写成了 1 而不是 0。CCPD 转换脚本里如果没除以宽高,YOLOv5 读进去就是越界坐标,直接忽略。解决:打开一个.txt标注文件,确认四个值都在 0 到 1 之间,且类别是 0。
4.2 现象:CNN 识别结果全是同一个字符
原因:输入图片没有做归一化,或者归一化方式不对。训练时如果用了rescale=1./255,推理时也要除以 255。另外,如果裁剪的车牌区域是空的(YOLO 没检测到),送进去的是一张全黑或全白的图,CNN 会输出一个固定结果。解决:在裁剪后加一个判断,如果plate_img.size == 0就跳过,并检查 YOLO 的置信度阈值是不是太高。
4.3 现象:加载 best.pt 报错 RuntimeError: Attempting to deserialize object on a CUDA device
原因:模型是在 GPU 上保存的,当前环境没有 GPU 或者 CUDA 不可用。解决:在加载时加map_location='cpu':
yolo_model = torch.hub.load('ultralytics/yolov5', 'custom', path='best.pt', map_location='cpu')或者把模型转到 CPU 再推理。这个报错在换机器跑的时候特别常见,血泪经验是先在本地 CPU 上跑通再上 GPU。
4.4 现象:TensorFlow 加载 saved_model 报版本不兼容
原因:saved_model.pb是用特定 TF 版本保存的,TF 2.3 和 TF 2.10 之间都有差异。解决:先看README.md里有没有写 TF 版本,没有的话试 2.5 到 2.8 之间的版本。如果还是不行,用tf.saved_model.load()而不是tf.keras.models.load_model(),前者兼容性更好。
4.5 现象:推理速度特别慢,一张图要好几秒
原因:YOLOv5 默认输入尺寸是 640,如果图片分辨率是 4K,预处理会花很多时间。另外,如果每次推理都重新加载模型,那更慢。解决:把模型加载放在循环外面,只加载一次;推理前把图片缩放到 1280 宽以内;如果还是慢,把 YOLOv5 换成yolov5n或yolov5s,别用m或l。
5. 进阶技巧:用 train.ipynb 微调与验证模型效果
5.1 在 CCPD 子集上做微调
train.ipynb不只是给你跑一遍训练,它其实是一个可以改的模板。如果你想在特定场景下提升效果,比如只识别蓝牌,可以把 CCPD 里蓝牌的图片单独抽出来,重新跑一遍训练。常见做法是先用完整数据集训一个基线,然后针对误检多的场景做增量训练。
# 在 train.ipynb 里修改 data 配置 data: train: /path/to/ccpd_blue/train/images val: /path/to/ccpd_blue/val/images nc: 1 names: ['plate']把plate.yaml里的路径改成子集路径,epochs设 30 到 50,lr0调小到 0.001,这样不会把预训练权重带偏。微调完之后用val.py跑一遍验证,看mAP@0.5有没有提升。
5.2 用混淆矩阵定位识别错误
CNN 识别部分最容易出错的是相似字符,比如0和O、1和I、8和B。可以在plate_dec.py里加一个混淆矩阵统计,把预测结果和真实标签对比,看看哪些字符经常被认错。
from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt # 假设 y_true 和 y_pred 是展平后的字符索引列表 cm = confusion_matrix(y_true, y_pred) sns.heatmap(cm, annot=False, cmap='Blues') plt.savefig('confusion_matrix.png')跑完之后看热力图,如果0和O交叉位置颜色很深,说明这两个字符混淆严重。解决办法是在训练时增加这两个字符的样本权重,或者在解码时加规则:车牌第二位不可能是O,只能是字母,这样可以直接排除掉。
5.3 验证模型是否真的复现成功
复现的标准不是“能跑”,而是“结果可重复”。我一般会做三件事:第一,用同一张图跑三次,看输出是否完全一致;第二,用best.pt在验证集上跑一遍,看mAP是否和 README 里写的一致;第三,把plate_rec.py里的推理结果和人工标注对比,随机抽 20 张图,看准确率有没有超过 90%。如果这三步都过了,说明这个包是真的能复现,不是那种只给你一个空壳的。
从那以后我每次拿到这种检测加识别的包,都强制走一遍“单图三次一致性 + 验证集 mAP + 随机抽样人工比对”的流程,少一步都不敢说它跑通了。希望帮到你。
本文还有配套的精品资源,点击获取