尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于YOLOv5与CNN的车牌检测识别实战:CCPD数据集全流程解析

发布时间:2026/9/24 18:04:52

资讯中心
01
ARTICLE

基于YOLOv5与CNN的车牌检测识别实战:CCPD数据集全流程解析

基于YOLOv5与CNN的车牌检测识别实战:CCPD数据集全流程解析
简介本资源面向计算机视觉方向的毕业设计、课程设计及学科竞赛参与者提供一套基于CNN与YOLOv5的车牌检测与识别完整工程数据集采用CCPD官方数据集可帮助读者快速搭建车牌识别实验环境并完成项目复现。压缩包共10个文件约44.33MB包含Python脚本、模型权重文件、YAML配置、Keras模型文件、Jupyter Notebook及README说明文档覆盖训练、检测、识别等核心环节便于按模块查阅与二次开发。目前已有78人学习下载适合具备一定深度学习基础、需要完整项目参考或扩展功能的学习者。资源内代码经过测试运行功能正常可复现复刻设计报告亦可借鉴并支持在此基础上修改以实现其他功能。作者具备全栈开发经验使用中遇到问题可随时联系获取解答与相关开发工具、学习资料鼓励学习进步。1. 车牌检测识别为什么总在“最后一公里”翻车停车场闸机抬杆慢半拍、高速卡口抓拍糊成一片、地库暗光下把“京A”认成“京B”——这些场景做过的都懂。车牌检测和识别看着是经典任务真落地时却总在最后一公里翻车检测框抖、字符粘连、倾斜角度一大就崩。标题里这套方案用 CNN 做字符识别、YOLOv5 做车牌定位数据集直接上 CCPD 官方数据集本质是把“检测”和“识别”拆成两段可控的流水线。它适合谁做毕设课设的学生、要快速搭车牌 demo 的工程师、以及想拿一个完整视觉 pipeline 练手的人。CCPD 是国内车牌场景里标注质量最扎实的公开数据集之一配合 YOLOv5 的工程成熟度能让你把精力放在调参和踩坑上而不是从零标数据。这一章先把“为什么这么搭”讲清楚后面直接上代码和参数。2. 拆解流水线YOLOv5 定位车牌CNN 识别字符2.1 为什么不是端到端一个模型搞定很多人第一反应是搞一个端到端模型输入整图直接输出车牌字符串。想法很美好实操很骨感。车牌在整图里占比极小端到端模型要同时学“在哪”和“是什么”训练时梯度会被背景噪声稀释收敛慢且容易过拟合。更现实的问题是CCPD 的标注是四点坐标加车牌号端到端需要你把字符级位置也标出来工作量翻倍。拆成两段就舒服多了。第一段 YOLOv5 只干一件事把车牌从整图里框出来输出裁剪后的车牌小图。第二段 CNN 只干一件事对规整后的小图做字符分类。两段各自的数据分布更集中YOLOv5 学“车牌长什么样”CNN 学“字符长什么样”互不干扰。常见做法是检测用 YOLOv5s 或 YOLOv5m识别用一个小型 CNN比如 4 层卷积加两层全连接整体推理在 GPU 上能压到 20ms 以内。提示如果你的场景车牌种类单一比如只有蓝牌检测模型可以更小如果蓝牌、绿牌、黄牌混在一起YOLOv5m 的容量更稳。2.2 CCPD 数据集的结构与转换脚本CCPD 官方数据集的文件名本身就是标注这是它最方便也最容易踩坑的地方。每张图文件名形如025-95_113-226469_448489-440489_232469_226469_440469-0-0.jpg用-分段后第 3 段是车牌四个角点坐标第 5 段是车牌号第 7 段是亮度第 8 段是模糊度。你得先写脚本把它转成 YOLO 需要的 txt 格式。import os import cv2 import numpy as np def ccpd_to_yolo(img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(img_dir): if not fname.endswith(.jpg): continue parts fname.split(-) # 第3段是四个角点格式 x1y1_x2y2_x3y3_x4y4 points parts[2].split(_) coords [] for p in points: x, y p.split() coords.append((int(x), int(y))) xs [c[0] for c in coords] ys [c[1] for c in coords] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) img_path os.path.join(img_dir, fname) img cv2.imread(img_path) h, w img.shape[:2] # YOLO 格式class cx cy bw bh全部归一化 cx (x_min x_max) / 2.0 / w cy (y_min y_max) / 2.0 / h bw (x_max - x_min) / w bh (y_max - y_min) / h label_path os.path.join(out_dir, fname.replace(.jpg, .txt)) with open(label_path, w) as f: f.write(f0 {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}\n) if __name__ __main__: ccpd_to_yolo(./ccpd/images, ./ccpd/labels)这段脚本的逻辑很直白从文件名解析四点坐标取外接矩形再按 YOLO 要求归一化。参数上注意class固定为 0因为只有“车牌”一类。cx cy bw bh全部除以图像宽高这是 YOLOv5 的硬性要求忘了归一化训练时 loss 会直接 NaN。跑完检查一下 labels 目录txt 数量和图片数量应该一致少一张都说明有文件解析失败。2.3 YOLOv5 训练配置与三个必调参数数据转好后YOLOv5 的训练入口是train.py。我一般会先建一个data/ccpd.yaml内容如下path: ./ccpd train: images/train val: images/val nc: 1 names: [plate]然后跑训练命令python train.py --data data/ccpd.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --name ccpd_exp三个必调参数--img决定输入分辨率CCPD 里车牌普遍偏小640 是底线显存够就上 1280--batch看显存16 是 8G 卡的稳妥值--epochs别迷信 300CCPD 数据量大100 轮后 mAP 基本就平了再训就是浪费电。训练完看runs/train/ccpd_exp/weights/best.pt这个权重后面识别阶段还要用。3. 字符识别 CNN 的搭建与训练细节3.1 车牌字符分类的 CNN 结构设计检测框出来之后把车牌小图裁出来做灰度化和尺寸归一化常见做法是 94x24 或 120x30然后送进 CNN。车牌字符集是固定的省份简称、字母、数字一共 65 类左右不含特殊车牌。所以这本质上是一个 65 分类问题不需要太深的网络。import torch import torch.nn as nn class PlateCNN(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.MaxPool2d(2), nn.Conv2d(128, 128, 3, padding1), nn.ReLU() ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d((1, 6)), # 保留水平方向适配字符序列 nn.Flatten(), nn.Linear(128 * 6, 256), nn.ReLU(), nn.Dropout(0.3), nn.Linear(256, num_classes) ) def forward(self, x): x self.features(x) return self.classifier(x)这里的关键设计是AdaptiveAvgPool2d((1, 6))。车牌是 7 个字符水平排列直接全局池化会丢掉位置信息保留 6 到 7 个水平槽位能让网络学到“第几个字符是什么”。Dropout(0.3)是防过拟合的后悔药CCPD 里某些省份样本少不加 dropout 容易在稀有省份上翻车。3.2 从检测框到字符标签的样本生成训练 CNN 需要“车牌小图 对应字符串”的样本对。CCPD 文件名里第 5 段就是车牌号直接拿来当标签。但要注意文件名里的车牌号是完整字符串而 CNN 是逐字符分类所以你得把字符串拆成字符列表再映射到索引。CHARS 京沪津渝冀晋蒙辽吉黑苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏陕甘青宁新 LETTERS ABCDEFGHIJKLMNOPQRSTUVWXYZ DIGITS 0123456789 CHARSET CHARS LETTERS DIGITS char_to_idx {c: i for i, c in enumerate(CHARSET)} def parse_plate_number(fname): parts fname.split(-) plate parts[4] # 第5段是车牌号 return [char_to_idx[c] for c in plate if c in char_to_idx]参数说明CHARSET的顺序一旦定下就不能改训练和推理必须用同一套映射否则识别结果会整体错位。parse_plate_number里过滤了不在字符集里的字符防止文件名里有异常符号导致 KeyError。生成样本时用 YOLOv5 的检测框裁图再 resize 到 94x24存成 npy 或直接在线裁都行。我一般离线裁好训练时直接读省得每个 epoch 都跑一遍检测。3.3 训练循环与验证指标CNN 的训练循环没什么玄学交叉熵损失加 Adam 就够。但验证指标别只看 accuracy车牌识别要看“整牌准确率”——7 个字符全对才算对。def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss 0 for imgs, labels in loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() return total_loss / len(loader) def eval_plate_acc(model, loader, device): model.eval() correct, total 0, 0 with torch.no_grad(): for imgs, labels in loader: imgs, labels imgs.to(device), labels.to(device) preds model(imgs).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) return correct / total注意eval_plate_acc这里算的是字符级准确率整牌准确率需要你把同一张图的 7 个字符预测拼起来再比对。实操中字符级能到 99% 时整牌往往只有 90% 出头差距就在那些模糊和倾斜样本上。4. 检测与识别联调从整图到车牌字符串4.1 推理管道的组装顺序联调时顺序很重要先检测再裁图再识别。别反过来。检测用best.pt识别用训练好的 CNN 权重。整条管道写成一个函数输入是原始 BGR 图像输出是车牌字符串列表。def detect_and_recognize(img, yolo_model, cnn_model, device): results yolo_model(img, size640) detections results.pandas().xyxy[0] plates [] for _, row in detections.iterrows(): if row[confidence] 0.5: continue x1, y1, x2, y2 int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) crop img[y1:y2, x1:x2] gray cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY) gray cv2.resize(gray, (94, 24)) tensor torch.from_numpy(gray).float().unsqueeze(0).unsqueeze(0) / 255.0 tensor tensor.to(device) with torch.no_grad(): preds cnn_model(tensor).argmax(dim1) plate_str .join([CHARSET[i] for i in preds.cpu().numpy()]) plates.append(plate_str) return plates参数上confidence阈值 0.5 是起点实际场景如果漏检多就降到 0.3误检多就升到 0.6。resize的目标尺寸必须和训练 CNN 时一致不一致的话识别率会断崖式下跌这是血泪经验。4.2 检测框微调对识别率的影响YOLOv5 输出的框有时候会偏紧或偏松。偏紧会切掉字符边缘偏松会带进背景噪声。我一般会在裁图时把框向外扩 5% 到 10%给字符留点余量。def expand_box(x1, y1, x2, y2, ratio0.08): w, h x2 - x1, y2 - y1 dx, dy int(w * ratio), int(h * ratio) return max(0, x1 - dx), max(0, y1 - dy), x2 dx, y2 dy这个ratio别设太大0.15 以上容易把相邻车牌框进来。实测 0.08 到 0.12 之间比较稳。另外如果车牌倾斜严重裁出来的图会有黑边可以在 resize 前做一次透视校正但那是另一个话题了。4.3 批量推理与结果落盘实际跑测试集时别一张一张喂用 DataLoader 批量推理能快好几倍。结果落盘建议存成 json每张图对应一个车牌列表方便后面算指标。import json def batch_inference(img_list, yolo_model, cnn_model, device, out_json): all_results {} for img_path in img_list: img cv2.imread(img_path) plates detect_and_recognize(img, yolo_model, cnn_model, device) all_results[img_path] plates with open(out_json, w, encodingutf-8) as f: json.dump(all_results, f, ensure_asciiFalse, indent2)ensure_asciiFalse是为了让中文省份简称正常显示不加的话会变成 unicode 转义后面读起来费劲。5. 避坑与排查车牌检测识别最常见的五个翻车点5.1 现象训练 loss 正常但 mAP 一直是 0原因CCPD 转换后的 label 文件里坐标没归一化或者归一化时除了错误的宽高。YOLOv5 对坐标范围极其敏感超出 [0,1] 直接不参与计算。解决写个检查脚本遍历所有 txt确认四个值都在 0 到 1 之间。发现异常就回查转换脚本里w和h是不是取反了。5.2 现象识别结果整体偏移一位比如“京A12345”认成“A12345京”原因CNN 训练时字符顺序和推理时拼接顺序不一致或者AdaptiveAvgPool2d的水平槽位和实际字符数对不上。解决固定CHARSET顺序训练和推理共用同一个映射文件。池化槽位设成 7 而不是 6给每个字符一个独立槽位。5.3 现象地库暗光图片检测框抖动严重原因YOLOv5 在低照度下特征响应弱框的回归不稳定。CCPD 里虽然有亮度标注但训练时没做针对性增强。解决训练时加--augment并在数据加载里对暗光样本做 gamma 校正。推理时如果场景固定可以先把图提亮再送检测。5.4 现象绿牌新能源车牌识别率明显低于蓝牌原因CCPD 里蓝牌占比远高于绿牌类别不平衡导致 CNN 对绿牌字符欠拟合。解决对绿牌样本做过采样或者在 loss 里给绿牌字符更高权重。简单做法是复制绿牌样本到训练集里让比例接近 1:1。5.5 现象推理速度慢单张图超过 200ms原因YOLOv5 用了大模型比如 yolov5x或者 CNN 没跑在 GPU 上。解决检测换 yolov5sCNN 确认.to(device)了。另外把torch.no_grad()加上不然会白算梯度。6. 进阶技巧用检测置信度过滤 识别置信度重排前面讲的管道是“检测框直接裁图送识别”但实际场景里会有误检框比如把广告牌上的文字框成车牌。一个实用的进阶技巧是CNN 输出 softmax 概率后取 7 个字符概率的乘积作为整牌置信度低于阈值的直接丢弃。def plate_confidence(logits): probs torch.softmax(logits, dim1) top_probs, _ probs.max(dim1) return top_probs.prod().item()这个乘积会偏小因为 7 个概率相乘每个 0.9 的话乘积只有 0.48。所以阈值别设太高0.3 左右比较合适。另一个技巧是如果同一张图检测出多个框且重叠度高用 NMS 去重后再识别避免同一车牌出多个结果。验证方法上我习惯把测试集分成“正常光照”“暗光”“倾斜”三组分别算整牌准确率。正常组能到 95% 以上暗光组往往掉到 80% 左右倾斜组看角度30 度以内还行超过 45 度基本就得加校正模块了。这套方案值不值得做如果你要的是一个能跑通、能改、能写进毕设的完整 pipeline它足够扎实如果你追求工业级 99.9% 的识别率那还得在数据增强和校正上继续磨。我自己的习惯是先把 baseline 跑通再针对最差的那组数据做定向优化别一上来就堆模型。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。