
简介这套车牌图像数据集面向车牌检测与识别任务共包含1695张高质量实拍图像每张都有精确的车牌边界框标注以YOLO文本格式存储类别与归一化坐标可直接接入主流YOLO框架训练。压缩包内文件总数为2000主要组成为1696个txt标注文件、303个jpg图像文件及1个yaml配置文件txt与jpg一一对应yaml用于定义类别名称与数据集路径整体大小147.47MB结构清晰便于快速部署。当前已有132人学习下载对入门CV的开发者来说省去了手工标注的重复劳动也能为研究者提供标准化的测试基准。图像来源涵盖监控视频截帧、车辆评测实拍等多类场景包含不同角度与光照条件便于检验模型泛化能力配合yaml文件用户划分好训练/验证集即可开始训练适用于智能交通、停车场管理、安防监控等场景中的车牌识别算法验证。1. 车牌图像数据集到手先做什么1695张YOLO格式图片的真正用法多数人拿到一份“车牌图像数据集 YOLO格式 1695张图片”的组合时第一反应是直接开训第二反应是嫌数据量小。这两个反应我都经历过。实际做完一遍车牌图像识别项目后我的结论是这个量级的对象检测数据集最大价值不是帮你刷出天上掉的精度而是让你用最短时间把一条从数据检查、训练到验证的生产链路彻底跑通——它能把你的注意力从“格式转换”和“写解析器”上解放出来直接丢到真正决定模型上限的地方数据划分、类别均衡、过拟合控制。适合它的人是两类有真实车牌检测需求但暂无大规模标注资源的团队以及刚学完检测理论、想用真实数据把流程走顺的入门者。它当不了终点但它是性价比很高的起点。2. 训练前先摸数据底牌YOLO格式标注检查与数据集划分脚本2.1 读懂YOLO标签的五行数据归一化坐标与类别ID的约定YOLO格式的标注文件是一个和图片同名的.txt文件每一行代表一个目标框固定五个字段类别ID、中心点x坐标、中心点y坐标、框宽度、框高度。注意后四个值全部是相对于图片宽高的归一化比例取值范围在0到1之间。比如一个框的中心点正好在图片正中间宽度占整张图的一半那么这一行就是0 0.5 0.5 0.5 0.06最后一位是车牌高度占比常见竖直方向占比约6%。这个归一化设计对车牌检测非常重要。因为现实场景里图片来源很杂有的来自高位相机俯拍车牌在画面下半部分有的来自停车场道闸车牌占了大半个画面。不同分辨率、不同宽高比的图混在一起训练归一化坐标可以直接喂给网络不需要每张图做等比例缩放预处理——YOLO训练框架会在imgsz参数指定的尺寸下自动做 letterbox 填充而标注坐标依然成立。拿到这个数据集后我建议先确认三样东西图片后缀是.jpg还是.png还是混合是否带classes.txt或data.yaml类别文件标签文件是否和图片严格同名。这三个问题几乎决定了后续脚本怎么写。2.2 五秒跑完的标注质量检查越界、空文件与类别统计脚本1695张图听起来不多但人工去翻每张图的标注框既费眼睛又不靠谱。我一般会在动手训练前先跑一遍标注质量检查脚本把几类硬伤一次性排掉标签行字段数不是5、坐标值越界、框宽度或高度为0、类别ID超出类别文件定义。# check_yolo_labels.py # 检查数据集里所有 YOLO 标注文件的基本合法性 import os from pathlib import Path def check_label_file(txt_path: Path): issues [] with open(txt_path, r, encodingutf-8) as f: lines [ln.strip() for ln in f if ln.strip()] if not lines: return [empty_file] # 空标签可作为背景负样本但建议单独统计 for ln in lines: parts ln.split() if len(parts) ! 5: issues.append(ffield_count:{len(parts)}) continue try: cid int(float(parts[0])) xc, yc, bw, bh map(float, parts[1:]) except ValueError: issues.append(non_numeric) continue if bw 0 or bh 0: issues.append(zero_size) # 归一化坐标下框的边界必须落在 [0, 1] 区间内 if xc - bw / 2 0 or xc bw / 2 1: issues.append(x_out_of_bound) if yc - bh / 2 0 or yc bh / 2 1: issues.append(y_out_of_bound) return issues dataset_root Path(dataset) for split in [train, val]: label_dir dataset_root / split / labels if not label_dir.exists(): print(f{split}/labels 目录不存在) continue for txt in label_dir.glob(*.txt): if txt.name classes.txt: continue isss check_label_file(txt) if isss: print(f{txt}: {isss})这段代码的逻辑很直接先过滤空行再做字段数和数值解析的校验。其中box_out_of_bound的检查尤其值得看——坐标越界的框训进去轻则让损失函数出现明显抖动重则直接让训练进程在某个 epoch 崩溃。empty_file不算非法它代表一张没有目标的背景图YOLO训练会把它当负样本用但这类文件的数量需要心里有数。跑完这个脚本如果全是干净输出再去做类别统计看看类别ID分布是否和预期一致。车牌检测一般只有一个类别类别ID应为0。如果出现ID等于1或更大先别急着怀疑数据大概率是你把YOLO格式和COCO的80类约定弄混了或者数据集里混入了其他目标类别。2.3 按场景划分而不是随机划分训练集与验证集的正确比例切割数据集时很多人的第一反应是拿train_test_split随机分一下。对一般分类任务这没问题但对车牌检测这种场景关联很强的任务随机划分可能埋雷同一辆车在停车场不同角度拍了六七张随机划分会让同一辆车同时出现在训练集和验证集验证指标虚高得让你误以为模型已经通杀全场。我常用做法是按图片文件名里的场景信息分组后划分。如果这个数据集的文件名带了拍摄地点或时间戳字段多数爬取或采集的数据都会带先按这个前缀分组再按组划分是最稳的。如果文件名没有规律退而求其次做随机划分也可以但验证集比例最好拉到20%并且训练完后单独准备一批完全没见过的图片做抽测。# split_data.py # 按比例拆分数据集保证图像与同名标签一一对应 import random, shutil from pathlib import Path random.seed(42) # 固定随机种子让划分结果可复现 src_img_dir Path(images) src_lbl_dir Path(labels) val_ratio 0.15 train_img Path(train/images) train_lbl Path(train/labels) val_img Path(val/images) val_lbl Path(val/labels) for d in [train_img, train_lbl, val_img, val_lbl]: d.mkdir(parentsTrue, exist_okTrue) images list(src_img_dir.glob(*.jpg)) list(src_img_dir.glob(*.png)) random.shuffle(images) val_count int(len(images) * val_ratio) for i, img in enumerate(images): label src_lbl_dir / (img.stem .txt) if not label.exists(): print(f跳过无标签文件: {img.name}) continue dst_img_dir val_img if i val_count else train_img dst_lbl_dir val_lbl if i val_count else train_lbl shutil.copy2(img, dst_img_dir / img.name) shutil.copy2(label, dst_lbl_dir / label.name) print(ftrain: {len(list(train_img.glob(*)))} images) print(fval: {len(list(val_img.glob(*)))} images)注意这段脚本里我加了无标签文件的跳过逻辑。有些数据集里会存在少量只有图片没有标注的样本直接放进训练集YOLO会默认把它当空标签背景图如果你不希望背景图过多稀释正样本跳过是更稳的选择。1695张图按15%切验证集大约是250张上下这个规模做精度评估已经够用了。2.4 类别分布先看这一张图防止验证集变成“开卷考试”完成划分后我建议顺手统计一下训练集和验证集各自的目标框数量确认两个集合的分布基本一致。一个常见翻车场景是随机划分后验证集里恰好全是蓝底车牌训练集里却混着大量绿底新能源车牌结果验证分数高实际业务场景里绿牌一出现就漏检。一个更隐蔽的坑在空标签文件。如果这个数据集的制作者在采集时把一些没有车牌的背景图也放进来并保留了空txt你要决定这些背景图的用途。背景图可以作为负样本提升模型的误检抑制能力但比例太高会拖慢收敛。判断标准很简单空标签文件数量超过图片总数的10%建议单独抽出来不要全塞进训练集。3. 用YOLOv8跑通车牌检测从目录结构到训练命令的参数细节3.1 目录结构让YOLOv8读懂的images与labels排列YOLOv8的ultralytics框架对数据目录有约定规则图片放images目录标签放labels目录目录层级里train和val分开图片和标签的名字必须完全一致不包括后缀。这是最容易省事也最容易踩坑的一环。dataset/ ├── data.yaml ├── train/ │ ├── images/ │ │ ├── plate_001.jpg │ │ └── plate_002.jpg │ └── labels/ │ ├── plate_001.txt │ └── plate_002.txt └── val/ ├── images/ │ └── plate_100.jpg └── labels/ └── plate_100.txt如果你拿到的原始目录结构是images/和labels/平铺的那么第2章里的划分脚本已经帮你把结构规整成这个约定。这一步做完之后给data.yaml里的路径填对就可以直接开训了。框架会自动根据图像文件名去找同名txt不需要你显式指定标签路径。3.2 最小data.yaml配置:类别名、路径与验证集指向# plate.yaml # 路径建议写绝对路径避免在不同工作目录下启动训练时路径失效 path: /home/yourname/dataset train: train/images val: val/images names: 0: license-platepath是数据集的总根目录train和val是相对这个根目录的图片路径。names的类别顺序一定要和标签文件里的类别ID对齐。车牌检测一般只有一个类别也就是0。如果数据集自带的classes.txt里写的是别的名字你可以改成自己的但改了之后标签文件里ID不变一切以数字ID为准。补充一句有些老教程会让你在data.yaml里写nc: 1表示类别数量YOLOv8的写法已经改为只写names列表框架自动推导nc。如果你在网上搜到混合了两种写法的配置优先以官方最新版ultralytics文档为准。3.3 训练命令与四个调参优先级epochs、batch、imgsz与patience# 用预训练权重起步这是小数据集最安全的做法 yolo train modelyolov8n.pt dataplate.yaml epochs50 batch16 imgsz640 patience10这条命令是我在1695张小数据集上跑的第一版标配。逐项说modelyolov8n.pt是YOLOv8的nano版本预训练权重数据集小的时候从nano起步比从xlarge起步理智得多——大模型在这个数据量上过拟合几乎是必然的epochs50是基础迭代轮数后面根据曲线再增减batch16取决于显存8GB显存跑16没问题6GB以下建议降到8imgsz640是训练输入尺寸不做特殊处理就用默认。patience10是我特别想提醒的一个参数。它的含义是如果验证集指标连续10个epoch没有提升训练提前终止。很多人忽略这个小参数直接默认跑满全部轮数。在1695张这种量级的数据集上第30个epoch后过拟合几乎是必然趋势早停能帮你省下大量时间也能保住最好的权重。训练结束后看两个关键产物runs/detect/train/weights/best.pt和last.pt。best.pt是验证集上指标最优的权重last.pt是最后一个epoch的权重。部署时用best.pt这不用犹豫。# 推荐的进阶调法先跑一轮快速实验确认流程通再加轮数 yolo train modelyolov8s.pt dataplate.yaml epochs100 batch16 imgsz640 patience15nano跑通后如果精度不达标下一个升级方向不是直接换大模型而是把yolov8n.pt换成yolov8s.pt。s比n参数多一截但计算量还在可控范围适合在50轮内看到明显精度增益。如果s也提不动问题大概率不在模型容量而在数据质量或数据量这时候加图片比加算力有效得多。4. 车牌检测训练避坑指南五个让新手翻车的真实问题4.1 标签框越界导致损失变成NaN现象训练前几个batch一切正常突然某一步开始损失值变成nan后续所有epoch全部失效。原因归一化坐标越界的标签框在letterbox填充时被进一步推挤模型输出的预测框和这个越界目标之间的损失计算出现数值异常。尤其在图片本身就是长条形、宽高比极端的情况下越界的概率会被放大。解决跑第2章的检查脚本把x_out_of_bound和y_out_of_bound报出来的文件全部处理掉。处理方式有两种一是用图片的实际宽高把归一化坐标还原成像素坐标裁掉越界部分后重新归一化二是直接把这几张图从数据集里剔除——1695张图的场景下剔除十几张异常样本对整体影响极小。4.2 类别ID错位loss在降但输出全是错的现象训练时损失曲线漂亮验证集mAP也很高但拿去测试模型把背景当车牌框出来或者输出一堆无效区域。原因标签文件里的类别ID和data.yaml里names的顺序对不上。比如标签文件里写的是1而names里0: license-plate、1: background模型学到的就是把ID为1的东西当目标。很多人从VOC格式转YOLO时会漏掉ID减一操作这是血泪经验。解决训练前先用脚本统计所有标签文件中的最大类别ID再和data.yaml的names长度对比必须满足“最大ID小于等于names长度减一”。如果ID不连续建议重新映射成从0开始的连续ID。4.3 新能源绿牌漏检蓝牌数据一多其他颜色就变成稀有样本现象模型对蓝牌检测效果很好但对绿牌、黄牌、黑牌等特殊车牌漏检严重甚至完全框不出。原因公开数据集的采集渠道决定了蓝牌占比天然偏高。如果1695张图里绿牌不到100张检测器会学到“蓝色矩形字符纹理”这种隐性特征其他颜色车牌因为特征偏移直接被忽略。解决第一步单独统计各类别车牌的占比。第二步如果绿牌比例确实很低有两个可行的路收集少量绿牌图片补充数据或者用数据增强把蓝牌的色调在HSV空间向绿色偏移生成一批伪绿牌样本。增强的做法不完美但作为基线已经够用。下一步还是建议定向补数据这才是根治方案。4.4 数据增强开太猛车牌文字被当成噪点学掉了现象训练集损失一直在降验证集损失先降后升模型输出的框位置大致对但框内的文字区域模糊成一团。原因YOLOv8默认带马赛克增强小数据集上这种增强能提升泛化性但开启mosaic1.0且叠加了随机旋转、缩放后车牌上的字符可能被拉伸或旋转到无法辨认。模型学到的是“一个带边框的色块”而不是“字符纹理”自然对文字区域不敏感。解决把增强参数调保守一点例如在训练命令里加mosaic0.5 scale0.3 hsv_h0.015 hsv_s0.5 hsv_v0.3。这几个值是实际项目里常用的保守区间马赛克概率降一半缩放幅度收窄色相偏移只做微小扰动。如果发现字符恢复清晰再逐步加回去。增强不是越猛越好尤其在只有1695张图的时候它是在制造虚假多样性模型会在这些假样本上浪费容量。4.5 验证集里混进同一辆车mAP虚高上线就现形现象训练时验证mAP刷到0.9以上自己拿手机随便拍几张测试检出率不到一半。原因随机划分导致同一辆车在不同拍摄角度下的图片同时出现在训练集和验证集。检测器和分类器不一样它对场景背景、光线环境非常敏感。同车不同角度的图片背景相似、车身颜色一致模型可能记住的是这个车的整体外观而不是车牌本身。解决如果数据集文件名里带拍摄批次或车辆维度信息按这个字段分组后再划分确保同一辆车只出现在一个集合里。没有这个信息的话把验证集比例从15%提到20%并且训练完后再准备一个完全独立的场景做最终抽测。mAP是相对参考独立测试才是你真实业务场景的体检报告。5. 让1695张图发挥更大价值迁移学习、冻结层与真实图片验证5.1 用预训练权重起步这是小数据集最便宜的一次性能提升前面训练命令里我直接用了yolov8n.pt而不是随机初始化权重这在1695张的数据量上不是可选项而是必选项。预训练权重已经在COCO这样的大规模数据集上见过大量通用物体特征车牌虽然不在COCO的80类里但边缘、纹理、色彩这些底层特征是可以迁移的。你只需要让模型在车牌数据上微调“什么是车牌”而不是从零学习“什么是图像特征”。还有一个更细的技巧用freeze10把前10层网络冻结住前10层学到的是通用边缘和颜色特征在小数据集上改动它们反而容易破坏已有表征能力。# 冻结前10层做迁移学习适合数据量小且场景单一的情况 yolo train modelyolov8n.pt dataplate.yaml epochs50 batch16 freeze105.2 真实场景复测照片、视频帧与连续帧漏检观察所有权重最终都要过这关离开数据集拿到真实拍摄场景里去跑一遍。把手机里的停车场照片、行车记录仪截帧、甚至一段短视频丢进去推理比盯着mAP数字有用得多。# 用训练得到的最优权重对图片和视频做推理 yolo predict modelruns/detect/train/weights/best.pt source./test_images/ yolo predict modelruns/detect/train/weights/best.pt source./test_video.mp4我特别建议看连续视频帧而不是只看单张静图。车牌检测部署到道闸或监控场景时模型面对的是连续流单帧检出率90%不等于每辆过车都能被稳定识别。如果视频里同一辆车连续10帧中有3帧框丢了说明模型对运动模糊和角度变化仍然敏感。这是小数据集训练后最容易暴露的短板也是推动你下一步补充数据的最直观证据。我最早用这类小数据集训练时犯过最蠢的错误是直接默认参数跑满300轮回头一看过拟合得一塌糊涂。后来养成一个习惯1695张图先50轮快速验证流程再决定要不要加数据、调增强、换大模型。希望帮到你。本文还有配套的精品资源点击获取