ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

可乐标志检测数据集实战:VOC与YOLO双格式解析与训练避坑指南

可乐标志检测数据集实战:VOC与YOLO双格式解析与训练避坑指南 简介这份资源是面向目标检测初学者与算法工程师的可口可乐、百事可乐标志检测数据集可用于训练和验证品牌Logo识别模型适用于零售货架分析、广告监测等场景。数据集共2223张jpg图片每张均配有对应的VOC格式xml标注与YOLO格式txt标注标注文件合计2223个采用labelImg工具按矩形框方式完成包含CocaCola与Pepsi两个类别框数分别为2268和2398总框数达4666个类别分布较为均衡。压缩包内文件总数约2000个以xml标注文件为主另含一份说明文档整体大小约120.84MB目录结构简洁便于直接接入YOLO或VOC训练流程。目前已有347人学习下载适合需要快速获取双类别Logo检测数据、验证模型效果或进行迁移学习实验的读者使用。1. 可乐标志检测数据集2223 张图、4666 个框VOC 与 YOLO 双格式到底怎么落地手里有一批货架、冰柜、自动售货机的实拍图想训练一个能认出可口可乐和百事可乐标志的检测模型最卡脖子的往往不是网络结构而是标注数据从哪来。这份数据集给的就是现成答案2223 张 jpg 图片每张都配了同名的 Pascal VOC 格式 xml 和 YOLO 格式 txt标注工具是 labelImg类别只有两个——CocaCola 和 Pepsi。总框数 4666其中 CocaCola 2268 个、Pepsi 2398 个两个类别基本均衡不存在某一类被压着打的情况。它适合谁做零售陈列稽核、竞品铺货统计、饮料品牌露出分析的算法同学以及想拿一个干净二分类检测任务练手 YOLO 全流程的人。下面按「先看清结构、再跑通训练、最后避坑」的顺序拆开讲。2. 拆开压缩包VOC 与 YOLO 双格式的目录结构与字段含义拿到资源第一步不是急着训练而是把目录结构和两种标注格式的字段含义对齐。很多人翻车就翻在这里xml 和 txt 混在一个文件夹里脚本一读就串了。2.1 目录组织与文件命名规律从说明文件看图片、xml、txt 是平铺存放的命名前缀统一为xyxr_cla_加数字序号比如xyxr_cla_9.xml、xyxr_cla_1485.xml、xyxr_cla_2223.xml。序号不连续是正常的中间有跳号说明原始采集时做过筛选。常见做法是先把三类文件按后缀分拣到images/、annotations_xml/、labels_txt/三个目录再建立同名索引。分拣脚本我一般这么写# 在数据集根目录执行按后缀分拣 mkdir -p images annotations_xml labels_txt mv *.jpg images/ 2/dev/null mv *.xml annotations_xml/ 2/dev/null mv *.txt labels_txt/ 2/dev/null # 校验三类文件数量是否一致 echo jpg: $(ls images | wc -l) xml: $(ls annotations_xml | wc -l) txt: $(ls labels_txt | wc -l)逻辑说明mv按后缀搬运2/dev/null屏蔽没有匹配文件时的报错。最后一行是血泪经验——三个数字必须都是 2223只要有一个对不上后面训练必然出现「找不到标签」或「图片无对应标注」的报错。参数上没什么可调的唯一要注意的是别在 Windows 资源管理器里手动拖拽容易漏文件用命令行或脚本更稳。2.2 VOC 的 xml 字段与 YOLO 的 txt 字段对照VOC 格式的 xml 是「自描述」的打开一个文件能看到filename、size、object等节点每个 object 里有name和bndbox四个坐标xmin、ymin、xmax、ymax坐标是绝对像素值。YOLO 的 txt 则是「裸数据」每行一个框格式是类别索引 cx cy w h全部归一化到 0~1。两者对照关系如下项目VOC xmlYOLO txt坐标形式绝对像素 xmin ymin xmax ymax归一化中心点宽高类别表示字符串 name整数索引0/1类别映射直接可读需 classes.txt 或 names 列表每图框数多个 object 节点多行文本这里有个关键点YOLO 的类别索引是 0 和 1但哪个是 CocaCola、哪个是 Pepsi取决于生成 txt 时的类别顺序。这份数据集的类别名是[CocaCola,Pepsi]按惯例 0 对应 CocaCola、1 对应 Pepsi但不要凭惯例一定要自己抽查一个 txt 文件结合对应图片确认。我一般会写个校验脚本把 txt 画回图片上看框的位置和类别对不对import cv2, os names [CocaCola, Pepsi] # 与数据集类别顺序保持一致 img_dir, lbl_dir images, labels_txt for lbl in os.listdir(lbl_dir)[:5]: # 抽查前5张 img cv2.imread(os.path.join(img_dir, lbl.replace(.txt, .jpg))) h, w img.shape[:2] with open(os.path.join(lbl_dir, lbl)) as f: for line in f: c, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w); y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w); y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, names[int(c)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(fcheck_{lbl}.jpg, img)逻辑说明读 txt 每行把归一化坐标还原成像素坐标画框并写上类别名。参数上names的顺序必须和生成 txt 时一致否则框的位置对但标签全错。跑完打开check_*.jpg肉眼扫一遍确认框贴着标志、类别名没串这一步花五分钟能省后面几小时的排查。3. 从 VOC 到 YOLO转换脚本、数据划分与训练配置虽然数据集已经给了 YOLO 格式的 txt但实际项目里你往往需要重新划分训练集/验证集或者把 VOC 重新转一遍以统一类别顺序。这一章把转换、划分、配置三件事串起来。3.1 用 Python 做 VOC 转 YOLO 并处理边界框越界即便已有 txt重转一遍的价值在于你可以控制类别顺序、过滤掉异常框、统一小数位。VOC 转 YOLO 的核心是把绝对坐标归一化公式是cx(xminxmax)/2/W、w(xmax-xmin)/Wy 方向同理。坑在于有些框的 xmax 会等于或略大于图片宽度归一化后 w 可能超过 1训练时会被判为非法。脚本里必须做裁剪import xml.etree.ElementTree as ET import os classes [CocaCola, Pepsi] def voc_to_yolo(xml_path, out_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 裁剪到图片范围内防止越界 xmin max(0, min(xmin, img_w)); xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)); ymax max(0, min(ymax, img_h)) if xmax xmin or ymax ymin: continue # 丢弃退化框 cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))逻辑说明classes决定索引映射必须和训练时的 names 一致。裁剪那两行是后悔药专门治越界框xmaxxmin的判断丢弃退化框避免出现宽高为 0 的脏数据。参数上小数位保留 6 位足够YOLO 读取时不在乎精度但统一格式方便 diff。图片宽高从 xml 的size节点读也行但更稳的是用 cv2 直接读图片防止 xml 里 size 写错。3.2 训练集验证集划分与 data.yaml 配置2223 张图按 8:2 划分训练集约 1778 张、验证集约 445 张。划分要保证两个类别在两边都有足够样本随机种子固定下来方便复现。常见做法是写个脚本按文件名列表切分然后生成 YOLO 需要的data.yamlimport os, random, shutil random.seed(42) imgs sorted(os.listdir(images)) random.shuffle(imgs) split int(len(imgs) * 0.8) for phase, subset in [(train, imgs[:split]), (val, imgs[split:])]: os.makedirs(fdataset/images/{phase}, exist_okTrue) os.makedirs(fdataset/labels/{phase}, exist_okTrue) for name in subset: shutil.copy(fimages/{name}, fdataset/images/{phase}/{name}) shutil.copy(flabels_txt/{name.replace(.jpg,.txt)}, fdataset/labels/{phase}/{name.replace(.jpg,.txt)})逻辑说明random.seed(42)固定划分结果换机器也能复现同一份切分。shutil.copy保留原文件方便反复实验。对应的data.yaml内容如下path: ./dataset train: images/train val: images/val nc: 2 names: [CocaCola, Pepsi]参数说明nc是类别数这里固定为 2names顺序必须和 txt 里的索引严格对应写反了模型会把可乐认成百事。path用相对路径方便整个目录打包迁移。3.3 训练命令与关键超参设置配置就绪后用 YOLO 系列训练一条命令即可。以常见的 yolov8n 为例轻量、适合二分类小数据集yolo detect train datadataset/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16逻辑说明modelyolov8n.pt用预训练权重做迁移学习2223 张图从零训容易过拟合预训练能明显加快收敛。imgsz640是通用尺寸如果标志在图中占比很小可以提到 960 或 1280但显存和速度要权衡。batch16在 8G 显存上比较稳显存不够就降到 8。epochs100配合早停patience 默认 50基本够用。训练中重点看mAP50和两个类别的precision/recall是否均衡如果某一类 recall 明显低多半是那一类的小目标多需要调 imgsz 或加数据增强。4. 避坑与排查标注、格式、训练三类高频问题这一章全是踩过的坑按「现象 → 原因 → 解决」写遇到对应症状直接对号入座。4.1 训练报「No labels found」或大量图片被跳过现象启动训练后日志刷出大量WARNING: No labels found实际参与训练的图片远少于 2223。原因通常是 txt 和 jpg 不同名或者分拣时把 txt 放错了目录YOLO 按图片路径推导标签路径找不到就跳过。解决回到 2.1 的校验命令确认三个目录文件数都是 2223且文件名除后缀外完全一致。特别注意xyxr_cla_9.jpg对应的是xyxr_cla_9.txt序号不能错位。4.2 类别索引错位导致可乐百事互换现象训练指标看着不错但推理时把 CocaCola 框标成 Pepsi。原因生成 txt 时的类别顺序和data.yaml的names不一致或者中途换过转换脚本。解决用 2.2 的可视化脚本抽查确认框上的类别名和实际标志一致。一旦发现错位不要改 yaml 去迁就而是重跑转换脚本统一顺序因为错位的 txt 已经污染了训练集。4.3 小目标漏检严重、mAP 上不去现象大标志能检出冰柜角落的小标志频繁漏检recall 偏低。原因imgsz640下小目标缩得太小特征丢失。解决把imgsz提到 960 或 1280同时开启 Mosaic 增强YOLO 默认开。如果显存吃紧改用yolov8s并降低 batch。另一个思路是切片推理把大图切成小块分别检测再合并适合标志密集的货架图。4.4 验证集指标虚高但实际场景拉胯现象验证集 mAP 0.9换一批新拍的货架图就崩。原因2223 张图可能来自有限的拍摄场景训练集和验证集同分布模型记住了背景而非标志本身。解决划分验证集时按场景分组比如按拍摄设备或货架类型切分让验证集包含训练集没见过的背景。另外可以加颜色抖动、随机裁剪等增强逼模型关注标志本身。4.5 框贴边被裁导致标注偏移现象可视化时发现部分框比实际标志偏小或偏移。原因原始标注时框贴到了图片边缘转换时被 3.1 的裁剪逻辑截断。解决这是正常保护但如果偏移明显说明原标注本身有问题。用可视化脚本全量扫一遍把异常框对应的图片挑出来要么修正 xml 重转要么直接从训练集剔除。2223 张里剔掉几十张脏数据对最终指标通常是正向的。5. 进阶技巧用混淆矩阵和切片推理把二分类检测榨干训练跑通只是及格线真正决定这份数据集能不能用在生产上的是你会不会看混淆矩阵、会不会处理密集小目标。这一章讲两个具体技巧都是我在实际项目里反复用的。先说混淆矩阵。YOLO 训练结束会在runs/detect/train/下生成confusion_matrix.png二分类任务里它就是一个 2x2 加背景的矩阵。重点看两处一是 CocaCola 和 Pepsi 之间的误判数如果非对角线数值大说明两个标志在视觉上被模型混淆了——可乐的红底白字和百事的蓝底白字在低分辨率下确实容易混这时候要么提高输入分辨率要么在数据增强里加对比度扰动。二是背景列background的数值如果大量真实框被预测成背景就是漏检对应 4.3 的小目标问题。我一般会把混淆矩阵和验证集的 PR 曲线对着看PR 曲线能告诉你 recall 提到多少时 precision 开始掉从而定一个合适的置信度阈值。再说切片推理SAHI 思路。货架图里标志往往又小又密整图缩到 640 会丢细节。做法是把原图按重叠窗口切成若干 640x640 的小块逐块推理再把框映射回原图坐标做 NMS 合并。核心代码逻辑是def slice_infer(img, model, slice_size640, overlap0.2): h, w img.shape[:2] step int(slice_size * (1 - overlap)) boxes [] for y in range(0, h, step): for x in range(0, w, step): patch img[y:yslice_size, x:xslice_size] if patch.shape[0] 32 or patch.shape[1] 32: continue res model(patch)[0] for b in res.boxes: xyxy b.xyxy[0].cpu().numpy() xyxy[[0, 2]] x; xyxy[[1, 3]] y # 映射回原图 boxes.append((*xyxy, float(b.conf), int(b.cls))) return boxes # 后续接 NMS 合并逻辑说明overlap0.2保证切片边界的目标不会被切断step是滑动步长。映射回原图那两行是关键x 方向加偏移、y 方向加偏移顺序不能反。最后所有框汇总做一次 NMSIoU 阈值一般设 0.5。这套流程在密集货架图上通常能把小目标 recall 提 10 个点以上代价是推理耗时增加适合离线稽核而非实时场景。最后说个习惯。这份数据集标注质量整体不错但「不对训练精度作保证」这句话是认真的——它给的是干净标注不是调好的模型。我现在的固定动作是拿到任何检测数据集先跑一遍全量可视化把框画回图上逐张扫挑出异常样本再跑一次基线训练看混淆矩阵最后才决定要不要上切片推理或换更大的模型。这套流程走下来基本不会出现「训练指标漂亮、上线就崩」的翻车。从那以后我每次接新数据集都强制走一遍可视化加基线希望帮到你。本文还有配套的精品资源点击获取
返回列表