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

资讯详情

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

COCO车牌数据集解析与YOLOv8训练实战

COCO车牌数据集解析与YOLOv8训练实战 简介面向计算机视觉与智能交通应用场景的中国汽车车牌识别数据集涵盖8493张真实环境下的车牌图片并支持COCO格式标准标注可帮助开发者训练车牌检测与字符识别模型应对光照变化、角度倾斜、遮挡等复杂条件。数据集已按COCO格式做好标注可直接接入MMDetection、Detectron2等主流框架配套的JSON标注文件结构清晰TXT文件提供补充说明压缩包共2000个文件、约348MB含1995张JPG原图、3个JSON标注文件与2个TXT说明文件。目前已有629人学习下载适合目标检测、OCR识别、安防监控与自动驾驶领域的入门及进阶研究者。借助高覆盖场景样本和较高识别水准使用者可大幅节省数据采集与标注时间快速验证算法性能并部署到实际车牌识别系统中。1. 这份8493张的中国车牌数据集先别急着调参做停车场道闸或者电子警察识别项目最卡进度的不是模型结构而是数据质量和标注格式。这份标注为COCO格式的中国车牌数据集一共8493张图片样本覆盖了倾斜、模糊、夜间、远距和不同光照下的车牌从文件命名规律看它是从CCPD中国城市停车场数据集的一批子图经过Roboflow清洗和重新导出得到的。99.4%这个识别率听起来很漂亮但先别急着拿它当结果——要弄清楚这是检测框命中率、车牌分类正确率还是端到端字符识别率三者评价体系完全不同。我用pycocotools把标注加载出来做了两轮体检下面从标注结构、可视化、训练参数到复现口径把这条链路完整拆一遍。2. 从文件名到COCO拆解车牌标注的数据结构这份数据的COCO标注文件是标准的annotationsJSON包含images、annotations、categories三个数组。训练前要确认的第一件事标注框是贴着车牌四边还是包含车身的牌照区域。我抽查了几张图框都紧贴车牌边缘四点的顺序并不一致这对转换到YOLO的x_center y_center w h格式没有影响但如果你要做透视矫正就需要按角点重新排序。下面从文件名规则和COCO字段两方面拆。2.1 文件名里的字段先看懂再动手Roboflow导出的文件名保留了一部分原始标注信息例如0049-4_4-223-526_313-572-313-572_223-565_223-526_313-533-0_0_15_25_24_24_31-40-10_jpg.rf.ad6e521a8add2c9051de9ac657cd34c.jpg可以看到三段信息开头0049-4_4是图片编号和疑似倾斜角中间223-526_313-572-313-572_223-565_223-526_313-533是车牌四边形的坐标序列末尾0_0_15_25_24_24_31-40-10疑似是车牌字符类别和边界宽高但经过Roboflow的哈希重命名后这些字段的语义已经不再可靠。因此我只把文件名用作快速筛查比如统计图片数量、检查是否重复真正的标注信息以COCO JSON为准。这里有个容易踩的坑.rf.后面那串哈希是Roboflow重新生成的不代表原始图片ID如果你想对回CCPD原图需要靠自己维护文件名映射表。2.2 用Python拆解文件名做快速校验下载完数据后第一步不是训练而是确认压缩包是否完整、文件名是否乱码。写一个解析脚本把所有文件名拆成数字段检查坐标是否越界import re from pathlib import Path img_dir Path(car_plate_dataset/images) for img_path in img_dir.glob(*.jpg): fname img_path.stem.split(.rf.)[0] # 去掉roboflow后缀 parts re.split(r[-_], fname) nums [int(p) for p in parts if p.isdigit()] # 简单检查文件名里的数字是否都在合理范围 if max(nums) 2000: print(f{img_path.name}: 坐标值异常 {nums})这段代码先把.rf.后面的哈希去掉再按-和_切割把所有数字段提取出来。如果某张图的最大数字超过2000说明坐标可能不在这张图的分辨率范围内需要人工复核。注意Roboflow有时会保留原始分辨率的标注但导出时会把图片缩放此时边界框坐标是原始像素还是缩放后像素看COCO images里的width/height和bbox是否匹配即可。这段脚本不能证明标注正确但能快速暴露“图片和标注来自不同分辨率”这类低级错误。2.3 COCO JSON的字段与(\texttt{bbox})单位标注文件本体一般长这样{ categories: [{id: 1, name: plate}], images: [ {id: 0, file_name: 0.jpg, width: 1280, height: 720} ], annotations: [ { id: 0, image_id: 0, category_id: 1, bbox: [425, 310, 210, 120], segmentation: [[425,310, 635,310, 635,430, 425,430]], area: 25200, iscrowd: 0 } ] }bbox是[x, y, width, height]原点在左上角单位是像素segmentation是多边形坐标列表这里和bbox重合是因为矩形车牌做了四点标注。area对检测模型没有直接影响但pycocotools的评估会用它做按大小分组的指标所以不能乱填。COCO官方建议area是多边形的实际面积矩形下就是w*h两者相等。字段作用可以参考下表字段类型作用bbox4个float目标框位置和尺寸像素值非归一化segmentationlist of list多边形标注检测训练时通常不使用iscrowd0/1是否密集群体目标车牌数据集中全为0image_idint关联images数组areafloat目标面积评估按面积分组时使用注意categories里只有plate一个类说明这是单类检测任务。如果后面你想扩展到新能源绿牌、黄牌挂车需要自己合并多个数据集并重新分配category_id否则names字典和标签索引会错位。3. 用pycocotools加载COCO标注先做一轮画框体检数据集的标注不能只信README。很多下载的数据在传输过程中会出现标注错位、类别ID被重置甚至XML转COCO时坐标偏移半个像素。我习惯先加载一遍随机画框再做统计。3.1 读取标注并验证ID一致性from pycocotools.coco import COCO ann_file annotations/instances_train.json coco COCO(ann_file) cat_ids coco.getCatIds() print(类别ID:, cat_ids, [c[name] for c in coco.loadCats(cat_ids)]) img_ids coco.getImgIds() print(图片总数:, len(img_ids)) ann_ids coco.getAnnIds(imgIdsimg_ids[:1]) anns coco.loadAnns(ann_ids) print(第一张图的标注数:, len(anns), bbox:, anns[0][bbox])COCO构造会检查JSON语法和关联字段如果报错通常是categories的ID不连续或者image_id指向不存在的图片。注意getAnnIds(imgIdsimg_ids[:1])只能取前一张图的标注如果返回空列表说明图片和标注没关联上需要检查image_id的偏移量——有些转换工具把图片ID从1开始有些从0开始。这里补一个边界检查len(coco.anns)应等于标注总数coco.imgToAnns里的键集合应等于coco.imgs的键集合不一致就说明存在孤立标注。3.2 画框检查边界是否贴合车牌边缘可视化代码import cv2 from pathlib import Path from pycocotools import mask as maskUtils img_dir Path(car_plate_dataset/images) def draw_bbox(img_path, bbox, color(0, 255, 0), thickness2): img cv2.imread(str(img_path)) x, y, w, h [int(v) for v in bbox] cv2.rectangle(img, (x, y), (x w, y h), color, thickness) return img # 随机抽9张图拼成3x3的画框网格 import random random.seed(0) sample_ids random.sample(img_ids, 9) tiles [] for img_id in sample_ids: img_info coco.loadImgs(img_id)[0] anns coco.loadAnns(coco.getAnnIds(imgIdsimg_id)) img cv2.imread(str(img_dir / img_info[file_name])) for ann in anns: x, y, w, h [int(v) for v in ann[bbox]] cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), 2) tiles.append(img) # 简单横向拼接 grid cv2.hconcat(tiles[:3]) cv2.imwrite(check_grid.jpg, grid)这里bbox的w和h可能因为标注工具不同而带小数直接int()截断会导致框宽少1像素影响不大但会在计算IoU时产生微小偏差最好用round()。我见过最典型的问题是框没有贴住车牌而是框了一整个车头说明标注坐标经过了缩放但images里的宽高没更新或者标注工具默认采用了外接矩形而不是紧致框。画框网格不需要全部检查抽9张就能暴露80%的格式问题。3.3 按尺寸和宽高比做统计体检用标注数据统计目标尺寸import numpy as np sizes [] for ann in coco.anns.values(): x, y, w, h ann[bbox] sizes.append((w, h)) sizes np.array(sizes) print(平均宽高:, sizes.mean(axis0)) print(宽高比中位数:, np.median(sizes[:, 0] / sizes[:, 1])) print(小目标(32px):, (sizes.max(axis1) 32).sum())中国车牌标准宽高比约为3:1到4:1如果平均值偏差过大说明存在非车牌的标注目标或者标注框变形。这里的“小目标”按COCO定义宽度和高度的最大值小于32像素。8493张图里如果小目标占比太高训练时imgsz必须大于等于640否则车牌会被压成一条线。更细致的做法是统计每个image_id的标注数量正常情况每张图只有1个车牌如果出现大量单图多框要么是数据本身有多车牌场景要么是标注重复。3.4 体检清单做完以上步骤我会记录下表作为数据集是否可用的依据检查项通过标准图片数与COCO JSON一致偏差小于0.1%所有bbox在图片尺寸内无越界框车牌宽高比在2.5:1到4.5:1之间异常比例低于1%随机9张画框贴合车牌无整体偏移category_id只有1类无多类混入如果全部通过才进入训练环节。这个体检过程5分钟能跑完但能省掉后面排查精度低的半天时间。4. YOLOv8微调从COCO格式到车牌检测模型的关键参数COCO格式是中间态训练时我更习惯转成YOLO的txt格式因为后续换框架、做剪枝、部署到TensorRT都方便。这一章覆盖转换脚本、目录结构和训练参数。4.1 把COCO JSON转成YOLO txtYOLO格式要求每个图片一个txt每行是class_id x_center y_center width height坐标归一化到0-1。import json from pathlib import Path with open(annotations/instances_train.json) as f: coco_data json.load(f) out_dir Path(labels/train) out_dir.mkdir(parentsTrue, exist_okTrue) img_id_to_name {img[id]: img[file_name] for img in coco_data[images]} # 按image_id聚合annotations anns_by_img {} for ann in coco_data[annotations]: anns_by_img.setdefault(ann[image_id], []).append(ann) for img_id, anns in anns_by_img.items(): img_info next(img for img in coco_data[images] if img[id] img_id) w, h img_info[width], img_info[height] lines [] for ann in anns: x, y, bw, bh ann[bbox] cx (x bw / 2) / w cy (y bh / 2) / h lines.append(f0 {cx:.6f} {cy:.6f} {bw/w:.6f} {bh/h:.6f}) txt_name Path(img_info[file_name]).stem .txt (out_dir / txt_name).write_text(\n.join(lines))这段脚本把COCO的bbox换算成归一化中心点坐标。注意bw/w和bh/h如果大于1说明标注框超出了图片边界常见原因是Roboflow在缩放图片时没有同步更新标注或者原始图片带了黑边。碰到这种情况建议先用np.clip把坐标截断到0-1否则YOLOv8训练时会产生NaN损失。转换后最好加一个校验扫描所有txt确认每行都是5个浮点数且类别ID最大值为0。4.2 目录结构与(\texttt{data.yaml})配置YOLOv8需要以下目录结构dataset/ images/train/ images/val/ labels/train/ labels/val/ data.yamldata.yaml内容path: dataset train: images/train val: images/val names: 0: plate注意names字典的顺序必须和转换脚本里的class_id一致。如果names只有一项但labels/train里的txt出现1训练会直接报错invalid class index。更隐蔽的问题是val目录里如果缺图片训练不会报错但验证结果全是0所以先跑一次yolo detect val确认验证集可加载。4.3 训练参数选择与显存估算官方预训练权重适合直接迁移。训练命令yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ batch16 \ optimizerAdamW \ lr00.001 \ mosaic0.5关键参数说明参数推荐值说明modelyolov8n.pt预训练权重迁移学习能明显加快收敛imgsz640车牌属于中小目标超过640未必更多收益mosaic0.5增强效果好但车牌可能被切成碎片需要降权patience20早停轮数防止过拟合close_mosaic10最后10轮关闭mosaic减少标注噪声optimizerAdamW数据量中等时比SGD收敛更稳定显存不够时优先把batch从16降到8而不是降低imgsz因为车牌宽高比接近1:4imgsz太小会把车牌压成横条。训练中观察box_loss和cls_loss如果cls_loss在10轮内降到接近0可以提前停掉因为单类检测的cls_loss通常不会是大头重点看box_loss和dfl_loss。5. 复现99.4%识别率评估口径、常见坑与验证技巧最后这章讲指标口径和验证方法。99.4%这个数字必须拆开看否则你会在汇报时被问住。5.1 99.4%是检测率还是识别率官方摘要里的99.4%识别率大概率是端到端识别率也就是车牌字符全部识别正确的比例而不是目标检测的mAP。如果你的项目只需要检测车牌框那评估应该用mAP0.5如果要做识别需要再接OCR模块。常见做法是先用这篇的数据集训练YOLOv8检测器然后把检测框裁出来输入给车牌识别模型或CRNN最后统计整牌识别正确率。这里的“识别率”和“检测率”通常差2到3个百分点。5.2 三个影响复现的坑第一个坑是类别单一。这批数据是普通蓝底车牌没有新能源绿牌和挂车黄牌。迁移到真实停车场场景时绿牌比例可能占到20%单独用这份数据训练会明显掉点。第二个坑是样本分布不均衡模糊和倾斜样本占比不高。我在训练时把hsv_h、hsv_s的增强强度调低因为车牌颜色本身就是重要特征过度增强会让蓝底变绿底。第三个坑是评估指标用错COCO默认报告mAP0.5:0.95而业务上“框住车牌”通常只要求IoU0.5所以部署时直接看mAP50同时统计正检率。5.3 用TTA和交叉验证估算真实识别率低成本验证方法是把数据集按5折切分用YOLOv8的val模式跑每一折把每张图的检测和真实框做IoU匹配。TTA可以提升小目标召回yolo detect val \ modelruns/detect/train/weights/best.pt \ datadataset/data.yaml \ splittest \ imgsz640 \ augmentedTrueaugmentedTrue会做水平翻转和尺寸缩放推理通常能把小目标车牌的召回率提升1到2个百分点但推理耗时增加约30%。如果线上对延迟敏感建议只在夜间或远距场景开启白天用普通推理。最终汇报口径明确写清“检测率”还是“识别率”即可99.4%能不能复现取决于你是否用了同样的OCR后处理以及测试集是否包含多角度倾斜样本。本文还有配套的精品资源点击获取
返回列表