尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

黄牌车牌检测数据集JSON标签解析与YOLO格式转换实战

黄牌车牌检测数据集JSON标签解析与YOLO格式转换实战 简介面向车牌识别与检测任务的数据集内容为已标注的黄牌车辆图像及配套标签文件覆盖不同角度的实际拍摄场景适合计算机视觉方向的研究者、算法工程师和高校学生用于模型训练、效果评估与调优。压缩包共含九千三百二十四个文件大小约两百五十七兆字节其中标签文件与图像文件基本对半图像以JPG为主另有少量JPEG标注采用多边形目标框四个顶点分别落在车牌四角能够贴合倾斜、透视等不同角度。目前已有八百八十人浏览学习。数据经过人工逐张筛选剔除模糊图片并处理有歧义的区域可直接用于车牌检测与字符识别减少清洗和标注时间如果后续需要矩形框或其他标签格式还可联系作者转换使用上更加灵活。整体目录结构清晰便于自行划分训练集和验证集是开展车牌识别实验或项目开发时较为实用的数据资源。 做车牌检测的朋友应该都有过这种感觉公开能下载的车牌数据集一搜一大把但真正拿来训练 YOLOv8、能跑出像样 mAP 的十个里能挑出两个就算不错。尤其是“黄牌”这种特定车型车牌和满大街的蓝牌比起来公开资源少得可怜。我去年接了一个大型车辆出入口识别的小项目甲方要求对黄牌货车、挂车做到全天候检测找了一圈现成数据集要么只有几百张图要么标注格式完全是自造的最后干脆基于“车牌检测数据集黄牌 json 标签”这套结构自己整理和改造了一版。今天就把这套数据集的整理思路、json 标签的解析方式、转成 YOLO 训练格式的完整流程以及我踩过的那些坑一次性说清楚。这套事情适合谁如果你正准备训练自己的车牌检测模型或者已经从某个渠道下载到了车牌图片和 json 标签但不知道如何下手处理那么这篇文章基本能帮你省掉一周的试错时间。整篇文章不会讲太多虚的主要围绕“黄牌数据集长什么样、json 怎么解析、怎么转 YOLO 格式、训练前要检查什么”这几件事展开。1. 数据集构成与黄牌检测的独特难点1.1 黄牌和蓝牌的检测差异黄牌在国内主要挂在大中型货车、客车、挂车上和蓝牌小汽车相比有几个直接影响检测模型训练的特点。首先是车牌悬挂位置高而且车身平面大摄像头从低处拍过去会产生明显的仰角透视车牌在画面里经常是一个倾斜的梯形而不是一个规整的矩形。其次是黄底黑字的颜色对比度在夜间偏低尤其是黄色在钠灯路灯下的色偏非常严重有时候拍出来接近橙色甚至土黄色。第三个问题是污损率极高货车常年跑长途车牌被泥浆、灰尘遮挡是常态而且挂车尾部的车牌经常被货箱挡掉一半。这些特点决定了我们不能直接拿蓝牌数据集的权重去微调而是需要一批有代表性的黄牌样本。以我手头这套数据为例图片分辨率以 1080p 为主来源覆盖了白天、傍晚、夜间、逆光、雨雾天每张图包含 0 到 3 个车牌目标不等。标注时我没有采用字符级标注而是统一采用车牌外接框检测——也就是每个目标只标一个四边形或者矩形框标签统一叫 plate。1.2 下载到的数据往往长什么样如果你是从网上下载到这套“车牌检测数据集黄牌 json 标签”大概率会看到这样的目录结构plate_dataset/ ├── images/ │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... ├── labels/ │ ├── 000001.json │ ├── 000002.json │ └── ... └── README.txtimages 目录放原图labels 目录放和图片同名的 json 文件这种组织方式对接 LabelMe 或者 CVAT 导出格式很常见。我在实际处理中遇到过两种 json 结构一种是 LabelMe 风格每个 json 里有一个 shapes 数组包含了标注点坐标和标签名另一种是 COCO 风格把多张图的所有标注汇总到一个大 json 里。下面这套整理方案以 LabelMe 风格为主因为单张图一个 json 在排查问题时最方便标注错了改一个文件就行不用动整个数据库。COCO 这种大 json 如果抽其中一部分做训练集还得额外写过滤脚本做数据清洗时真的很费事。2. JSON 标签结构解析与关键字段2.1 LabelMe 风格 JSON 长什么样拿我整理后的一个典型 json 文件来说核心结构是这样{ version: 5.2.1, flags: {}, shapes: [ { label: plate, points: [[856, 512], [1074, 584]], group_id: null, shape_type: rectangle, flags: {} } ], imagePath: 000001.jpg, imageData: null, imageHeight: 1080, imageWidth: 1920, imageDepth: 3 }很多新手拿到 json 后第一反应是去读 imageData 字段因为里面看起来有很长一串内容。但请注意LabelMe 导出的 json 里 imageData 经常是 null因为图片是单独存放的base64 编码只在某些标注工具的嵌入式快照模式里才会出现。如果你的 json 里 imageData 不为空而且很大说明这个 json 自带了图片数据转换时可以直接从它里面解码出图不一定非要到 images 目录去找同名文件。shapes 数组里每个元素对应一个标注目标。label 是类别名points 是一个二维数组存放多边形顶点坐标。shape_type 如果是 rectanglepoints 里只有两个点分别是矩形的左上角和右下角如果 shape_type 是 polygonpoints 里会有四个甚至更多点这种情况常见于标注人员用多边形精细框出车牌外轮廓。两种类型在转换时处理逻辑不一样我后面会专门说。2.2 黄牌目标在 json 中的常见标注问题整理这套数据集的过程中我发现黄牌目标在 json 里最容易出现的标注问题有两个。第一个是坐标越界标注时不小心把矩形框拉到了图片外面导致 points 里的 x 或 y 大于 imageWidth 或 imageHeight。这个问题在画面边缘的车牌上特别容易出现尤其是车牌只剩一半在画面内时标注人员往往习惯性把框拉出去。第二个问题是全天候样本的标签一致性同一个车牌在白天是清晰的矩形框在夜间被灯光打亮后可能变成了多边形因为标注人员想更贴合高光边界。但如果训练时目标格式不统一模型学到的框形状就会乱。我最后采取的做法是全量统一所有 shape_type 都转换成 rectangle也就是不管 polygon 还是 rectangle最终都取最小外接矩形。有人可能会担心这样丢失精度但从检测任务的角度来说车辆出入口、停车场这些场景只需要定位车牌大概位置矩形框完全够用而且和 YOLO 系的 anchor 机制契合度更高。如果你的后续任务需要做车牌矫正、字符识别那建议保留四角点标注那是另一套处理方案。3. JSON 标签转 YOLO TXT 格式的完整脚本3.1 为什么必须转换YOLOv5 和 YOLOv8 训练时标签文件不是 json而是每个图片对应一个同名的 txt 文件每行代表一个目标格式为class_id x_center y_center width height注意这里的 x_center、y_center、width、height 都是相对于图片宽度和高度的归一化数值值域在 0 到 1 之间。归一化的原因是模型输入尺寸会缩放不同分辨率的图片如果直接使用绝对坐标训练时就乱了套。这是目标检测训练里的基础操作但也正因如此很多人会忽略一个小问题——json 里存的是左上角 xy 和右下角 xy转换时需要先算出中心点和宽高再分别除以 imageWidth 和 imageHeight。顺序错了框就全偏了。3.2 转换脚本与参数选择过程我的转换脚本用 Python 写核心逻辑分三步遍历 labels 目录下所有 json 文件解析出每个目标框写成同名 txt 并放到指定输出目录。先放完整代码import json import os import glob def convert_labelme_json_to_yolo(json_path, output_dir, class_namesNone): if class_names is None: class_names {plate: 0} with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data.get(imageWidth, 0) img_h data.get(imageHeight, 0) image_path data.get(imagePath, ) if not img_w or not img_h: print(fWarning: missing imageSize in {json_path}) return base_name os.path.splitext(os.path.basename(image_path))[0] if not base_name: base_name os.path.splitext(os.path.basename(json_path))[0] txt_path os.path.join(output_dir, base_name .txt) lines [] for shape in data.get(shapes, []): label shape.get(label, ) if label not in class_names: continue shape_type shape.get(shape_type, rectangle) points shape.get(points, []) if shape_type rectangle and len(points) 2: x1, y1 points[0] x2, y2 points[1] else: xs [p[0] for p in points] ys [p[1] for p in points] x1, y1, x2, y2 min(xs), min(ys), max(xs), max(ys) # 坐标越界裁剪 x1 max(0, min(x1, img_w)) y1 max(0, min(y1, img_h)) x2 max(0, min(x2, img_w)) y2 max(0, min(y2, img_h)) if x2 x1 or y2 y1: continue x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h width (x2 - x1) / img_w height (y2 - y1) / img_h lines.append(f{class_names[label]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) def batch_convert(json_dir, output_dir): os.makedirs(output_dir, exist_okTrue) json_files glob.glob(os.path.join(json_dir, *.json)) for jf in json_files: convert_labelme_json_to_yolo(jf, output_dir) if __name__ __main__: batch_convert(labels, yolo_labels)这段代码里我特意加了几个容易被忽略的处理。第一是 polygon 取最小外接矩形时用 min/max 遍历所有顶点而不是只取前两个点。第二是坐标越界裁剪把超出图片范围的数值强行拉回边界内这一步如果不做训练时 YOLO 会直接报错或者 loss 出现 nan。第三是过滤掉 x2x1 或 y2y1 的无效框这些框通常是在标注时手滑创建了两点重合的目标。3.3 做一个可视化检查脚本转换完成后我强烈建议不要急着开训练先画一遍框看看转换结果对不对。我自己写了一个极简的可视化脚本用 OpenCV 读 txt 标签在图上把矩形画出来然后一张一张肉眼扫过去。这一步能发现大量机械化转换发现不了的问题比如标注框是不是比实际车牌大了一圈、夜间反光把框撑大了、某些图片的标签和文件名对不上等等。import cv2 import os def draw_yolo_labels(image_dir, label_dir, output_dir, class_namesNone): if class_names is None: class_names {0: plate} os.makedirs(output_dir, exist_okTrue) for img_name in os.listdir(image_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue img_path os.path.join(image_dir, img_name) base os.path.splitext(img_name)[0] txt_path os.path.join(label_dir, base .txt) img cv2.imread(img_path) h, w img.shape[:2] if os.path.exists(txt_path): with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) x_center float(parts[1]) * w y_center float(parts[2]) * h box_w float(parts[3]) * w box_h float(parts[4]) * h x1 int(x_center - box_w / 2) y1 int(y_center - box_h / 2) x2 int(x_center box_w / 2) y2 int(y_center box_h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, class_names.get(cls_id, str(cls_id)), (x1, max(0, y1 - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) out_path os.path.join(output_dir, img_name) cv2.imwrite(out_path, img) print(fprocessed {img_name})跑完可视化重点看三类图车牌位于画面边缘的图、夜间图、雨雾天图。边缘图看框是否被裁剪得不完整夜间图看反光区域是否导致框比实际车牌大了很多雨雾天图看标注是否漏检。一套数据如果这三类图都经得住看那训练起来心里就有底了。4. 训练前的数据划分与黄牌增强策略4.1 划分训练集和验证集时的坑YOLO 训练要求的数据划分很简单但偏偏是这里最容易出问题。很多人脚本写完直接按 9:1 随机切分结果训练集和验证集里出现了同一辆车的图片。对于车牌检测来说同一辆车的不同帧画面极度相似如果同时出现在训练集和验证集里验证集精度看起来会虚高一上线就现出原形。我在划分时采用的策略是先按图片的文件名前缀分组同一个采集批次或者同一辆车的前后帧尽量归到同一边。实际操作中是先把所有图片按拍摄时间排序再每隔 10 帧抽一帧进验证集这样能最大程度避免数据泄漏。黄牌这块尤其严重因为货车经常停在路边不动连续几分钟拍出来的几十帧几乎一模一样你不做去重或者分组切分训练集和验证集相似度极高mAP 能虚高五六个点。4.2 黄牌场景的增强策略数据增强上对黄牌数据不能照搬通用目标检测那套路数。YOLOv8 自带的 mosaic 和 mixup 对提升鲁棒性确实有帮助但对车牌字符型目标来说过度的几何扭曲会让字符变形到失真的程度。我训练时把 hsv_h、hsv_s、hsv_v 这三个颜色增强参数调高了因为黄牌在不同色温光源下的色偏本来就大模型必须见过足够多不同色调的黄才能泛化但 translate 和 shear 我调得比较保守因为车牌是平面物体现实中不太可能出现夸张的斜切视角。一个经验值供参考我最终用的配置是 hsv_h0.02、hsv_s0.6、hsv_v0.5translate0.1scale0.4mosaic1.0大家可以根据自己数据的光照分布再微调。还有一个值得单独说的点就是不要把“蓝牌”归类为背景。有些项目里不需要检测蓝牌但数据集里混入了大量蓝牌照片如果不加任何处理蓝牌会被当成困难负样本导致模型对蓝色区域的响应被抑制。但蓝牌和黄牌在颜色特征上差异明显蓝牌样本过多会让模型倾向忽略饱和度高的小目标。我处理时把蓝牌图片单独过滤出来按适当比例保留一部分作为负样本既不会让模型看到蓝牌就“懵”也不会因为负样本太多而压低了黄牌的召回率。4.3 训练参数和超参的一点经验当你把 json 标签转成 txt、划分好数据集之后训练命令基本是标准的 YOLOv8 流程。我自己用的命令是这样的yolo detect train datadataset.yaml modelyolov8n.pt epochs150 imgsz640 batch16 device0数据集不大时从头训练很容易欠拟合我建议用 yolov8n.pt 或者 yolov8s.pt 做迁移学习。哪怕预训练权重里没有车牌类别但模型底层的纹理、边缘、颜色特征提取能力已经很强了微调一下就能收敛。我手头这套黄牌数据总共只有 3200 多张图用 yolov8s 从头训的话 mAP0.5 能到 0.91 左右从 COCO 预训练权重迁移上来能到 0.96差别还是很明显的。另外epoch 数不是越多越好我跑过 300 轮的实验发现 150 轮以后 val loss 基本不再下降反而有轻微过拟合的趋势早停法在这个场景下很管用。5. 常见问题与排查技巧实录这一部分整理一下我在处理这套“车牌检测数据集黄牌 json 标签”时实际遇到的问题基本都能对应到你们可能踩到的坑。问题现象可能原因解决办法转换后 txt 全是空文件json 里 shapes 数组中 label 不叫 plate可能叫 license_plate 或 0检查 README 或打开一个 json 确认 label 值在 class_names 里做好映射训练报错“image not found”json 里的 imagePath 不含扩展名或扩展名大小写不一致转换时使用 json 文件名作为基准而不是 imagePath 字段框整体偏左上/右下json 坐标使用中心点格式而脚本按左上右下解析对照标注工具导出格式确认 points 语义不知道就打印一个 json 自己判断夜间图片车牌漏检严重增强参数里 hsv 变换不够或训练集夜间样本占比过低从原数据中补充夜间帧提高 hsv_s、hsv_v 的随机范围验证集 mAP 很高但实际效果差训练验证拆分时有同源数据泄漏按时间或拍摄场景分组切分增强样本多样性训练 loss 先降后升、振荡学习率过大或 batch size 太小把初始 lr 降到 0.001 以下batch size 提到 16 以上一个 json 里出现了两个重叠框标注时重复标了同一块车牌可视化检查时发现重叠框手动删掉后重新生成 txt除了表里的问题还想补充一点标注质量层面的体会。数据传输过程中偶尔会遇到 json 文件损坏最常见的是编码格式问题——Windows 下标注工具导出的 json 用 GBK 编码保存了中文字段导致 Python 读入时报 UnicodeDecodeError。我转换脚本里加了 encodingutf-8 参数但如果你在 Windows 上拿到数据可能需要试一下 gbk 编码或者用 chardet 自动检测否则程序就会中断在第一个文件上后面基本没法继续跑。再有一个知识点容易被忽略就是转换前最好先跑一下数据完整性检查。我把检查规则写得很简单遍历所有标注过的图片确认每张 json 都存在且能被 json.load 正确解析再遍历所有 json确认对应的图片文件存在。两轮遍历下来基本上能过滤掉 90% 的脏数据。这个检查往往只花几分钟但能为后续训练节省好几个小时的排查时间强烈建议大家在开始转换前先做这一步。6. 从数据集到模型落地的整体流程现在把整套流程串起来看你会发现“车牌检测数据集黄牌 json 标签”这个题目听起来是个数据集的下载和使用问题但真正落地时涉及的工作链条其实不短。第一步是确认数据规模和标注类型第二步是解析 json、检查脏数据第三步是转换成 YOLO txt 格式并做可视化验证第四步是合理划分训练集和验证集第五步才是开始训练调参。每一步之间都有依赖关系前一步没做好后面跑出来的结果都会受到影响。以我个人的经验来说这套黄牌数据集在做完上述处理后用 YOLOv8s 训练出来的模型在实际出入口场景的表现相当稳定白天检测基本一台设备能做到实时流畅夜间配合补光灯也能维持不错的召回率。而且因为我有意加入了大量极端角度和污损样本模型对车牌缺角、泥浆覆盖这些情况的容忍度明显好于之前用蓝牌数据微调的版本这说明样本选择和标注质量在目标检测项目里的重要性其实比模型结构的选择还要高。最后再分享一个小技巧如果你后续计划把检测结果接进车牌识别流程建议你在检测阶段就保留置信度输出和坐标信息不要只保存一个类别标签。我实际开发中经常遇到检测框每帧抖动十几像素的情况最后在输出端做了一次 IoU 跟踪匹配用连续三帧的检测结果取中值来平滑坐标识别准确率提升非常明显。这个经验后续有机会再展开但有一点可以确定检测这步做扎实了后面一切都顺。本文还有配套的精品资源点击获取
返回列表