ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于YOLO的汽车牌照检测实战:从数据标注到模型部署

2026/8/31 15:22:12 拓冰建站 浏览量
基于YOLO的汽车牌照检测实战:从数据标注到模型部署 简介本资源是面向人工智能视觉方向初学者与实战开发者的YOLO车牌检测专用数据集适用于车牌识别系统研发、智能交通项目实训及目标检测模型调优等场景。数据集共433张真实车牌PNG图像配套433个YOLO格式TXT标注文件含归一化中心坐标与宽高及1个data.yaml配置文件总计867个文件压缩包大小203MB目录结构严格划分为train/images、train/labels、test/images、test/labels四级路径训练集346张80%、测试集87张20%开箱即用无需额外划分。目前已有152人学习下载所有标注统一以类别标签0表示车牌格式规范、边界框精准配合示例文件可快速验证数据加载逻辑与模型训练流程特别适合YOLOv5/v8等主流框架的端到端车牌检测任务实践。1. 项目整体设计与思路拆解车身颜色、车标、车型、车牌这四个要素基本构成了车辆识别的核心信息而车牌识别又是其中落地场景最丰富的一块——停车场出入口、电子收费、违章抓拍、园区安防全都依赖它。但很多初次接触这个方向的同学容易忽略一个关键点无论下游接的是OCR字符识别还是直接做端到端的车牌识别第一步都必须先把车牌区域从复杂场景中“找出来”。这个“找出来”的动作就是目标检测。我这次做的项目就是基于YOLO系列模型实现汽车牌照的检测核心任务是从一张图里把车牌的位置框出来输出类别和坐标。整套流程下来涉及数据集整理、标注校验、格式转换、模型训练、效果评测、推理部署几个阶段。目前业内的主流方案里YOLO系列因为部署方便、推理速度快、精度也不错基本是首选即便在移动端和边缘设备上YOLO也有轻量化版本能压住帧率。这个项目适合两类人参考一类是有检测基础但没碰过车牌场景的人另一类是想找一个完整工程链路案例、从数据到部署通走一遍的初学者。为什么把车牌检测单独拎出来做而不是直接套一个通用检测模型因为车牌检测有几个通用模型不太友好的特点目标尺寸小一张1080p的图中车牌往往只占几十分之一的面积长宽比比较极端蓝牌和新能源牌照大约是3比1到4比1的横向矩形而港澳等地又有近似正方形的样式再加上不同省份、不同字体的字符差异光照、遮挡、角度带来的干扰。这些因素决定了它需要一套针对性的数据策略和训练方案不能无脑套用通用权重。另外在正式动手之前需要先明确一个任务边界这个项目做的是“定位”车牌不是“识别”车牌字符。两者在流程上是先后关系——检测负责给识别抠出准确区域识别只针对已裁剪的车牌图像做OCR。项目里我用YOLOv8作为主力模型也顺手对比了YOLOv5和YOLOv7的推理表现。整体而言YOLOv8在训练速度和细节特征提取上更稳且官方提供的接口和自定义配置都比较顺适合作为项目的主干方案。1.1 需求拆解与技术选型车牌检测的本质是一个单类别的目标检测问题——不需要区分“蓝牌”“黄牌”“新能源绿牌”这几个子类只要能把车牌区域框出来就完成目标。但这不代表把问题想简单了就够实际过程中会遇到的干扰非常具体车灯反射、车牌上方的文字贴纸、车头部位的品牌标识都容易产生误检而夜间、逆光、雨雪天条件下车牌区域的纹理信息会被严重削弱这对特征提取提出了额外要求。在模型选型上我对比了YOLOv5、YOLOv7和YOLOv8三套方案。YOLOv5胜在稳定、社区资料多出了任何问题都能搜到解决方案YOLOv7的检测精度稍高但配置成本略高YOLOv8在速度和精度之间取得了更理想的平衡而且用新的Anchor-Free方式处理目标框对车牌这种小目标更友好一些。考虑到后续可能要接到移动端我最终选了YOLOv8系列里的n/s两个规格一个是极轻量的n版一个是中等体量的s版便于后续对不同硬件做取舍。提醒一句Anchor-Free和Anchor-Based的差异不要只看概念。在实际训练中Anchor-Free可以让模型更灵活地适应不同宽高比的目标而车牌恰好是高宽比不统一的目标所以这个特性不是噱头是实打实的收益。1.2 项目准备工作清单在数据还没到手之前先把环境和工具链准备好。我用的是单张NVIDIA GeForce RTX 3060 12G显卡显存不算大但跑YOLOv8s配合合适batch size完全够用。软件层面系统Ubuntu 20.04 LTS深度学习框架PyTorch 1.13.1 CUDA 11.7模型库ultralytics YOLOv8官方仓库标注工具LabelImg本地标注 X-AnyLabeling半自动辅助标注数据处理Python 3.8 OpenCV pandas数据是所有检测任务的起点。我给自己的要求是正样本数量不低于5000张负样本不包含车牌的车辆图、道路场景图不少于1000张标注框的准确度必须人工复核一遍不能指望自动标注工具一步到位。准备工作的细致程度直接决定后面训练过程是否坎坷。2. 数据集准备与标注细节车牌检测的数据集有现成的开源组合可用最常用的包括CCPD中国城市停车场数据集全部来自真实停车场场景、各类爬虫采集的多场景车牌图、以及通过数据增强扩展的小样本集合。CCPD是国内车牌检测项目最常用的大规模公开数据集之一里面的图像覆盖了不同光照、角度、距离下的车辆前脸和车尾但目前我实际使用时发现它的标注格式不是YOLO格式而是基于坐标比例的一种记录方式需要先做一次格式转换并且它包含一些低质量、模糊的图片在过滤噪声前直接拿来训练可能拉低指标。我的做法是“开源数据自采数据”混合。先用CCPD选出的1万张高质量图片打底再补充了自己采集的1200张不同环境下的车辆图片这些自采图片主要覆盖CCPD里较少的场景——夜间、雨天、逆光、新能源绿牌、倾斜视角。最终数据集划分如下训练集9000张、验证集1500张、测试集1200张。2.1 标注规范与格式转换在开始标注之前先把标注规范定死。车牌的检测框区域我规定为车牌外边缘的内缩一小段矩形也就是要把车牌边框的白色边缘包含进去但不能把车头的进气栅格、车灯等部分框入。这个细节看似微小但不同标注人员如果标准不一致会让模型在推理时框偏导致后面OCR的输入区域不干净。实际标注时我有几个习惯对倾斜角度大的车牌用旋转框标注后再转成水平框因为YOLO的标签是水平矩形旋转框转水平框时要保证能完整包住车牌区域对遮挡超过30%的车牌选择不标注避免给模型制造混乱的边界夜间图片统一做亮度增强后再检查标注位置避免框住光晕区域一个画面出现多块车牌时例如前面车的车牌和远处车的车牌按从小到大全部标注不遗漏小目标数据格式转换方面YOLO格式的标签是一个txt文件每一行是“类别ID 中心点x坐标 中心点y坐标 框宽 框高”并且坐标值都是归一化的。格式转换直接用脚本自动化处理import json import os def cppd_to_yolo(json_path, img_width, img_height, save_path): with open(json_path, r, encodingutf-8) as f: data json.load(f) # 遍历标注中的车牌区域 plates data.get(plates, []) lines [] for plate in plates: # 获取四个顶点坐标通常是左上、右上、右下、左下 x1, y1 plate[x1], plate[y1] x2, y2 plate[x2], plate[y2] x3, y3 plate[x3], plate[y3] x4, y4 plate[x4], plate[y4] xs [x1, x2, x3, x4] ys [y1, y2, y3, y4] min_x, max_x min(xs), max(xs) min_y, max_y min(ys), max(ys) # 转成YOLO需要的中心坐标和宽高并做归一化 center_x ((min_x max_x) / 2) / img_width center_y ((min_y max_y) / 2) / img_height box_width (max_x - min_x) / img_width box_height (max_y - min_y) / img_height lines.append(f0 {center_x:.6f} {center_y:.6f} {box_width:.6f} {box_height:.6f}) with open(save_path, w) as f: f.write(\n.join(lines))这段脚本的核心逻辑是把多边形的四个点坐标先求外接矩形再转成YOLO格式的归一化坐标。需要注意两点一是不同数据集的JSON字段名可能不一样脚本里的字段需要按实际情况调整二是如果数据集的坐标系原点在右下角需要做镜像翻转处理否则训练出来的模型位置全偏。2.2 数据集目录结构与验证YOLOv8的数据集目录结构有一定约定需要严格按照训练集、验证集、测试集分开。我自己常用的组织方式是把训练图片放在train/images对应的标签放在train/labels验证集和测试集同理保持图片与标签文件同名。这种结构虽然老套但好处是后续不管换哪个YOLO版本目录都不用动。还有一个低级的坑标签和图片的数量对不上。有些开源数据集下载不完整或者标注文件损坏直接丢进去训练会报错。我习惯在训练前写一段校验脚本检查每张图片是否存在同名标签文件顺便统计标签内容是否为空、坐标值是否超出[0,1]范围。这一步建议不要偷懒特别是用混合数据集时不同来源的数据格式可能有细微差异统一校验能省下大量排查时间。3. 训练配置与实操过程数据准备好之后就进入训练环节了。我是用YOLOv8官方仓库来跑的训练脚本里最关键的几个配置是数据集配置文件、模型规格、训练轮数、图像尺寸、batch大小、数据增强参数。下面详细说说这些参数怎么定。3.1 数据配置文件与训练参数解析数据配置文件是一个data.yaml内容大致如下train: /data/license_plate/train/images val: /data/license_plate/val/images test: /data/license_plate/test/images nc: 1 names: [license_plate]这里最关键的是nc: 1表示当前任务只有车牌这一个类别。不要小看这个参数有时候在网上复制别人的配置忘了改类别数模型会默认按80个类别训练那结果就是训练起来损失下降正常但推理出来的全是没有意义的类别标签。训练命令我用的是yolo train data/data/license_plate/data.yaml modelyolov8s.pt epochs200 imgsz640 batch16 lr00.01其中yolov8s.pt是官方预训练权重基于COCO数据集训练得到的使用预训练权重做迁移学习可以明显加快收敛同时也能让模型在早期就具备一定的特征提取能力。imgsz640是输入图片的尺寸如果数据集存在大量小目标车牌可以尝试imgsz960甚至imgsz1280但显存占用会显著上升需要根据显卡情况平衡。我做了对比实验在同样训练轮数下imgsz从640提升到960后小目标的召回率上升了约3个百分点但训练时间增加了近一倍。batch size的取值取决于显存大小。12G显存跑YOLOv8s的话batch16比较稳如果强行调到32可能爆显存不过YOLOv8默认开了梯度累积可以缓解这个压力。我个人的经验是batch factor尽量取8的倍数因为GPU的并行计算结构对8的倍数友好一些。3.2 训练过程观察训练过程中要盯住几个关键的指标变化趋势不要只关心loss数值。YOLOv8日志里会出现box_loss、cls_loss、dfl_loss以及验证集上的precision、recall、mAP50、mAP50-95。我观察到的一个典型现象是前30轮loss下降非常快mAP50很快达到70%以上但是从第80轮开始mAP50的提升变得极其缓慢此时如果loss继续下降而mAP不再上升说明模型开始过拟合。这个阶段我会提前用早停机制或者调整数据增强的强度。数据增强方面YOLOv8默认开启了Mosaic增强就是把4张训练图拼成一张这对小目标检测的提升非常明显。但它在车牌场景下有个反作用如果Mosaic把4张车牌的尺寸缩得太小模型可能会学到“车牌就是一团模糊的纹理”这种不太好的特征。所以我把Mosaic的概率从默认的1.0调到了0.8并且关掉了标注框与图像发生严重裁剪时的增强逻辑避免训练数据出现“半块车牌”这类怪异样本。3.3 小目标优化策略车牌在YOLO中被归为小目标类别因为它在整个画面中的尺寸占比通常不足10%。针对小目标检测我额外做了三件事第一增加高分辨率输入。将imgsz设为960后小尺寸车牌在缩放过程中的信息损失明显减少检测框稳定性和定位精度都有提升。第二将训练集中的大图做了滑窗拆图切成若干小块再训练。这种方式相当于在训练阶段人为放大车牌对象的相对尺寸对于小目标比较有效。但要注意推理时如果输入整图尺寸超过模型输入要求需要在后处理时把切分的检测框映射回原图坐标这个过程我用了一段坐标映射的辅助代码。第三调整置信度阈值。训练完成后推理阶段默认的conf阈值是0.25但对车牌场景我习惯设为0.3到0.4之间因为车牌检测的误报率主要来自车灯、广告牌文字等地方提高阈值可以有效滤掉这些误检同时还能保证真正的车牌不被漏掉。3.4 模型训练优化的补充尝试除了前面提到的策略我还尝试过在YOLOv8的检测头前面添加一个注意力模块比如SESqueeze-and-Excitation或CBAMConvolutional Block Attention Module。注意力机制的本质是让模型在提取特征时更关注车牌区域而忽略背景干扰。实验结果显示加上CBAM后mAP50提升了约1.5个百分点但推理速度也降低了一些。如果项目对速度要求不那么苛刻这个方向值得尝试。另外我在训练时还使用了标签平滑label smoothing来提高模型泛化能力。车牌数据的标注质量虽经过人工校验但难免存在个别边界不准确的情况。标签平滑可以避免模型对标注精度过度敏感最终让检测框的稳定性更好不容易出现框的左边界忽大忽小的问题。如果追求更高精度还可以引入多尺度训练让模型在每轮迭代时随机采用不同尺寸的输入图。我在后期训练中把scale参数设为0.5也就是输入图像尺寸会在640到960之间随机变化。这种做法的代价是训练时间变长但收益是模型对不同距离拍摄的车牌都有更好的适应能力。4. 模型测试评估与常见问题排查训练结束后模型的测试评估过程非常关键直接影响后续能否落地。不要只看验证集上的指标我习惯准备一个完全独立的测试集里面尽量包含训练中没见过的场景。4.1 评估指标到底怎么看YOLO训练输出的指标里mAP50是IoU阈值取0.5时的平均精度mAP50-95是多个IoU阈值下的平均精度。对车牌检测来说mAP50的参考价值更大因为车牌检测最终要给OCR提供定位区域IOU在0.5以上就足够保证OCR准确率但mAP50-95能反映定位框的精细程度如果这个值太低说明框的位置不稳定后续做车牌矫正时会增加额外计算量。我最终的模型跑出来的数据大概是这样的指标数值precision0.968recall0.941mAP500.972mAP50-950.836单帧推理速度GPU12ms这个成绩对单类别检测来说已经具备实用价值。但注意precision和recall之间存在权衡如果业务上更看重不漏检比如收费场景可以适当降低置信度阈值把recall提升到0.97左右代价是precision会有所下降。4.2 推理脚本与结果可视化训练完成后我写了一个简单的推理脚本支持对单张图片和视频流做检测from ultralytics import YOLO import cv2 model YOLO(/data/license_plate/runs/train/exp/weights/best.pt) img cv2.imread(/data/license_plate/test/images/0001.jpg) results model.predict(img, conf0.35, imgsz960) for result in results: boxes result.boxes.xyxy.cpu().numpy() scores result.boxes.conf.cpu().numpy() for box, score in zip(boxes, scores): x1, y1, x2, y2 [int(v) for v in box] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f{score:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(/data/license_plate/output/detected.jpg, img)在真实测试图片和视频片段上模型能稳定框出车牌且大部分框都贴合车牌边缘。这说明前期的标注规范和训练参数调整是有效的。4.3 常见问题与解决方案整个项目过程中我踩过不少坑这里整理几个典型的问题现象原因与解决方案夜间检测漏检率高光线不足时车牌常常框不出数据集中夜间图片太少补充夜间样本并做亮度归一化增强误检车灯两个圆形车灯被框成车牌提高置信度阈值到0.35以上并检查标注时是否把反光区域误标为代表框车牌框偏大框的边缘明显超出车牌标注规范不够严格需要用更紧密的框重新标注并重新训练训练后loss为NaN训练早期loss直接变NaN学习率设置过大把lr0调整到0.001可以解决推理时显存爆掉输入高清大图时OOM降低imgsz或改用批量推理一次只处理一张图片4.4 小目标漏检的专项排查距离远、分辨率低的车牌在推理时容易漏检这是小目标检测的老大难。我针对这个问题做了专项排查和优化。首先是数据层面的优化。我统计了后台测试数据中被漏检的车牌在整幅图像中的面积占比发现绝大部分漏检的目标面积占比都低于2%也就是在640x640的输入图像里车牌区域不足30x30像素。针对这个问题我专门筛选了一批这样的“极小目标”样本在训练集中做了上采样复制让模型有更多机会见过这类样本同时在数据配置里把imgsz改为960增加输入分辨率后小目标的特征可以传得更充分。其次是推理层面的处理。对于视频流场景如果车牌连续多帧出现但某几帧漏检我会加一个简单的跟踪后处理利用卡尔曼滤波或IoU匹配来填补漏检帧这个方法在摄像头固定且车牌运动连续的场景下特别有效。还有一点容易被忽略模型在浅层特征图上对小目标更敏感但浅层特征也包含更多背景噪声。YOLOv8的检测头有三个尺度分别对应不同尺寸的目标。如果小目标漏检严重可以检查是否在训练时对浅层特征的权重做了不当的裁剪或者干脆改用带有专门小目标检测头的变体模型。5. 模型导出与部署注意事项训练好的模型要真正派上用场还需要做模型导出和部署适配。这个阶段也有不少细节。5.1 导出到ONNX和TensorRTYOLOv8官方接口支持导出各种推理格式。我导出了ONNX格式和TensorRT格式。ONNX格式的好处是通用性强可以跨平台部署TensorRT格式是为了在NVIDIA GPU上获得更高的推理速度。导出TensorRT的流程比较繁琐主要步骤包括把ONNX模型转成TensorRT引擎、确定批大小和精度模式、做动态shape配置。我用的是FP16精度模式推理速度比FP32提高近一倍而且精度损失在可接受范围内。如果部署环境是CPU则需要转成ONNX后再通过ONNXRuntime推理或者转换成OpenVINO格式这个格式在Intel平台上优化得很好。注意导出后的模型用起来和PyTorch推理有一些差异主要是TensorRT对输入图像的预处理方式要求不同。转换之后务必要用一张测试图片走完整条推理流程对比输出坐标是否和PyTorch一致避免因预处理不一致导致结果偏差。5.2 部署中的常见坑部署到实际业务中最容易遇到的问题依次是摄像头画面比例和训练集不一致比如方形图变成16比9宽幅图导致车牌被拉扁变形。这种情况下需要做等比缩放和padding后再推理。车牌倾斜角度过大会导致检测框虽然准确但OCR模块无法正确识别。建议在检测后接一个透视矫正步骤用OpenCV的getPerspectiveTransform把倾斜车牌拉正。视频流的帧率不稳定。检测模型推理耗时抖动较大会造成卡顿可以在部署时开启批处理把多个视频流合到一起推理提高GPU利用率。5.3 结合车牌识别的完整方案纯检测只能回答“车牌在哪里”要最终输出车牌号码还需要接入一个OCR识别模块。目前比较常见的做法有两类一类是用传统的OpenCV做字符分割再配合模板匹配或小型CNN分类器另一类是直接用端到端的OCR网络比如基于CRNN或是PaddleOCR的车牌专用版。前者的优点是部署轻量、逻辑简单对车牌这种字符布局相对固定的场景效果已经很稳定后者更适合字符倾斜、字体多样、光照极端的情况下使用。我在项目中先用YOLO检测框出车牌区域然后将裁剪区域输入到PaddleOCR的车牌识别模型里最终识别准确率大约可以做到90%以上。如果先做一个颜色分类蓝牌、黄牌、绿牌再按颜色分组匹配字符集准确率还能进一步提升。6. 项目经验总结与扩展思路做到这一步整个“汽车牌照数据集 YOLO 目标检测”项目基本走通。总结成一段话就是数据是基础、标注规范是杠杆、训练参数是催化剂、部署验证是检验标准。这四个环节环环相扣任何一环掉链子最终效果都会打折扣。我个人在这几次版本迭代中比较深的一个体会是——不要一开始就追求大模型。车牌检测是一个单类别任务需求相对聚焦并不需要像COCO那80类那样庞大的特征容量。先用轻量化的YOLOv8n打通整个链路配合数据迭代把mAP做到可接受范围再根据瓶颈决定是否升级到更大模型。这个顺序能帮你节省大量调试时间也能更快定位是数据问题还是模型能力问题。6.1 数据迭代和模型迭代的配合方式在项目初期我先用4000张标注好的图片训了一版模型mAP50只有88%漏检主要集中在夜间和远距离场景。随后我做了数据补采专门增加了800张夜间图片和300张远距离小目标图片重新训练后mAP50直接提升到了94%。这说明数据补充带来的增益往往比换更大的模型更明显。如果后续还要进一步提升可以考虑在模型迭代上做几个方向把YOLOv8的C2f模块替换成更轻量的结构来提升速度在检测头引入可变形卷积来适应车牌的多角度变化用知识蒸馏的方式让大模型指导小模型学习兼顾速度和精度。6.2 项目可扩展的场景车牌检测之外这套数据准备、训练调优、部署验证的流程完全可以复用到其他目标检测任务上。比如交通标志检测同样是小型目标、多类别、对精度要求高车辆零部件检测质检场景中需要对产品上的局部小目标做定位无人机视角下的车辆检测目标小、背景复杂与车牌检测有相似的技术难点行人属性识别中的头肩检测辅助人脸识别或客流统计我想说的是目标检测本身是一个通用能力但每次换到一个具体场景都有它的特殊脾气。车牌检测的核心难点不在模型结构而在于怎么把数据质量、标注标准和训练策略针对这个场景的独特问题调到一个舒服的位置。把这套方法论掌握住换什么场景都不会慌。本文还有配套的精品资源点击获取