简介:本资源是一套完整的中文车牌识别高分项目实现,基于YOLOv5实现车牌定位、CRNN模型完成字符识别,面向人工智能、自动化、电子信息等专业学生及初学者,适用于毕业设计、课程设计、项目演示与算法进阶学习。压缩包共92个文件,含53个Python核心脚本(涵盖检测、识别、训练、测试、GUI界面及模型转换)、15个YAML配置文件(支持多种YOLOv5模型结构与超参微调)、9张效果示意图与7张测试样图,以及2个预训练权重.pth/.pt文件,整体体积仅7.65MB,结构清晰、模块解耦,便于理解与二次开发。已有112人下载学习,项目经导师指导并获95分答辩评审,所有代码均通过实测验证,配套详细文档覆盖环境配置、数据准备、训练流程、推理部署及常见问题说明,还包含plateNet/LPRNet/colorNet等关键网络实现、onnx/openvino导出脚本及torch2trt加速支持,显著降低复现门槛。 直接把 YOLOv5 和 CRNN 拼在一起做中文车牌识别,这个组合在国内毕设、课设和简历项目里出现频率非常高。原因很简单:它足够经典,分工明确——YOLOv5 负责把车牌从复杂场景里“抠”出来,CRNN 负责把抠出来的车牌图转成字符串,两件事都各自有极其成熟的方案可以借鉴。我最初拿到这套源码的时候,第一感觉是资料真全,模型、训练日志、标注工具、说明文档都齐了,但真正动手跑起来、再自己复现一遍之后,才发现里面的细节远比想象中多。
这篇文章我会把整套方案的思路、数据准备、训练调参、串接部署以及我踩过的坑全部拆开讲清楚。适合正在做毕业设计的学生,也适合想快速落地一个车牌识别 demo 的工程师参考。我不会把代码贴得密密麻麻,而是把每个环节的“为什么这么做”和“实际怎么调”讲透,你拿着思路就能在自己的项目里复现。
1. 整体方案拆解:为什么是 YOLOv5 + CRNN,而不是端到端一步到位
1.1 两阶段架构的核心思路
车牌识别听起来是一个任务,实际上可以拆成两个子任务:第一步是定位,第二步是识别。这套系统采用的就是典型的两阶段架构。检测端用 YOLOv5,输入一张完整图像,输出若干个带置信度的车牌边界框;识别端用 CRNN,把每个裁剪出来的车牌小图变成一串字符,比如“京A12345”。
为什么要把两个任务拆开?因为检测和识别的输入输出差异很大。检测需要的是全局上下文信息,它得理解“车牌在画面里属于一个相对小的目标,周围可能有车头、车身、路面背景”,所以网络要能看全图,而且对尺度变化要敏感。而识别任务面对的是已经裁剪好的近似矩形的车牌照,它的核心是“从左到右逐字符读取”,更关注局部纹理和序列顺序。把这两个任务强行塞进同一个端到端模型里,反而会互相干扰。
从训练角度看,拆开也更省心。YOLOv5 有海量预训练权重,在 COCO 上训好的模型可以直接拿来做迁移学习,车牌检测数据量要求其实不高,几千张就能训得不错。CRNN 也是一样,可以先用合成车牌数据预训练,再用真实数据微调。两个模型的训练数据可以分别迭代、分别优化,调试起来心智负担小很多。这对学生项目或者中小团队来说是巨大的优势。
1.2 检测端的选型:为什么 YOLOv5 依然是稳妥选择
YOLOv5 虽然名字里带 v5,但在车牌检测这种相对单一的场景里,它依然是非常能打的选手。对比 YOLOv8、YOLOv9 这些更新版本,YOLOv5 的生态最成熟,教程多、踩坑记录多、ONNX 导出方便,对于课程设计级别的项目来说,“求稳”比“追新”更重要。
具体到车牌场景,YOLOv5s 这个轻量版本就够用。车牌目标在监控画面里通常占几十到几百像素,不算极小目标,用 s 版的感受野和特征提取能力完全覆盖。如果你用的是 640x640 输入,一张普通图片里车牌再怎么小也不会小于 20x20,s 版的 P3 层能够处理。
我实际测试下来的感受是:在 CCPD 数据集上用 YOLOv5s 训练,mAP@0.5 轻松到 0.98 以上;换到包含复杂场景的自建数据集,也能保持在 0.95 左右。作为对比,如果一开始就用 YOLOv8,虽然性能略有提升,但很多配套工具(比如标签格式转换脚本、部署示例)都得自己重新踩一遍,投入产出比不明显。如果你的项目时间紧,直接 YOLOv5s 起步是性价比最高的选择。
1.3 识别端为什么选 CRNN:序列建模天然适配车牌结构
车牌字符识别的核心难点在于:字符宽度不一、位置不固定、存在轻微倾斜和模糊,而且整串字符是一个强顺序依赖的序列。CRNN 的设计哲学恰好就是“用 CNN 提特征,用 RNN 建模序列,用 CTC 对齐”,每一步都踩在车牌的痛点上。
具体展开说,CRNN 的第一段是卷积网络,把输入图像转成一系列特征图;第二段是双向 LSTM,对特征序列做上下文建模。为什么要加 LSTM?因为车牌里有“京A12345”这种字符组合,前一个字符对后一个字符有强烈的先验约束,比如省份汉字后面一定跟字母,字母后面是数字或字母,双向 LSTM 能学到这种隐式语法。第三段是 CTC 解码,它最大的好处是不需要逐字符标注位置,只需要整串标注,训练数据标注成本大幅降低。
和 LPRNet、PaddleOCR 里的车牌子模型相比,CRNN 胜在结构简单、解释性强、改造成本低。你可以在不改变整体框架的情况下,把主干网络从 VGG 换成 ResNet,把 LSTM 层数从 1 层加到 2 层,每一步改动效果都是可预期的。对想要在答辩时展示“我理解这个模型”的学生来说,CRNN 比用现成 OCR 工具更拿得出手。
1.4 中文车牌的特殊难点:不是简单的 OCR
很多人以为车牌识别就是把开源 OCR 拿过来用,真上手会发现中文车牌有一堆“专属坑”。首先是字符集,中国车牌包含省份汉字(31 个省市简称)、24 个字母(不含 I、O)、10 个数字,但在实际场景中还会出现“警”“学”“挂”“使”等特殊字符。
其次是字符排布结构。最常见的蓝牌是单排 7 位,但新能源绿牌是 8 位,且第 2 位是字母 D 或 F;黄牌大车是双排结构,教练车、领馆车还有额外的小字。这套系统如果只针对蓝牌做,单排 CRNN 就够了;要支持绿牌就得把序列长度调成 8,要支持黄牌双排还得加一个“是否双排”的分类分支。这些都是做真实项目绕不开的细节。
再就是成像质量问题。车牌在画面里往往是倾斜的、模糊的、反光的,甚至被灰尘遮挡。YOLOv5 检测框是水平矩形,如果车牌本身有透视形变,裁出来的图就是歪的,CRNN 直接识别这种歪图效果会大打折扣。所以很多工程落地时会在检测和识别之间加一个透视矫正步骤,我在后面实操部分会详细讲怎么处理。
2. 环境搭建与数据准备:这套系统的地基
2.1 环境版本清单与依赖安装
先给出一份我验证过能稳定跑通的版本组合。GPU 环境建议 CUDA 11.3 以上,显存 6G 就够训练 YOLOv5s 和轻量 CRNN,如果显存只有 4G,batch size 就要调小一点。
Python 建议用 3.8 或 3.9,PyTorch 用 1.10 到 1.13 之间的版本。YOLOv5 官方仓库对这几个版本的兼容性最好,太新的 PyTorch 反而偶尔会出现算子兼容问题。
安装依赖的步骤很简单,YOLOv5 部分直接进仓库目录执行:
cd yolov5/ pip install -r requirements.txt这里有个小坑:requirements.txt 里会装一批库,如果你机器上已经装过其他深度学习框架,建议先建一个独立的 conda 环境,避免依赖冲突。CRNN 部分相对轻量,只需要 torch、torchvision、opencv-python、numpy、lmdb(如果用了 LMDB 格式的数据集)。
我个人的习惯是装完环境先跑一句python detect.py --source data/images/bus.jpg验证 YOLOv5 能否正常推理。很多问题在环境刚装好时暴露最明显,比如 CUDA 不可用、torchvision 版本不匹配,这时候排查成本最低。倒腾半天跑训练才发现环境有问题,那才是真浪费时间。
2.2 数据集从哪来:公开数据集 + 自采数据的组合策略
车牌识别不能只用公开数据集硬训,因为每个地区的车牌样式、场景光照、拍摄角度都有差异。最稳的组合是“公开数据集打底 + 少量场景数据微调”。
公开数据集方面,国内用得最多的是 CCPD(中国城市停车场数据集)和 CRPD(中国道路停车场数据集)。CCPD 有超过 20 万张图片,覆盖了不同天气、不同角度、不同距离的蓝牌场景,适合做检测模型的训练。CRPD 则更偏道路监控视角,包含更多复杂背景。这两个数据集的标注格式略有不同,CCPD 的车牌框坐标写在文件名里,需要写脚本解析;CRPD 则提供了 XML 或 TXT 标注,转换起来更方便。
YOLOv5 需要的标注格式是每个图片对应一个同名 txt 文件,每行是“class cx cy w h”,其中 cx、cy、w、h 都是相对于图片宽高的归一化值。转换脚本网上很多,但核心逻辑都是一致的:读取原标注 -> 解析出左上角坐标和右下角坐标 -> 算出中心点和宽高 -> 归一化 -> 写入 txt。
如果你的项目有特定场景需求,比如停车场出入口、高速卡口,建议自己也拍一些照片补充进数据集。不需要太多,几百张就能显著提升模型在你实际场景里的表现。自采数据需要注意的是标注一致性——框一定要紧贴车牌边缘,宁可多框一点也不能框一半,YOLO 对标注框质量的敏感度很高。
2.3 车牌区域裁剪:从检测到识别的关键中间步骤
训练好检测模型之后,接下来的流程是:对图片推理,得到车牌框,把框内的图像裁出来,送给识别模型。这一步看似简单,实际上有几个容易翻车的细节。
第一个细节是坐标换算。YOLOv5 输出的坐标是归一化的,需要乘回原图宽高,还要注意 YOLOv5 的坐标格式是 xywh(中心点 + 宽高),要先转成 xyxy(左上角 + 右下角)再裁剪。第二个细节是边界裁剪。如果检测框部分超出图像边界,cv2.getRectSubPix或image[y1:y2, x1:x2]可能会报错或得到空白图,必须加边界 clamp 处理。第三个细节是扩展边距。
直接裁剪检测框有时候会把车牌边缘的螺丝、边框截掉一部分,影响识别精度。我通常会在原框基础上向外扩 10% 到 15% 的宽度,再裁剪。比如检测框是 (x1, y1, x2, y2),扩展后的宽度就是w = x2 - x1,新的 x1 是max(0, x1 - 0.1 * w),依此类推。这个微小的改动在绿牌和双层黄牌识别上效果很显著。
2.4 数据增强:让模型在恶劣环境下也能干活
车牌识别最怕的场景是模糊、暗光、倾斜、逆光。数据增强就是用来模拟这些情况的。YOLOv5 自带的增强已经很完善,包括 Mosaic、HSV 色域增强、随机翻转、随机缩放,训练时默认开启,不需要额外处理。但有几个点需要手动调。
车牌检测场景里,上下翻转增强没有意义——车牌不会倒着出现,建议在数据配置文件的flipud参数里设置为 0,只保留左右翻转。另外,车牌的宽高比非常固定(普通蓝牌约 3:1),Mosaic 增强会把多张图拼在一起,产生一些极端宽高比的假车牌,反而可能干扰训练。如果你发现模型对正常车牌的检测反而不准了,可以先关掉 Mosaic 试试。
CRNN 这边的增强则集中在仿射变换、透视变换、高斯模糊、亮度对比度调整上。我常用的做法是用imgaug库写一个增强流程,每张车牌图随机应用 1 到 3 种增强,模拟真实场景中的成像退化。这里要特别注意:车牌字符的形变不能太夸张,否则人眼都难认,模型更学不会。
3. 检测模块实操:用 YOLOv5 训练高精度车牌检测器
3.1 数据集划分与标签格式转换
拿到原始数据后,第一步是划分训练集、验证集和测试集。推荐的比例是 8:1:1,且要保证三个集合中车牌的省份、场景分布大致相同。如果训练集里全是安徽车牌、测试集里全是北京车牌,那验证曲线会非常难看,模型泛化能力也会被高估。
标签格式转换我建议写一个独立脚本,而不是在训练脚本里边读边转。脚本的核心逻辑是遍历所有标注文件,统一转成 YOLO 格式的 txt。CCPD 的解析比较特殊,它的文件名里直接包含了车牌框信息,例如“025-95_113-154&383_386&473-386&473_177&454_154&383_363&402-0_0_22_27_27_33_16-37-15.jpg”,需要从下划线分隔的字段里提取坐标。
如果你是用标注工具(如 LabelImg、labelme)标注的自采数据,导出时选 YOLO 格式即可,省去转换步骤。唯一要注意的是标签文件里 class id 必须从 0 开始连续编号,我们这里只有一个类别“plate”,所以永远是 0。
3.2 修改配置:数据 YAML 和模型 YAML
YOLOv5 训练需要两个配置文件,一个是数据配置,一个是模型配置。数据配置写的是数据集路径和类别数:
train: /data/carplate/train/images val: /data/carplate/val/images nc: 1 names: ['plate']路径建议写绝对路径,YOLOv5 对相对路径的处理偶有坑。模型配置可以直接用官方自带的yolov5s.yaml,只需要把nc改成 1。如果你用的是 COCO 预训练权重,模型的最后一层卷积输出通道数会自动适配新的类别数,不需要手动改。
训练命令大致如下:
python train.py --img 640 --batch 16 --epochs 100 --data carplate.yaml --weights yolov5s.pt --device 0--img一般设 640,如果你的车牌特别小,可以试着调到 960,能提升小目标检测能力,但训练速度会慢不少。--batch根据显存调节,6G 显存建议 16,如果爆显存就降到 8。--epochs我建议先跑 100 轮看趋势,如果 val mAP 还在涨就继续加。
3.3 训练过程分析与 checkpoint 选择
训练过程中要盯三个指标:train/loss、val/obj_loss、metrics/mAP_0.5。正常情况下,loss 会前 20 轮快速下降,后面趋于平缓;mAP_0.5 会逐步逼近 0.95 以上。如果你发现 loss 下降很慢或者直接不降,大概率是学习率设置问题,YOLOv5 默认的--lr0 0.01对迁移学习场景基本够用,不需要特殊调整。
训练完成后,项目文件夹下会生成runs/train/expX/weights/目录,里面有last.pt和best.pt。很多人直接用last.pt,这是不对的。last.pt只是最后一轮的权重,如果训练后期出现轻微过拟合,它的效果反而不如中间某轮的best.pt。best.pt是验证集指标最优的权重,业务上线或者测试都应该用它。
我习惯在训练结束后再用测试集做一次完整评测,统计 mAP、每类精确率和召回率。在车牌检测这种单类别场景里,如果召回率低于 0.95,说明漏检严重,优先检查数据增强是否过强;如果精确率低,说明误检多,优先检查背景样本是否太少——可以专门跑一批没有车牌的图片做负样本。
3.4 推理与检测结果后处理
YOLOv5 推理输出的预测框置信度阈值默认是 0.25,NMS 的 IoU 阈值默认是 0.45。实际车牌场景里,置信度阈值可以提高到 0.45,因为我们只关心高置信度的车牌框,宁可漏一两个低置信度的,也不要误检一堆车身贴纸。NMS 的 IoU 阈值也可以适当调低到 0.3,减少重叠框。
推理时要留意的是批次大小。如果要对视频流做实时处理,单帧推理时间必须压到 30ms 以下,这要求输入图片先缩放到 640x640 并做归一化。YOLOv5 的detect.py已经封装好了这些操作,但如果是自己写推理脚本,一定要记得做同样的预处理,否则模型输出会非常离谱。
4. 识别模块实操:训练一个可靠的中文车牌 CRNN
4.1 字符集设计与标签编码
CRNN 的输出是字符类别概率序列,所以训练前必须先定义字符集。我用的字符集包含三部分:省份汉字(京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼)、24 个大写字母(去掉 I 和 O)、10 个数字,再加上一个 CTC 空白符。
字符集要写成一个列表文件,每个字符对应一个索引。注意索引 0 通常留给 CTC 的 blank,字符从 1 开始编号。这样设计的好处是 CTC 解码时,blank 索引是固定的 0,不用额外处理。
标签编码就是把车牌字符串映射成索引序列。例如“京A12345”,如果字符集里“京”是索引 5、“A”是 3,那么这个标签序列就是 [5, 3, 10, 2, 4, 8, 9](数字对应的索引是它们在字符集中的位置)。训练时目标序列不需要补齐到固定长度,CTC 天然支持变长序列。
4.2 网络结构与输入尺寸
我这里用了一个精简的 CRNN 结构:主干是类似 VGG 的卷积层,提取特征后压缩高度,再把特征图按宽度方向展开成序列,输入双向 LSTM,最后接全连接层输出每个时间步的字符概率。
输入尺寸很关键。CRNN 要求输入图像高度固定,宽度可变,但为了 batch 训练方便,通常会把所有图片 resize 成固定尺寸。我用的组合是height=32, width=128,因为普通蓝牌宽高比约 3:1,128 宽的序列足够容纳 7 个字符。新能源绿牌是 8 位,128 宽也够。如果是双排黄牌,建议单独训练一个高度 64 的模型,或者加一个预处理把双排拆成单排。
图像送入网络前要转成灰度图还是保留 RGB?我的实验表明,灰度图识别准确率略低,因为颜色信息在区分某些易混字符时有帮助。所以建议保留 RGB 三通道。
4.3 CTC Loss:为什么它能解决“对齐”问题
CRNN 训练最核心的就是 CTC Loss。传统的分类 loss 要求每个输入位置都对应一个标签,但车牌字符宽度不一,很难做逐像素对齐。CTC 的思路是:允许网络每个时间步输出一个字符或者空白,然后通过动态规划,在所有可能路径中找出概率最大的那条作为预测。
打个比方,CTC 就像是在说:“我不关心你每个时间步具体预测什么字符,只要整个序列经过压缩处理后能拼出正确答案就行。”它允许连续重复字符和空白出现,最后用去重操作把冗余去掉。这种机制让 CRNN 不需要字符级标注,只需要整串标注就能训练。
训练时 loss 直接取负对数似然。PyTorch 里用torch.nn.CTCLoss,需要传入三个参数:网络的 logits 序列(形状是[T, N, C],T 是时间步数,N 是 batch size,C 是字符集大小)、标签序列(形状是[N, S],S 是标签长度,用 -1 填充对齐)、以及每个样本的标签长度。这里最容易出错的是 logits 的维度顺序,一定记得先permute成[T, N, C]。
4.4 训练策略与识别后处理
CRNN 训练我建议先从合成数据开始。网上有开源的车牌合成器,可以生成几万张逼真的车牌图片,覆盖各种字体、背景、光照。用合成数据预训练 20 轮之后,再用真实车牌数据微调。合成数据的好处是标签完全精准,不会出现人眼识别错误导致标签噪声的问题。
真实数据微调时,学习率要降下来,我用的初始学习率是 1e-4,比预训练阶段小 10 倍。训练 50 轮左右,验证集字符准确率能到 98% 以上。字符准确率的计算方法是:对每张测试图片,把模型输出的字符序列和真实标签逐位对比,统计正确字符的比例。
推理时要做两件后处理。第一件是 CTC 解码,把概率序列转成字符串,规则是取每个时间步概率最大的索引,然后去除重复字符和 blank。第二件是置信度过滤,如果某张车牌图的平均置信度低于 0.8,宁可输出“unknown”也不要硬猜,避免后续流程拿到错误结果。
5. 系统集成与完整 Pipeline:从图片到车牌字符串
5.1 检测 + 识别的串接流程
把 YOLOv5 和 CRNN 串起来,一个最简的推理流程是这样的:
import cv2 import torch import numpy as np # 加载模型 detect_model = torch.hub.load('yolov5', 'custom', path='best.pt', source='local') recognize_model = CRNN(num_classes=len(CHARS)).to(device) recognize_model.load_state_dict(torch.load('crnn.pth', map_location=device)) recognize_model.eval() # 检测 img = cv2.imread('test.jpg') results = detect_model(img) boxes = results.xyxy[0].cpu().numpy() # 裁剪 + 识别 for box in boxes: x1, y1, x2, y2, conf, cls = box if conf < 0.45: continue plate_img = img[int(y1):int(y2), int(x1):int(x2)] plate_img = cv2.resize(plate_img, (128, 32)) # 归一化、转 tensor、送入 CRNN # 输出字符串这段代码虽然只有不到 20 行,但工程化时要考虑很多细节。首先是模型加载方式,torch.hub.load适合快速验证,正式部署建议直接用torch.jit.trace导出成 TorchScript,或者导出 ONNX。其次是数据预处理的一致性,CRNN 训练时如果做了归一化(比如除以 255、减均值除方差),推理时也要做完全一样的操作。
5.2 透视矫正:提升识别准确率的关键技巧
前面提到,直接裁剪检测框会遇到倾斜问题。YOLOv5 输出的是水平矩形框,而真实车牌可能是透视形变的,比如从侧面拍摄时,车牌会变成一个梯形。CRNN 对这种形变非常敏感,稍微歪一点,字符就错。
解决办法是在裁剪后做透视矫正。如果你有车牌的四个角点,可以直接用cv2.getPerspectiveTransform做矫正。但 YOLOv5 只输出水平框,没有角点信息。一个折中的方案是:假设车牌的上下边框在检测框内基本平行,用边缘检测 + 直线拟合来找四边形的四个顶点,再做矫正。
更简单粗暴的方法是:对裁剪出来的车牌图做“水平拉直”。很多情况下,车牌虽然倾斜,但整体形变不大,直接按检测框裁剪后,再做cv2.warpAffine旋转,让车牌长边尽量水平,就能提升不少识别准确率。我实测下来,在卡口场景里,这个预处理能让整体识别准确率提升 3% 到 5%。
5.3 性能优化与实时性考量
如果需要对视频流做实时识别,性能优化是绕不开的。YOLOv5s 在 GPU 上单帧推理约 10ms,CRNN 约 5ms,加起来 15ms,理论上能跑到 60 FPS。但实际中瓶颈往往在预处理和后处理上——图像缩放、颜色转换、裁剪、归一化这些步骤如果用 Python 裸写循环,会吃掉大量时间。
优化思路有几条。第一,尽量 batch 推理,视频流中连续帧可以攒一批再统一推理;第二,把图像 resize 和归一化放到 GPU 上做,用torch.nn.functional.interpolate代替cv2.resize;第三,如果车牌数量多,可以先用一个轻量的筛选器把没有车牌的帧直接跳过,减少 CRNN 的调用次数。
5.4 整体系统的模块划分与代码组织
拿到这套源码时,我发现它的目录结构非常典型:detect/放 YOLOv5 相关代码,recognize/放 CRNN 相关代码,tools/放数据转换脚本,inference/放完整推理脚本。这种模块化组织方式非常值得借鉴。
我自己在重构的时候,把流程分成了四个模块:数据层负责读取和标注转换,模型层负责加载和推理,服务层负责封装 API 接口,应用层负责对接摄像头、图片文件等输入源。如果只是做毕设,不需要上 Flask 这类服务框架,但保持模块划分能让答辩时讲起来更有条理。
6. 常见问题与排查技巧实录
6.1 训练不收敛或 loss 为 NaN
这是 YOLOv5 和 CRNN 训练中最常见的问题。YOLOv5 方面,loss 为 NaN 大概率是学习率过大或数据集中存在空标注文件。检查方法很简单:在训练命令里加--label-smoothing 0.1试试,如果还不行,就检查数据集的 txt 标注文件是否有全零坐标或空文件。
CRNN 方面,loss 为 NaN 通常是输入图像出现了 NaN 像素值,或者标签序列里有超出字符集范围的索引。建议在 DataLoader 里加一个断言,检查每个 batch 的输入是否有限值。另外,CTCLoss 对log_probs的数值稳定性要求较高,如果网络输出里有极端值,可以在 forward 最后加一层log_softmax并用torch.clamp限幅。
6.2 检测框抖动和漏检问题
检测框抖动在视频流里最常见,表现是同一辆车前后几帧的检测框位置跳来跳去。这通常是单帧检测的固有噪声,解决办法是加一个简单的跟踪平滑,比如对最近 5 帧的检测框坐标取平均,或者用 IoU 匹配做逐帧关联。
漏检问题要分情况讨论。如果是远处的小目标漏检,考虑把输入分辨率从 640 提高到 960;如果是强光、逆光场景漏检,建议在数据增强里加强亮度扰动,并在训练数据里加入更多逆光样本。还有一种容易忽略的情况:如果车牌被遮挡了一半,YOLOv5 的检测框会非常不稳定,此时可以适当降低置信度阈值,但要做好误检增多的心理准备。
6.3 识别错字的规律与对策
CRNN 识别错字不是随机的,它有很强的规律。最常见的是 O 和 0、D 和 Q、B 和 8 这类形状相近的字符混淆。要解决这个问题,可以在训练数据里专门加入一些字符级样本,或者在字符集设计时做相似字符混淆增强,随机把标签里的 O 替换成 0,让模型学会“看上下文”。
省份汉字错误也很典型,比如“皖”错成“晚”、“鲁”错成“曾”。这类错误说明模型对汉字细节的敏感度不够,可以考虑在训练时对车牌图做更强的高频细节增强,比如锐化。还有一个小技巧:如果业务场景限定了某个地区,可以在后处理时加一个省份白名单,把不在白名单里的汉字直接按照相似度映射到最近允许的汉字。
6.4 zip 资源包解压报错的处理
这套资料打包成 zip 分发,很多人在解压时遇到“file is not a zip file”或者“invalid zip archive: could not find EOCD”的报错。这个问题的根源通常是压缩包下载不完整,或者文件在传输过程中被截断。EOCD(End of Central Directory Record)是 zip 文件末尾的目录记录,找不到它基本可以断定文件损坏。
解决方法是重新下载,并检查下载文件大小是否和源文件一致。如果下载工具支持断点续传,建议开启。还有一个容易踩的坑:某些网盘客户端会把 zip 文件另存为 .download 后缀,手动改回 .zip 后解压工具会无法识别,这时候应该重新用客户端下载完成,而不是手动改后缀。
在 Linux 服务器上解压时,用unzip命令遇到中文文件名乱码也很常见,需要先设置环境变量:
export LANG=zh_CN.UTF-8 unzip project.zip如果 zip 包是加密的,unzip会提示输入密码。这类项目资源通常会在文档里说明密码,注意查看 readme.txt 或 download.txt。
6.5 环境配置中的“经典”报错与修复
把源码从 zip 里解压出来之后,运行环境配置阶段最容易出问题。先整理几个我遇到过的经典报错:
第一个是No module named 'torchvision'。大概率是创建了多个虚拟环境,pip 命令装到了别的环境里。检查方法是which python和python -c "import torchvision"同时执行,确保你用的解释器和 pip 的环境一致。
第二个是 CUDA 不可用,torch.cuda.is_available()返回 False。先用nvidia-smi看驱动支持的 CUDA 版本,再对比 torch 的 CUDA 版本。
第三个是显存不足 OutOfMemoryError。解决办法是把 batch size 调小,并把--workers调低,减少数据加载对显存的压力。如果还不行,试试在训练命令里加--cache不生效的话,就检查是不是有别的进程占用了显存。
6.6 高参考价值的调参速查表
最后整理一份调参速查表,方便你遇到问题时快速定位方向。
| 问题现象 | 优先检查项 | 推荐处理方式 |
|---|---|---|
| 训练 loss 不降 | 标注质量、学习率 | 检查标注文件,降低学习率至 1e-4 重训 |
| 检测 mAP 低 | 数据量、分辨率 | 增加数据,提高输入分辨率到 960 |
| 检测框乱跳 | 置信度阈值、NMS | 提高阈值到 0.45,降 NMS 到 0.3 |
| 识别错字多 | 字符集、矫正预处理 | 检查字符集,加透视矫正 |
| 推理速度慢 | 模型大小、预处理 | 换 YOLOv5n,优化 resize 逻辑 |
| zip 解压失败 | 文件完整性 | 重新下载,检查文件大小一致性 |
最后说两句
整套系统跑通之后回头看,最大体会是:YOLOv5 + CRNN 这个组合最值钱的地方不是某个模型多先进,而是每个环节都极其透明,出了问题能定位、能修、能改进。车牌检测不准就调检测数据,识别错字就调识别数据和网络结构,两边互不干扰,调试效率非常高。如果你也想在这个项目上继续深挖,一个值得尝试的方向是把检测和识别合并到一个 onnx 模型里做端到端加速,另一个方向是把双排黄牌支持加进去,这两块做完,这套系统的完整度会再上一个台阶。
本文还有配套的精品资源,点击获取