
简介基于YOLOV5的目标检测车牌定位与识别项目源码面向计算机视觉初学者及需落地车牌识别场景的开发者解决自然场景下车辆车牌快速定位与精准识别问题。压缩包共68个文件以19个py源码脚本、9个yaml模型配置、5个pt预训练权重、16个jpg测试图片及OCR相关文件为主整体约321.5MB目录结构清晰便于直接运行与二次开发。整个流程涵盖车牌区域定位、边框校正、超分辨率增强与光学字符识别经多场景测试支持图片及视频实时识别速度和准确率优于传统车牌识别方案。资源已有2529人学习是理解YOLOV5工程化应用、积累目标检测实战经验的优质参考。 车牌识别这种项目在CV方向算是最经典的落地场景之一了。从停车场道闸到违章抓拍再到最近很火的智慧园区、高速收费到处都用得上。我当初做这个项目的时候目标很明确不搞花里胡哨的算法对比就用YOLOv5老老实实把“车牌子在哪”和“车牌号是啥”这两件事做扎实。这篇博文就算是个实操记录从环境搭建、数据集处理、模型训练到最后的Web界面部署和边缘端移植思路把我踩过的坑和验证过的方案一次性讲清楚。1. 项目整体设计与任务拆解1.1 为什么不直接端到端识别而是先定位再识别车牌识别说白了是个两阶段任务。很多人一开始会想能不能拿一个模型直接输入图片输出车牌号理论上可以但实际搞过就知道端到端模型对数据量和场景泛化的要求太高了。车牌有蓝牌、黄牌、绿牌新能源、白牌军警字体、颜色、倾斜角度、光照条件五花八门一个模型直接输出字符串中间任何一环出了问题都很难排查。所以这个项目走的是“定位识别”的两阶段路线。第一步用YOLOv5把车牌区域从整张图中框出来第二步对框出来的车牌区域再做字符级别的识别。这么做的好处有三个第一个是任务解耦定位不准可以单独优化定位模型识别出错可以单独优化识别模型不会互相干扰第二个是数据利用率高定位模型只需要标注车牌框识别模型只需要车牌区域图数据准备的门槛大大降低第三个是推理速度快YOLOv5的轻量级特性保证了实时性不管是CPU还是边缘设备都能跑得动。1.2 技术选型为什么YOLOv5是毕设和实战项目的最优解说实话现在YOLO系列已经出到v8、v9甚至v10了但我依然推荐新手或者做工程落地优先考虑YOLOv5。原因不复杂社区生态太成熟了。GitHub上的issues、博客教程、开源权重、各种魔改版本满天飞你遇到的大多数环境问题、训练问题基本搜一下就能找到答案。而且YOLOv5对硬件要求相对友好GTX 1660这种级别的显卡就能跑得有模有样不像某些新模型动辄就要24G显存。还有一点很实际就是部署形态灵活。YOLOv5export.py跑一行命令就能导出TorchScript、ONNX、TensorRT、OpenVINO等多种格式后面要往树莓派、Jetson Nano或者Windows上部署都很顺畅。我做这个项目时先是PyTorch模型直接跑推理后面又导出了ONNX用OpenCV的DNN模块加载整个过程几乎没有额外适配成本。2. 数据集准备与标注经验分享2.1 数据从哪里来怎么处理才靠谱做车牌识别项目数据是起点也是决定上限的环节。我用的数据主要有三个来源公开数据集比如CCPD自己开车记录仪的截图以及网上搜集的停车场、街景图片。CCPD是合肥工业大学的开源数据集规模很大但有个问题是它偏重于安徽的场景直接拿来做训练很容易过拟合到特定环境。混合自己的数据能明显改善泛化。拿到原始图片后先做一轮清洗。删掉重复帧、模糊到人眼都认不出的图、严重遮挡的图。这里有个经验之谈车牌区域占了整张图不到5%的图片在训练定位模型时尽量剔除或者单独处理不然模型会倾向于把整辆车框进去因为那样loss更低。清洗完的数据我按8:1:1切成训练集、验证集、测试集并且保证三部分的数据分布一致避免某个场景只出现在训练集里。对于识别模型的数据准备思路就不太一样了。识别模型吃的是车牌区域图我直接从定位模型的检测结果里截取加上CCPD数据集里的裁剪图按省份简称、字母、数字分类存放。这一步工作量大但值得细心做因为后面你要是不想手动标注成千上万张图这里的数据组织方式决定了你能不能顺利跑通自动标注流程。2.2 标注工具与自动标注的小技巧定位模型的标注工具我用的是LabelImg虽然界面朴素但胜在稳定支持Pascal VOC和YOLO两种格式直接导出。YOLO格式是txt文件每行代表一个目标内容是“类别id x_center y_center width height”注意这里的x_center、y_center、width、height都是相对图片宽高的归一化值。这个格式稍微有点绕但LabelImg会自动生成不用手算。识别模型的标注更讲究技巧。如果逐张框字符位置再标字符内容效率低到怀疑人生。我的做法是先写一个脚本按照车牌字符宽度均匀切分出七个区域新能源车牌是八个字符然后在字符样本库里批量确认每个区域的内容。前提是定位模型输出的车牌框足够正如果倾斜严重就要先做仿射变换校正。实际上字符分割这一步用固定宽度切分不是最优但配合角度校正后准确率也能到95%以上对于毕设和展示型项目完全够用。注意CCPD数据集的标注里没有直接给出字符级别标签但它的文件名本身包含省份简称和车牌号可以用正则表达式解析出来配合框坐标批量生成字符级训练数据。这个技巧能省下至少几天的人工标注时间。3. YOLOv5环境配置与训练流程详解3.1 环境配置那些坑一次说清楚YOLOv5环境配置本身不复杂核心依赖就几个PyTorch、OpenCV、matplotlib、numpy、tqdm。真正让人头大的是版本匹配问题。我实际用的组合是Python 3.8 PyTorch 1.8.2 CUDA 11.1 cuDNN 8.0.5这个组合虽然不新但胜在稳定网上排查问题能搜到的帖子最多。配置的时候有几个细节值得注意。第一用conda创建独立环境不要直接装在base环境里后面项目多了你就知道这有多重要。第二PyTorch的安装建议去官网用pip安装不要用conda默认源慢不说还经常装到CPU版本。第三如果电脑没有NVIDIA显卡也不要放弃这个项目YOLOv5支持纯CPU训练只是速度慢几倍用yolov5s模型配上较小的输入尺寸和batch size跑个几十轮还是能出结果的。验证环境是否配置成功最快的办法就是下载官方预训练权重然后跑一行python detect.py --weights yolov5s.pt --source data/images/bus.jpg如果能看到检测框输出恭喜你后面的事情就顺利多了。3.2 训练自定义数据集的完整流程数据准备好后要在YOLOv5项目根目录下新建自己的数据集配置文件。我是这样组织的# plate_data.yaml train: /home/user/plate_dataset/images/train val: /home/user/plate_dataset/images/val test: /home/user/plate_dataset/images/test nc: 1 names: [license_plate]这里nc是类别数我们只检测车牌一个类别所以是1。names列表里写类别名要和标注文件里的类别id一一对应。配置好之后把标注好的txt文件分别放到train和val目录下对应的labels文件夹里注意图片和标注文件的文件名要一致。训练启动命令是这个python train.py --data plate_data.yaml --weights yolov5s.pt --epochs 150 --batch-size 16 --img 640 --device 0参数选择上有几个经验。第一用yolov5s这个版本起步最合理。yolov5n精度偏低yolov5m训练时间和算力要求翻倍但精度提升并不算大yolov5s正好平衡。第二img大小我选了640这是速度和精度的折中点。车牌是小目标选320虽然更快但小尺寸车牌容易漏检我试过好几个数据集640最稳定。第三epochs我设了150配合早停机制一般训练到100轮左右就会收敛后面基本就在验证集上波动了。3.3 超参数调整新手最容易忽略但影响最大的环节很多人训练YOLOv5只调batch size和epochs对超参数文件不闻不问。实际上YOLOv5的data/hyps/hyp.scratch-low.yaml里藏了不少门道。里面定义的lr0是初始学习率默认0.01如果训练初期loss剧烈震荡可以先降到0.005试试。warmup_epochs是热身轮数默认3.0是为了让训练更稳定不用动。我在车牌定位模型上实测有效的一个调整是增加mosaic数据增强的概率。YOLOv5默认启用了mosaic增强但如果你发现模型对边缘场景比如严重倾斜的车牌效果差可以把mosaic的概率调高或者延长mosaic生效的epochs。原理不复杂mosaic会一次拼接四张图相当于强行让模型看到更多样化的背景和小目标组合。另外车牌字符识别任务对锐利度敏感训练识别模型时我推荐关闭或者降低HSV增强里的饱和度变化幅度否则字符颜色会被扰乱导致识别模型把颜色特征当作主要依据泛化就会出问题。4. 训练效果调优与推理优化实践4.1 损失函数与训练监控怎么判断模型学得好不好训练过程中要重点盯三个指标box_loss框回归损失、obj_loss目标置信度损失、cls_loss分类损失。对车牌定位模型来说cls_loss一开始就会很低因为只有一个类别。真正要关注的是box_loss和obj_loss。如果在训练日志里看到box_loss降到0.03以下obj_loss在0.02左右波动验证集上的mAP0.5能超过98%这个模型算是练成了。YOLOv5训练过程会生成results.png图里面包含了各种曲线。我习惯重点看PR曲线它能直观反映模型在不同置信度阈值下的查准率和查全率。车牌检测的要求是“宁可漏检不要误检”因为漏检了顶多识别不到误检了可能把别的区域当成车牌去识别导致错误输出。所以我会把置信度阈值设得略高一般在0.5到0.6之间。4.2 推理加速与后处理调优YOLOv5默认的NMS非极大值抑制阈值是0.45IoU阈值是0.45。这个组合在一般场景下没问题但如果两张车距离很近车牌的检测框重合度比较高NMS可能会误杀其中一个框。我遇到过的情况是停车场里并排停的车左边的车和右边的车车牌在画面中重叠了一部分默认NMS下会丢失一个车牌的检测结果。把IoU阈值调到0.6后这个问题就不再出现了。推理加速方面我自己在部署阶段做了一个非常有效的优化使用half精度推理。PyTorch下设置model.half()显存占用几乎减半推理速度提升20%左右。代价是精度略有下降但在车牌识别这种场景里几乎感知不到。如果还要更激进可以用TensorRT导出FP16的engine文件在Jetson设备上推理速度能跑到10ms以内。另一个容易被忽视的提速点是设置推理尺寸。训练时用640推理时不一定要用640。如果你的摄像头画面里车牌区域比较大把推理尺寸降到320反而能获得更快的速度因为车牌已经足够清晰。如果画面中车牌很小那就老老实实保持640甚至升到960。这个需要根据实际部署场景去试。4.3 各模型参数横向对比模型版本参数量推理耗时(ms, T4 GPU)mAP0.5车牌场景实测适用场景YOLOv5n1.9M2.892.6%树莓派、手机端YOLOv5s7.2M3.997.1%常规实时检测YOLOv5m21.2M6.198.3%高精度场景YOLOv5l46.5M9.598.7%服务端高性能从表里能看出YOLOv5s和YOLOv5m之间mAP差距只有1.2个百分点但参数量差了将近三倍。车牌定位任务本身不算复杂yolov5s是最优解再多算力不如留给识别模型。5. Web界面搭建与识别系统集成5.1 Flask快速搭建车牌识别演示系统模型做得再好没有一个直观的界面展示答辩或者给领导汇报的时候总差点意思。我用Flask写了一个简单的Web演示系统支持上传图片、实时视频流识别两种模式。Flask的优势是轻量不用单独配前端框架直接render_template就能搞定。核心代码不复杂这里展示关键的推理接口app.route(/predict, methods[POST]) def predict(): file request.files[image] img_bytes file.read() img cv2.imdecode(np.frombuffer(img_bytes, np.uint8), cv2.IMREAD_COLOR) # 定位阶段 results plate_model(img, size640) boxes results.xyxy[0].cpu().numpy() for box in boxes: x1, y1, x2, y2, conf, cls box if conf 0.55: continue plate_img img[int(y1):int(y2), int(x1):int(x2)] # 识别阶段 plate_text recognize_plate(plate_img) # 画框和标注 cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(img, plate_text, (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) # 编码返回 _, encoded cv2.imencode(.jpg, img) return Response(encoded.tobytes(), mimetypeimage/jpeg)需要注意的是部署环境里如果没有GPU必须在加载模型时设置cpu_only模式map_locationcpu不然在服务器上会报错。Flask自带的开发服务器只能应付演示场景真要并发访问还是要换成gunicorn或者uwsgi。5.2 车牌字符识别从分割到OCR的完整方案车牌字符识别的实现方式有两条路可走。一条是基于字符分割CNN分类的传统路线稳定可靠另一条是用CRNN或者PaddleOCR直接做端到端的序列识别更加省事。在这个项目中我两条路都试过最终在演示系统中用的是分割CNN方案因为字符集固定各省简称24个字母10个数字训练一个小的分类网络就够了推理成本低。分割方案的关键在于字符切分。常规车牌是7个字符第一位是省份简称汉字第二位是发牌机关代号字母后面五位是字母和数字的混合。新能源车牌是8位但结构类似。切分时先对车牌区域做二值化和形态学处理再投影法找字符边界。我实测下来投影法对轻微倾斜的车牌不太稳定建议先做仿射变换校正再用固定间隔切分。CNN分类网络我用的是一个简化版ResNet输入尺寸32x32输出维度是字符集大小。训练数据直接从公开数据集和CCPD里批量裁剪每个字符样本都做好归一化。这个分类器单独评测准确率能到99.2%误差主要出现在相似字形上比如“8”和“B”、“0”和“O”、“D”和“O”。因为车牌本身有防伪字体设计实际识别时可以结合车牌规则约束一层后处理比如第二位一定是字母后五位中字母和数字的位置有规律可以过滤掉一些明显违反规则的输出。5.3 自动标注把训练的成果循环利用起来前面说到的自动标注其实就是在训练好第一版定位模型后用它去跑未标注的图片生成伪标签然后人工检查修正。具体流程是先拿公开数据集训练一个粗模型然后对大量新收集的图片跑推理把置信度高于0.8的检测结果直接转成YOLO格式的标注置信度介于0.5到0.8的挑出来人工确认。这个方案能让你从几千张图片的标注工作中省下至少一半时间。我专门写了一个小工具通过界面加载图片后自动显示模型预测框人工只需要看框对了没有错了微调一下然后一键确认。配合键盘快捷键一张图平均处理时间不到10秒效率比纯手工标注高好几倍。这个过程本质上就是半自动标注也是很多工业级数据闭环平台的核心思路。如果你的数据集图片特别多强烈建议先训一个初步模型再标注不要傻傻从头框起。6. 树莓派与边缘端部署思路6.1 模型轻量化与格式转换现在很多项目的落地场景都在边缘端最典型的是树莓派或者Jetson Nano配合USB摄像头做停车场入口检测。热词里提到的树莓派5部署YOLOv5模型其实就是模型轻量化加推理框架选型的问题。第一步是把训练好的PyTorch模型转成更适合边缘设备推理的格式。如果目标是树莓派可以直接导出TorchScript格式python export.py --weights runs/train/exp/weights/best.pt --include torchscript onnxONNX格式比较通用可以配合OpenCV DNN或者ONNX Runtime来跑不依赖PyTorch环境部署体积小很多。我实测下来同样一个yolov5s模型PyTorch直接推理一张640x640的图在树莓派4上要2到3秒换成ONNX Runtime优化后能压到1.5秒左右要是用NCS2神经计算棒加速还能再快一些。6.2 树莓派上的实际优化经验在树莓派这种算力受限的设备上光靠模型加速还不够还得从输入端想办法。我实际部署时做了三个优化。第一个是把摄像头分辨率降下来因为对幅面很大的画面做推理很多像素都是浪费的。直接把摄像头输出设在640x480检测效果几乎没有下降但预处理时间省了很多。第二个是用MJPEG格式读摄像头而不是默认的YUYV这个改动能把摄像头读取的CPU占用率降一半。做法是在picamera库初始化时强制指定format。第三个是针对车牌检测的专项优化摄像头固定后在画面上画一个兴趣区域ROI只对ROI内部做YOLOv5检测。因为车牌检测是静态安装场景车牌只会出现在画面的特定区域这样直接把推理面积缩小到原来的三分之一。提示如果你要部署到树莓派这样的ARM架构设备强烈建议在PC上训练模型导出ONNX后再在树莓派上装ONNX Runtime的ARM版本不要试图在树莓派上训练模型内存和散热都扛不住。7. 常见问题与避坑指南7.1 环境与训练高频报错排查YOLOv5的报错大多数集中在环境依赖不匹配上。第一个高频问题是“No module named torchvision”这个好解决pip安装对应版本就行。第二个是CUDA out of memory这种通常是batch size设太大了要么调小要么开启梯度累积或者直接用yolov5s甚至yolov5n。第三个是训练到一半loss变成nan大概率学习率太高或者数据里有异常的标签坐标检查一下标注文件里有没有越界的框。我自己遇到最坑的一个问题是同样的代码换了一台机器后训练出来的模型mAP大幅下降。排查了很久发现是两台机器的OpenCV版本不同导致图片读取时通道顺序和图像缩放算法有细微差别。这个案例提醒我项目复现时一定要把环境依赖的版本号锁死最好用requirements.txt固定版本。7.2 识别效果不佳的实战排查思路如果发现模型的识别效果不理想不要急着调参或者换模型先按顺序排查这几个地方。第一看输入数据。如果你部署现场的摄像头角度跟训练数据差异很大模型没见过这种角度的车牌识别效果肯定好不了。解决办法是收集现场数据微调或者用透视变换数据增强让模型看到更多角度的车牌。第二看预处理。车牌文字的边缘锐度对识别结果影响极大如果图片经过压缩传输JPEG压缩产生的人工噪声会干扰字符分割。可以在预处理时加一个轻度的锐化或者去噪操作。第三看置信度阈值。阈值设太高容易漏检设太低会出现误检。用验证集上的PR曲线确定阈值比凭感觉设置靠谱得多。第四看NMS设定。车牌密集场景适当调高IoU阈值这个前面说过了但很多人忽略。7.3 六类典型问题速查表问题现象可能原因解决方案loss不下降或震荡学习率过高或过低降低lr0到0.005启用Cosine LRmAP正常但漏检推理置信度阈值太高从0.25降到0.15观察效果绿色新能源车牌识别差训练数据中绿牌样本少补充绿牌数据或增加颜色增强车牌区域倾斜导致识别错缺角度校正环节增加仿射变换校正或DBNet文字检测摄像头画面拖影曝光时间过长提高快门速度降低帧率图片推理慢输入尺寸过大或未用half降到416或320开启半精度推理8. 结语与个人经验沉淀项目做到最后最深的体会是车牌识别这个任务难点从来不在模型结构而在工程细节。从数据清洗、标注工具链、训练超参数到部署环境的兼容性每一个环节都可以毁掉一个看似完美的模型。我始终觉得YOLOv5做车牌定位是同量级方案里最优的选择不是因为某个指标比别的模型高多少而是在整个生命周期里最省心。最后分享两个自己总结的小习惯。第一个每次训练实验前都把数据集、配置文件、超参数文件、代码版本号打包记录成一个日志这样模型效果变化时可以追根溯源。第二个保存best.pt和last.pt的备份时顺手做一个ONNX的导出归档因为你不知道什么时候就需要在嵌入式设备或者OpenCV环境里重新加载它。这两个习惯帮我避免了很多次“辛辛苦苦训练好模型却部署不上”的尴尬。本文还有配套的精品资源点击获取