
简介本资源是一套基于YOLOv7的中文车牌检测与识别完整实现方案面向计算机、人工智能、自动化等专业学生及初学者解决多类型车牌含单/双层在复杂场景下的精准定位与字符识别问题。项目支持12类中文车牌包括新能源、警用、武警、使馆、港澳、民航等特殊车牌兼顾实用性与教学拓展性适用于课程设计、毕业设计、项目演示及算法入门实践。压缩包共97个文件含38个Python核心脚本如detect_rec_plate.py、plate_rec.py、13个YOLO配置yaml文件、5个Shell训练/部署脚本、4个C/CUDA加速模块用于TensorRT/NCNN推理、2个预训练模型.pt/.pth及配套文档与测试图像整体24MB结构清晰、模块解耦。已有192人学习下载提供完整README说明、可运行验证的毕设级代码答辩平均分96分并附带中文字体、预处理工具与多后端ONNX/TensorRT/NCNN适配支持便于二次开发与工程落地。 做车牌识别这个项目最早是因为小区停车场道闸老是识别不准遇到蓝牌还算凑合碰上绿牌新能源和双层黄牌大车就频繁翻车手动录车牌太痛苦。后来翻了一圈开源方案发现英文车牌的成熟项目很多但轮到中文车牌就各种水土不服——省份简称认不出来、双层车牌框错位置、倾斜角度一大就识别失败。于是自己动手用yolov7做车牌检测再挂一个轻量级识别网络把整车流程完整跑通包括双层车牌和中文省份字符的识别。这篇文章把完整思路、训练过程、推理部署和踩过的坑一次性整理出来给你一个可以直接拿来参考或者二次开发的落地样板。不管你是刚入门目标检测的学生还是工作中需要处理车牌识别需求的工程师这套方案都能给你一个相对完整的基线。检测用yolov7识别用类似LPRNet的轻量序列模型中间穿插颜色分析和透视校正源码里训练、推理、接口测试都有了。下面我从项目拆解开始一步步讲清楚为什么要这么设计以及每一阶段的关键细节。1. 项目整体设计与任务拆解1.1 车牌识别的完整链路检测和识别是两件事很多人刚接触车牌识别下意识觉得这是一个端到端的问题输入一张图输出一串字符。但实际工程里几乎都是拆成两段来做先检测车牌区域再做字符识别。这两段任务差异很大强行糅在一个模型里反而会导致训练难度上升、调参不透明、出错后不好定位。检测阶段解决的是“车牌在哪里”的问题。输入是整张图片可能是路口的全景、停车场的入闸画面车牌可能很小也可能有倾斜、遮挡、反光。这时候用yolov7这类目标检测模型输出车牌的外接矩形框配合置信度过滤就能稳定把车牌区域从复杂背景里抠出来。识别阶段解决的是“车牌内容是什么”的问题。检测框裁剪出来的小图经过尺寸归一化、颜色分析、倾斜校正之后送进识别网络输出一个字符串比如“京A12345”。这个阶段面对的基本是固定尺寸的窄长图和检测完全不同。这两步分开设计的好处很明显检测网络的训练数据可以是任意场景图识别网络只需要车牌特写图部署时如果发现某个环节拉胯也能单独优化不用整个模型重训。我在项目里就是检测和识别各自训练、各自调优最终串联成一条pipeline这也是工业界最主流做法。1.2 为什么选择yolov7而不是yolov5或yolov8选yolov7不是因为它最新而是因为它在项目落地的时间和算力约束下性价比最高。yolov7的ELAN结构Efficient Layer Aggregation Network在保持较高精度的同时推理速度很快尤其在1080Ti、3090这类常见显卡上表现很好。对比yolov5yolov7的mAP在同等训练条件下普遍高出一截对比后来的yolov8yolov7的部署生态和教程成熟度反而更好踩坑时能找到更多参考。我实际使用中发现yolov7在检测小目标车牌时有一个明显优势它的特征融合网络对不同尺度特征的利用效率更高而车牌在整张图片里经常只占很小一块属于典型的小目标。用yolov7默认的输入尺寸640时近距离抓拍能稳定检出远距离画面如果把输入分辨率提到960召回率还能明显上涨。当然如果你更熟悉yolov8或者yolov5迁移到这套方案也不难核心pipeline逻辑是通用的只是换检测权重和配置文件而已。1.3 12种中文车牌的界定从颜色和用途维度分类标题里说的“支持12种中文车牌”我按照国内路面常见的号牌类型做了梳理主要是从颜色和用途两个维度划分的这也是识别前颜色判断要解决的问题。项目支持的类型包括但不限于蓝牌小型汽车、黄牌大型汽车、黄牌挂车双层、绿牌新能源小型汽车、绿牌新能源大型汽车、白牌警用车辆、白牌武警车辆、黑牌涉外车辆、黑牌港澳入出车辆、农用车辆黄绿牌、教练车黄牌、临时纸制车牌等。这些车牌类型在字符排列上并不完全一样。比如绿牌新能源车牌是8位字符比传统蓝牌的7位多一位挂车车牌往往是上下两层的结构上层小字写着省份简称加“挂”字下层才是主号牌内容。如果不对这些类型做区分识别结果很容易串位。所以在识别流程里我会先用颜色分析把车牌归到大类再用宽高比判断是否双层最后才送字符识别模型。这样一来后处理逻辑能针对不同类型做不同的拼接校验识别错误率明显下降。2. 数据准备与数据集构建2.1 车牌数据从哪来公开数据集与自采数据的权衡训练车牌识别模型最头疼的就是数据。中文车牌涉及各省简称、字母、数字的组合加上不同光照、角度、遮挡数据量需求不小。我在这个项目里主要用了两类数据第一类是公开数据集比如CCPD中国城市车牌数据集里面是约30万张真实场景下的蓝牌和绿牌图片带检测框标注非常宝贵。但CCPD的省份分布不均衡大多数是安徽的“皖”字开头所以单靠它训练模型对某些省份简称的识别能力会明显偏弱。这时候就需要第二类数据来补齐。第二类是自采和合成数据。我用自己的相机在停车场、路边、朋友车库里拍了一些不同省份的实车照片同时用代码合成了一批车牌图片。合成时先按标准排版渲染字符再随机做透视变换、加高斯噪声、调亮度对比度、加模糊。合成数据虽然和真实数据有分布差异但用来补足汉字样本量特别有效尤其是那些公开数据里很少见的省份简称。这里有个原则要讲清楚车牌数据涉及公民隐私自采时只拍车牌本身或车身局部不要拍人脸和完整环境合成数据不存在隐私问题但要控制好仿真度否则模型泛化能力会受影响。2.2 标注规范检测框与识别标签怎么标检测阶段和识别阶段的数据标注要求完全不同。检测数据的标注格式是yolo格式每个车牌框用归一化的中心点坐标加宽高表示。我建议标注时稍微比车牌边缘外扩一点把车牌四周几像素的边框也包进去这样模型学到的特征不只是字符区域还包括车牌底色和边框对后续裁剪稳定性有好处。另外标注时不要框进无关的保险杠、车灯区域否则模型容易把那些区域也当成车牌特征。识别数据的标注相对简单每张裁剪出来的车牌小图对应一个字符串标签比如“京A12345”。但要注意标签里的字符必须和图片一一对应尤其是双层车牌要先切分成两行再分别标注避免把上下两行内容合并成一个长字符串那样模型很难学。我还额外给每张车牌图打了一个颜色标签用于训练一个简单的颜色分类器。这个分类器不是主模型但它在判断车牌类型蓝、绿、黄、白、黑时非常有用能帮后处理逻辑决定字符位数和拼接方式。2.3 数据增强让模型适应不同光线、角度和噪声车牌识别最大的敌人是环境多样性。同一个车牌白天和晚上拍出来的像素分布差异巨大更不用说下雨、逆光、灰尘附着这些场景。所以数据增强是训练里必不可少的一环。我常用的增强组合包括随机亮度对比度调整模拟早晚光照变化、随机高斯模糊模拟快速通过闸机时的运动模糊、随机透视变换模拟不同相机角度、随机HSV色偏模拟不同摄像头白平衡差异、以及随机添加椒盐噪声。增强强度不能太大否则模型会学到错误特征比如把噪点当成字符笔画。检测模型和识别模型我都用了增强但侧重点不同。检测模型更依赖随机裁剪、缩放和翻转帮助它适应不同尺度和位置的车牌识别模型则更依赖模糊、噪声和透视变化因为这些是裁剪后小图最常见的退化情况。增强策略调好之后模型在真实场景的鲁棒性提升非常明显有时比增加模型参数量还管用。3. 模型训练与核心实现3.1 环境搭建与依赖安装环境是第一个坑我建议直接用Python 3.8或3.9配PyTorch 1.10以上的版本。yolov7官方仓库对PyTorch版本不算挑剔但太新的版本偶尔会遇到CUDA算子兼容问题实测下来PyTorch 1.12是最稳的组合。基础依赖包括torch、torchvision、opencv-python、numpy、matplotlib、tensorboard、pyyaml、tqdm等。如果要用GPU训练务必装好对应CUDA版本的PyTorch别贪图方便直接装CPU版本否则训练速度慢到怀疑人生一个epoch跑几个小时都正常。我的建议是先用小批量数据跑通流程确认环境没问题再上全量数据训练。识别模型我参考了LPRNet的思路用PyTorch从零搭一个轻量CNN加RNN的结构输入尺寸固定为宽94、高24的灰度图。这个尺寸是参考车牌真实宽高比定的太长会导致字符被压缩太宽又会引入多余背景。3.2 车牌检测模型训练yolov7的配置与调参检测模型训练前先把数据组织成yolov7要求的目录结构images目录放训练图片labels目录放对应的txt标注文件再写一个plate.yaml填写类别数量这里只有1类就是license_plate、训练集和验证集路径。训练命令大概是这样python train.py --data plate.yaml --cfg yolov7.yaml --weights yolov7_training.pt --batch-size 16 --epochs 200 --img 640有几点值得展开说明。批大小在显存允许的条件下尽量设大但也要注意如果batch-size设到32或者更大学习率也要跟着调否则收敛不稳定。输入分辨率我建议训练时用640起步如果发现小目标漏检可以升到960但代价是训练和推理都会变慢在算力和精度之间要有个取舍。训练过程中我还会开启mosaic增强这是yolov5和yolov7里很有效的技巧把4张图拼成一张再训练能让模型在小目标检测上表现更稳。但mosaic也要适度如果图像里车牌占比本来就小拼图后车牌变得更小反而可能导致模型学不到有效特征。我一般会在最后50个epoch关闭mosaic让模型在正常分布下微调。初始权重我用的不是随机初始化而是yolov7在COCO上的预训练权重。因为车牌检测这个任务对底层特征边缘、纹理、颜色块的需求和COCO通用目标检测是有重叠的用预训练权重可以大幅度缩短收敛时间最终精度也普遍更好。3.3 车牌识别模型训练LPRNet思路与CTC损失识别模型的结构类似LPRNet输入是车牌小图输出是一串字符序列。因为车牌字符个数不固定蓝牌7位、绿牌8位所以不能简单地用固定分类输出而是用CTC损失来做序列对齐。CTC允许模型输出一个比目标更长的序列然后通过动态规划找到最优路径从而解决字符长度不固定的问题。我搭建的识别网络结构是几个卷积层提取图像特征然后接双向LSTM建模序列依赖最后用一个全连接层输出每个时间步的字符概率分布。字符集合包括31个省份汉字简称、24个英文字母去掉I和O避免和数字1、0混淆以及10个数字。训练识别模型时的数据组织方式是一个纯文本文件每一行是图片路径加标签标签里不含空格。训练时随机对输入图片做缩放、平移、亮度变化然后统一resize到94乘24。优化器用Adam初始学习率设为0.001配合余弦退火调度器。CTC loss用PyTorch内置的torch.nn.CTCLoss需要把输入转成形状时间步、批大小、类别数并处理好空字符索引的问题。一个容易忽略的细节是CTC的blank位要放在字符集合的第0位和预测输出对齐好否则解码时会得到乱码。解码时用贪心搜索直接取每个时间步最大概率的字符合并连续重复并去掉blank就能得到最终字符串。虽然也可以用beam search提升少量准确率但实时场景下贪心解码速度更快效果已经足够。3.4 双层车牌的判定与行切分逻辑双层车牌是中文车牌识别里最容易被忽略的坑。常见于大型货车、挂车车牌高度比普通蓝牌高不少字符分成上下两行。如果直接把它当作普通单层车牌输入识别模型模型看到的是一张被压缩得厉害的长图字符之间严重粘连识别结果基本是不可用的。我的处理思路是分三步。第一步检测框出来后计算宽高比普通蓝牌宽高比接近3:1双层车牌宽高比大概在1.5:1到2:1之间用这个比值先初步判断是否疑似双层。第二步对检测框内的图像做灰度化和二值化然后做水平方向投影统计找到上下两行的分割线。第三步按分割线把图切成上下两行分别送识别模型再把两行结果按顺序拼起来。这里有个细节要特别注意挂车车牌的下行是主体上行一般是省份简称加“挂”字比如“皖A·1234挂”这样的结构。拼接时不能简单把两行结果顺序相连需要根据车牌类型做格式化后处理否则输出结果可读性很差。如果二值化受光照影响严重投影分割不干净我会退回用检测框内的字符高度分布和先验知识做辅助判断实在不行就把整块图同时送单层识别和双层切分识别比较两个结果的置信度取置信度高的一方作为最终输出。4. 推理部署与工程化实现4.1 完整的推理流程从图片到车牌文本推理阶段是把所有模块串起来的环节我的pipeline顺序是读图、yolov7检测车牌框、按置信度过滤、对每个框裁剪、HSV颜色判断、倾斜校正、单双层判定、归一化缩放、LPRNet识别、后处理格式化。这里每一步的顺序都是有讲究的。颜色判断必须放在倾斜校正之前因为校正过程会插值重采样可能导致颜色失真单双层判定也应该在缩放之前做否则高度压缩后上下行的边界就找不清了。import cv2 import torch # 假设 detector 是加载好的yolov7模型recognizer 是识别模型 def plate_recognition(frame): boxes detector.detect(frame, conf_thres0.5) results [] for box in boxes: x1, y1, x2, y2, conf box plate_img frame[y1:y2, x1:x2] plate_type classify_color(plate_img) if is_double_layer(plate_img): text recognize_double_layer(plate_img, plate_type) else: text recognize_single_layer(plate_img, plate_type) results.append({box: box, text: text, type: plate_type}) return results上面是简化示意真正工程里还需要加一个目标跟踪器让视频流中同一个车牌在连续帧里只输出一次避免重复识别。我用的是简单的IOU匹配加卡尔曼滤波效果够用没有上更重的DeepSORT毕竟车牌识别场景里移动速度相对有限。4.2 倾斜校正与透视变换的实现细节车牌在画面里很可能是倾斜的尤其车辆变道或相机安装角度歪的时候。倾斜的车牌直接送识别模型字符会被压缩变形识别率明显下降。所以校正步骤不能省。我的做法分两种情况小角度倾斜用仿射变换就能纠正大角度透视变形比如车辆侧向驶过需要做透视变换。小角度倾斜的处理思路是先用Canny边缘检测找到车牌框的长边再用霍夫变换检测直线计算直线的角度然后按这个角度旋转图片把车牌摆正。Simulate一个场景车牌右倾10度旋转-10度之后就水平了。这个方案实现简单速度快适合闸机、监控等相机位置相对固定的场景。大角度透视的处理更复杂一些需要找到车牌的四个角点然后计算透视变换矩阵映射成一个规则的矩形。角点定位我用的是轮廓近似方法先找最大轮廓再用approxPolyDP拟合四边形。如果场景中车牌边缘被遮挡轮廓破裂这一步容易失败所以我一般先用HSV颜色掩膜把车牌区域提取出来再做轮廓查找成功率会高很多。4.3 性能优化TensorRT加速与批处理纯PyTorch的推理速度在GPU上还能接受但在CPU上跑就非常吃力了。如果要把项目部署到边缘设备或者只有CPU的服务器上TensorRT加速是绕不开的一步。yolov7检测模型转TensorRT官方仓库提供了工具脚本导出engine文件后推理速度能提升2到3倍。识别模型结构简单转TensorRT要稍微改一下网络层定义去掉动态shape的麻烦把输入固定成132494就行。转完之后一张图的完整识别耗时在GPU上能压到10毫秒以内基本满足实时摄像头流处理需求。如果暂时上不了TensorRT也可以先用半精度推理加批处理过渡。半精度就是模型参数和输入都转成float16显存占用减半速度也有提升。批处理则是把多张车牌图打包成一个batch一次推理GPU利用率会高很多比如停车场同时有多辆车入镜的场景检测出多个车牌后一起送识别模型吞吐量提升非常明显。5. 常见问题与排坑记录5.1 车牌漏检和误检怎么排查漏检是最让人头疼的。遇到漏检我一般先从三个方向排查。第一步检查检测阈值。conf_thres设得太高比如0.7以上低置信度的真实车牌会被滤掉这种情况把阈值降到0.25到0.3再看漏检往往就消失了。第二步检查输入分辨率。如果车牌在原图中确实很小或者车辆距离较远640分辨率的特征图中车牌可能只剩几个像素检测器很难捕捉。把推理输入放大到960甚至1280召回率提升很明显但要注意显存占用和速度变化。第三步检查训练数据分布。如果漏检集中在夜间或者某个特殊类型的车牌上说明训练集里这类样本太少了。此时需要针对性补充数据比如专门收集夜间车牌的图片做增强训练比盲目加数据更有效。误检的情况通常是车身贴纸、车灯区域、甚至路面反光被当成车牌。这类问题可以通过在检测后加一个颜色校验环节来过滤车牌的HSV颜色必须落在特定范围蓝色、绿色、黄色等不在范围内的检测框直接丢弃。这样误检率能降一个量级付出的计算成本却极小。5.2 识别字符混淆怎么办识别阶段最常见的错误是易混淆字符比如“0”和“O”、“1”和“I”、“8”和“B”还有汉字简称之间的混淆。这类问题不是简单调参能解决的要从数据和处理两个层面同时下手。数据层面我在字符集合里直接去掉了I和O强制模型把这俩字母当作数字1和0来识别。这是因为中文车牌里字母I和O本来就不使用去掉后可以避免大量无意义的二义性。另外针对容易混淆的汉字比如“冀”和“鲁”、“湘”和“浙”我在合成数据里加大了这些汉字的渲染样本量让模型见过更多变体。处理层面我在识别结果后加了一个规则校验。如果识别出的省份简称不在合法列表里或者车牌长度不符合该车牌类型对应的位数就回退到置信度第二高的候选结果。这个规则校验虽然简单但能把明显不合逻辑的识别结果拦截下来。还有一种情况是识别模型对模糊车牌输出结果不稳定连续帧识别结果不一致。我加了一个时序投票机制在视频流中记录同一个车牌的多次识别结果取频率最高、置信度最高的作为最终输出。这个机制对静态场景特别有效基本能消除单帧识别抖动。5.3 双层车牌识别失败的处理经验双层车牌失败的原因很多时候不是识别模型不够好而是前面的切分环节出了问题。二值化在光照不匀时会把上下两行的边界糊在一起导致切分线找不到或者切出的下行内容包含上行残留。我的改进思路是不依赖单一的二值化方案而是同时尝试固定阈值、自适应阈值和HSV颜色通道分离三种方式各自做投影分析最后投票选择最稳定的切分结果。自适应阈值对大光比场景适应性更好但容易把字符和背景粘连HSV分离对蓝色、黄色车牌特别有效因为底色和字符颜色在HSV空间有明确区分。另外一个实用的建议是对双层车牌搜索上行字符和下行字符的投影峰值时不要把阈值定得太敏感否则会把一个字符的笔画分到两行。我通常先把投影图做一遍高斯平滑再找波峰波谷这样切分线更稳定。如果切分后上下行都无法可靠识别我会额外跑一次整块图的热力图注意力可视化检查模型到底关注了哪些区域。很多时候是因为训练数据里双层车牌带“挂”字的样本太少导致模型看到双层结构就困惑。补充一批双层车牌的真实和合成数据重训识别模型之后问题自然就缓解了。6. 扩展方向与优化建议6.1 从静态图到视频流的实时识别目前整个系统已经能在单张图片上稳定跑通但真实场景往往是摄像头视频流这中间还有很多工程细节要处理。视频流输入需要做帧率控制不要每一帧都跑完整检测识别那样既浪费算力又容易产生大量重复结果。我通常会设置一个冷却时间同一个车牌被识别成功后5秒内不再重新识别期间只做目标跟踪。车载摄像头角度会变化倾斜校正的模块要频繁触发如果发现透视变换失败率升高可以适当扩大搜索的角点范围或者改用基于关键点回归的校正网络。另一个优化方向是让识别模型对低质量图像更鲁棒。比如雨雪天气车牌沾水、泥点遮挡这类退化样本很难提前预料。我的做法是在推理前加一个轻量级图像质量评估模块如果图像太模糊先做一次超分辨率重建或者去雨去雾增强再送识别模型。这个增强模块的加入会增加额外耗时只有在质量评估觉得“可能需要”时才触发用在性价比高。6.2 模型剪枝与轻量化部署如果目标设备是树莓派、Jetson Nano这类嵌入式平台就算当前模型能跑性能和功耗也完全不够。这时候需要做模型剪枝和量化。yolov7本身支持结构重参数化把训练好的模型重参数化之后推理图结构更简洁速度自然提升。再配合FP16或者INT8量化模型体积能压到原来的四分之一推理速度提升约两到三倍。我在Nano上实测过FP16量化后检测加识别一张图的总耗时从原来的800毫秒降到300毫秒左右基本达到准实时的可用水平。识别模型更轻量可以直接改成MobileNet系列的backbone或者用结构重参数化把卷积层融合掉。LPRNet本身已经很小我主要是把输入尺度从94乘24进一步缩小到80乘20牺牲少量精度换来更快的速度。嵌入式设备上跑精度下降一点点问题不大但速度能稳定达到25帧以上现场体验完全不同。以后如果想把项目做得更通用还可以在检测端引入更轻量的模型结构或者在识别端尝试基于Transformer的序列方案。不过从工程稳定性角度讲现在的组合已经很成熟后续优化重点应该放在数据迭代和场景适配而不是频繁更换模型结构。这套方案从零到落地最深的体会是车牌识别真正难的地方不是模型结构而是数据分布和工程细节。yolov7和LPRNet都是成熟方案网上资料很多但能准确稳定识别中文车牌、尤其是双层车牌的靠的是数据补齐、颜色判断、切分逻辑和后处理校验这些看起来不起眼的模块。如果你也在做相关项目建议先跑通现有的开源代码再按自己的场景逐步调整。踩坑是难免的但每踩一个坑把原因记录下来项目就会越来越稳。希望这份记录能帮你少走一些弯路。本文还有配套的精品资源点击获取