罐装饮料识别数据集实战:COCO转YOLO与YOLOv8训练全攻略
2026/9/23 2:50:56 网站建设 项目流程

简介:面向目标检测与图像识别学习者的罐装饮料识别数据集,涵盖薯片、东鹏特饮、红牛、芬达、养乐多、可乐、雪碧、王老吉、AD钙奶、金典、特仑苏、蒙牛、伊利、旺仔牛奶等常见商品,标注信息完整,可直接用于零售场景下的商品检测模型训练。打包文件共1681个,以1676张jpg图片为主体,配有3个JSON标注文件和2个TXT说明文件,标注遵循COCO格式,适合Faster R-CNN、YOLO等主流模型直接读取,省去人工标注环节。每张图片的目标框与类别信息均保存在JSON中,TXT文件可作类别映射或训练说明;数据覆盖不同拍摄角度、光照和摆放方式,有助于增强模型在货架或自动售货机等实际场景中的泛化能力。整体大小45.57MB,便于下载与快速迭代实验。已有823人学习浏览,适合需要现成训练数据的学生、算法工程师及零售AI项目开发者,也可用于毕业设计、课程实践或迁移学习基础训练。

1. 罐装饮料识别数据集:一千多张图能支撑起什么场景

货架盘点、自动售货机视觉结算、无人零售柜,这些场景背后都有一个共同的技术需求:快速准确地识别货架上的饮料品牌。市面上公开的饮料数据集不少,但要么类别太杂、背景太乱,要么标注格式需要二次加工。这份罐装饮料识别数据集提供了一千多张真实场景图片,覆盖东鹏特饮、红牛、芬达、养乐多、可乐、雪碧、王老吉、AD钙奶、金典、特仑苏、蒙牛、伊利、旺仔牛奶等常见品类,并且直接给出COCO格式的标注文件,省去了自己画框标类别的功夫。对刚接触目标检测的开发者来说,它是一份可以直接喂给训练框架的现成数据;对做零售视觉方案的工程师来说,它则是一个可以用来验证算法选型和调参思路的基准集。本文就从数据格式、转换脚本、训练配置到踩坑记录,完整拆一遍这套资源的用法。

2. 数据集构成与COCO格式:先看清这一千多张图里藏着什么

2.1 文件命名里的信息:来自Roboflow的导出痕迹

打开数据集压缩包,你会发现文件名长这样:294_jpg.rf.9c5ca41979ee0399ba8106f011a025a2.jpgpepsi-cola24_png.rf.24fd4e671b63b84b31712c206ba8290e.jpg。这里的.rf.是 Roboflow 平台导出文件的典型标记,294_jpg表示原始文件名和图片格式,后面那串十六进制字符串是图片在平台上的唯一标识。这意味着这份数据大概率是从 Roboflow 项目里导出的,而 Roboflow 在导出时会自动完成训练集、验证集、测试集的划分,并且按标注格式重新组织文件结构。

这个信息对你后续使用有实际影响。Roboflow 导出时默认会做数据增强(如翻转、旋转、亮度调整),所以同一张原图可能会在数据集中出现多次,但文件名后缀不同。如果你发现训练集里有两张看起来几乎一样的图,不用惊讶,这是增强后的副本,不是数据重复。另外,Roboflow 导出的 COCO 格式里,图片的id和标注的image_id对应关系是打乱的,不能想当然认为第一张图对应第一个标注条目,一定要通过 JSON 里的字段关联。

2.2 COCO标注的JSON结构:看懂categories与annotations

COCO 格式的核心是一个 JSON 文件,里面有三个关键字段:imagesannotationscategoriesimages是一个列表,每个元素包含idfile_namewidthheight四个基本属性;annotations列表里每个元素描述一个目标框,包含idimage_idcategory_idbboxareaiscrowdcategories列表则定义了类别 ID 到类别名称的映射。

这份数据集里bbox的格式是[x, y, width, height],注意这是左上角坐标加宽高,不是中心点坐标加宽高。很多人在写转换脚本时在这里翻车,把 COCO 的 bbox 当成了中心点格式,导致画出来的框位置完全错位。另外,category_id是从 1 开始编号的,不是 0,这在转 YOLO 格式时要做减一操作,因为 YOLO 的类别索引是从 0 开始的。

{ "images": [ {"id": 1, "file_name": "294_jpg.rf.9c5ca41979ee0399ba8106f011a025a2.jpg", "width": 640, "height": 640} ], "annotations": [ {"id": 1, "image_id": 1, "category_id": 5, "bbox": [210, 180, 120, 240], "area": 28800, "iscrowd": 0} ], "categories": [ {"id": 1, "name": "coca-cola"}, {"id": 5, "name": "red-bull"} ] }

上面这段 JSON 说的是:编号为 1 的图片里有一个目标框,它的类别 ID 是 5,框的左上角在 (210, 180),宽 120、高 240,面积 28800 像素。这里iscrowd为 0 表示这是一个独立目标,不是一群人挤在一起的那种密集标注。

2.3 类别清单与场景边界

从摘要描述看,这份数据集的类别包括薯片、东鹏特饮、红牛、芬达、养乐多、可乐、雪碧、王老吉、AD钙奶、金典、特仑苏、蒙牛、伊利、旺仔牛奶等。这些类别分两类:一类是瓶装/罐装饮料,一类是利乐砖或塑料袋包装的乳制品。后者的外观差异比饮料罐更小,比如金典、特仑苏、蒙牛、伊利这四个品牌的外包装主色调都是白色系,区分难度明显高于可乐和雪碧。

这个类别结构决定了模型的难点不在“检测”而在“分类”。检测阶段只需要把货架上的物体框出来,但分类阶段要区分白底包装上极其相似的文字和图案。如果你用这份数据训练完发现金典和特仑苏经常混淆,不用怀疑数据质量,这是这类细粒度分类任务的固有难点,后面会专门讲怎么缓解。

3. 从COCO到YOLO:标注格式转换脚本与参数调整

3.1 为什么要转格式:训练框架的输入差异

COCO 格式是很多检测框架的通用标准,但 YOLO 系列(包括 YOLOv5、YOLOv8)默认使用自己的 TXT 标注格式。YOLO 的标注方式是每张图片对应一个同名 TXT 文件,文件里每行描述一个目标框:class_id x_center y_center width height,四个数值全部归一化到 0~1 之间,就是除以图片宽高后的比例值。

如果你直接用 COCO 的 JSON 去训练 YOLO,需要写个 DataLoader 做适配,但更省事的做法是转成 YOLO 原生格式。转换的核心就是把 COCO 的[x, y, width, height]像素坐标变成 YOLO 的[x_center, y_center, width, height]归一化坐标,同时把category_id减 1 作为 YOLO 的类别索引。

3.2 转换脚本实现与逐段说明

下面是我常用的 COCO 转 YOLO 脚本,基于 Python 和 pycocotools,在 Windows 和 Linux 上都能直接跑。脚本会读取 COCO 的 JSON 文件,为每一张图片生成对应的 TXT 标注文件,并按trainvaltest三个子目录分别存放。

import os import json from pycocotools.coco import COCO def coco_to_yolo(coco_json_path, output_dir): # 读取 COCO 标注文件 coco = COCO(coco_json_path) # 建立 category_id 到 yolo_id 的映射,注意减 1 cat_id_map = {cat_id: idx for idx, cat_id in enumerate(coco.getCatIds())} # 遍历所有图片 for img_id in coco.getImgIds(): img_info = coco.loadImgs(img_id)[0] img_width = img_info['width'] img_height = img_info['height'] file_name = img_info['file_name'] # 图片文件名去掉扩展名,作为 txt 文件名 txt_name = os.path.splitext(file_name)[0] + '.txt' txt_path = os.path.join(output_dir, txt_name) ann_ids = coco.getAnnIds(imgIds=img_id) anns = coco.loadAnns(ann_ids) lines = [] for ann in anns: # bbox 格式是 [x, y, width, height] x, y, w, h = ann['bbox'] # 转成 YOLO 需要的中心点格式并归一化 x_center = (x + w / 2) / img_width y_center = (y + h / 2) / img_height norm_w = w / img_width norm_h = h / img_height yolo_id = cat_id_map[ann['category_id']] lines.append(f"{yolo_id} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}") with open(txt_path, 'w') as f: f.write('\n'.join(lines)) if __name__ == '__main__': coco_to_yolo('annotations/instances_train.json', 'labels/train')

这个脚本的关键逻辑就三步。第一步,通过coco.getCatIds()拿到所有类别 ID,然后用 enumerate 重新编号,让类别从 0 开始;第二步,遍历每张图片的标注,把 bbox 从左上角坐标加宽高换算成中心点坐标加宽高;第三步,写 TXT 文件时保留六位小数,避免精度丢失。参数方面,coco_json_path指向你的 COCO JSON 文件路径,output_dir是 TXT 文件输出目录,需要提前建好。

转换前建议先检查 bbox 是否有超出图片边界的框。COCO 允许框略微越界,但 YOLO 训练时大部分框架会报警甚至忽略这些框。我一般会在转换脚本里加一个判断:如果归一化后的值小于 0 或大于 1,就打印图片名和框坐标,手动排查。

3.3 转换结果验证:可视化与统计

转换完成后别急着训练,先做两步验证。第一步是可视化,在图片上画出 TXT 标注的框,看位置是否和原目标吻合。可以用 OpenCV 快速画框,确认转换没有出现坐标偏移或类别错位。第二步是统计,检查生成的所有 TXT 文件里有没有空文件,空文件说明对应图片没有标注目标,这在推理阶段可能被误认为是背景。

import cv2 import os def visualize_yolo_annotation(image_path, txt_path, class_names): img = cv2.imread(image_path) h, w = img.shape[:2] with open(txt_path, 'r') as f: for line in f.readlines(): parts = line.strip().split() cls_id = int(parts[0]) x_center, y_center, box_w, box_h = map(float, parts[1:]) # 反归一化还原像素坐标 x1 = int((x_center - box_w / 2) * w) y1 = int((y_center - box_h / 2) * h) x2 = int((x_center + box_w / 2) * w) y2 = int((y_center + box_h / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.imshow('check', img) cv2.waitKey(0) cv2.destroyAllWindows()

这段可视化脚本把归一化的 YOLO 坐标反算回像素坐标,注意中心点要减去宽高的一半得到左上角,加上一半得到右下角。class_names是一个列表,索引对应 YOLO 的类别 ID,顺序必须和训练时的data.yaml一致,否则画出来的类别名会错乱。建议随机抽 20 到 30 张图做可视化,重点看多目标场景下框之间的重叠情况和边界贴合度。

4. 用YOLOv8把数据集跑起来:训练配置与参数调优

4.1 数据集yaml的写法与路径坑

数据准备好后,第一步是写 YOLOv8 的data.yaml文件。这个文件告诉训练框架三件事:类别列表、每张图片的路径、验证集的路径。路径建议写绝对路径,因为相对路径在不同环境下解析结果不一样,经常出现 “No labels found” 的报错,其实就是路径没写对。

# data.yaml path: D:/datasets/beverage_dataset # 数据集根目录 train: images/train val: images/val test: images/test names: 0: chips 1: dongpeng 2: redbull 3: fanta 4: yakult 5: cola 6: sprite 7: wanglaoji 8: ad-milk 9: jindian 10: telunsu 11: mengniu 12: yili 13: wannaixn

注意path指向的是数据集根目录,trainval是相对这个根目录的子路径。names列表的索引和转换脚本里的类别 ID 必须一一对应,顺序错了模型不会报错,但你的预测结果会全乱。这里colab环境或者 Windows 路径带中文的情况尤其容易踩坑,YOLO 框架在部分版本里对中文路径支持不好,建议数据集路径保持纯英文。

4.2 训练命令与关键超参数

数据集就绪后,用下面的命令启动训练。这里选择 YOLOv8s 作为基础模型,因为数据集规模只有一千多张,用 v8x 或 v8l 这种大模型很容易过拟合,训练时间还长。imgsz设为 640,这是速度和精度的平衡点,如果你的目标场景是货架远端拍摄,可以试 832 或 1024,但显存占用会明显上升。

yolo detect train \ model=yolov8s.pt \ data=data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ workers=8 \ lr0=0.01 \ optimizer=auto \ patience=20 \ project=runs/detect \ name=beverage_exp1

参数说明:epochs=100对一千多张图来说足够,配合patience=20做早停,连续 20 轮验证集指标不提升就自动停止,节省时间;batch=16在 8GB 显存下比较稳,如果你的显卡显存更大可以提到 32;lr0=0.01是初始学习率,YOLOv8 默认使用 SGD 优化器,如果是 AdamW 建议降到 0.001;workers=8是数据加载线程数,Windows 下如果报 DataLoader 相关错误,降到 2 或 4。

实际跑起来你会发现训练前几个 epoch 的 loss 下降很快,但到 30 到 50 个 epoch 后会进入平台期。这时候不用急着加 epoch 数,先看验证集的 mAP 变化曲线,如果 mAP50 还在缓步上升就让它继续跑,如果已经持平甚至下降,说明开始过拟合了,应该提前终止。

4.3 训练过程中的监控指标

训练日志里重点看三个指标:box_losscls_lossmAP50-95box_loss衡量预测框和真实框的回归误差,持续下降是正常的;cls_loss是分类损失,如果这个值降不动,说明相似类别(比如金典和特仑苏)的区分有问题。mAP50-95是 COCO 标准评估指标,mAP50 表示 IoU 阈值为 0.5 时的平均精度,mAP50-95 是多个阈值下的平均,后者更严格。

我一般同时开着 TensorBoard 看曲线,命令是tensorboard --logdir runs/detect。看到 mAP50-95 连续 10 个 epoch 不涨,就手动 Ctrl+C 停掉,然后去分析错误样本。训练结束后,runs/detect/beverage_exp1/weights目录下会有best.ptlast.ptbest.pt是验证集指标最好的一轮权重,推理时用这个。

5. 避坑指南:标注数据训练最常见的五个问题

5.1 图片尺寸不一致导致归一化坐标错位

现象:训练时 loss 正常下降,但用best.pt做推理时,检测框的位置整体偏移,有的框跑到目标旁边去了。

原因:数据集里的图片尺寸不统一,部分图片是 640×640,部分是 416×416,还有少量竖版长图。Roboflow 导出时通常会自动 resize 到统一尺寸,但这份数据里混入了原始尺寸的图片。转换脚本里如果直接拿每张图片的宽高做归一化,理论上没问题,但 YOLO 训练时会对图片做 letterbox 填充(把图缩放到正方形再补灰边),如果训练时输入的图和标注时的图尺寸不一致,归一化坐标就被拉偏了。

解决:训练前用脚本统一检查所有图片尺寸。YOLOv8 的imgsz=640会自动做 letterbox,理论上能处理任意尺寸输入,但前提是标注文件里每张图的归一化坐标是对应它自己原始尺寸的。我在转换脚本里加了一步:读取图片实际宽高,打印所有尺寸分布,如果有超过 10% 的图片尺寸和主流尺寸不一致,先统一 resize 再转换。

5.2 类别 ID 减 1 的细节漏掉

现象:训练顺利完成,但预测出来的类别名和实际目标对不上,比如把红牛识别成了可乐。

原因:COCO 的category_id从 1 开始,YOLO 的类别索引从 0 开始。转换时忘记减 1,导致所有类别整体错位一位,模型学到的类别对应关系和真实情况差了一格。

解决:转换后随机抽 10 个 TXT 文件,手动核对类别 ID 是否和data.yamlnames对应。我习惯在转换脚本里写一个自检函数,输出每个类别 ID 的样本数,和 COCO 原始 JSON 里的统计对比,数量一致才继续训练。

5.3 小目标漏检:货架远端饮料框太小

现象:训练集 mAP 很高,但实际拍一张货架全景图,远处的饮料瓶全部漏检,一个框都没出。

原因:一千多张图里,大部分标注框是近景或中景,占图片面积比例较大。远场景下饮料罐可能只有 20×30 像素,对于 YOLOv8s 的原生检测头来说,小目标特征在多次下采样后已经丢失。mAP 高是因为验证集里也以小目标为主,模型没学会检测小目标不代表它不行,只是数据分布没覆盖。

解决:不要只依赖原始数据集,用 Roboflow 的增强功能或自己写脚本对小目标区域做裁剪,生成一批“大目标”训练样本。或者训练时开启 YOLOv8 的augment=Truemosaic=1.0,让模型在多尺度输入下看到更多中间态。另一个实用思路是把imgsz从 640 提到 960,让小目标在输入时占更多像素。

5.4 相似包装类别互相混淆

现象:金典、特仑苏、蒙牛、伊利经常互相识别错误,验证集 mAP 不低,但实际用起来这几个类别总是错。

原因:这四个品牌的包装主色调都是白色系,形状也都是利乐砖,检测特征几乎一致。模型只能靠包装上的文字和图案细节区分,但一千多张图里每个类别的样本量可能只有几十张,远不够学到稳定的文字特征。

解决:这是典型的细粒度分类问题。第一种方案是增加每类的样本量到 200 张以上;第二种方案是把模型从 v8s 升级到 v8m 或 v8l,更大的模型能学到更细的特征;第三种方案是接受现实,把这几个类别合并成一个 “white-milk” 类,后接一个 OCR 模块读包装上的文字,用文本内容做最终区分。我在实际项目里经常用第三种方案,效果稳定且不受光照影响。

5.5 数据集划分不合理导致评估虚高

现象:训练时验证集 mAP50 高达 0.98,但放线上完全不是这么回事,识别率掉到 60% 多。

原因:这份数据集是从 Roboflow 导出的,导出时的划分可能是随机的。同一场景的连续帧(比如同一瓶可乐在视频里的多帧)可能同时出现在训练集和验证集里,模型相当于记住了答案。这种“数据泄漏”让评估结果虚高,实际场景的光照、角度、遮挡变化后,模型立马现原形。

解决:按图片分组或按场景划分数据集,保证同源图片不跨集。你可以看文件名前缀,比如294_jpg里的 294 如果是一个场景编号,就按这个编号划分。更好的做法是手动拍一批现场照片作为验证集,验证集不参与训练,这样评估结果才真实。

6. 进阶:用混淆矩阵与可视化验证模型,再谈部署取舍

训练完拿到best.pt只是第一步,我一般会先跑一遍yolo detect val,然后去看三样东西:confusion_matrix.pngresults.pngval_batch0_pred.jpgconfusion_matrix.png能直接看出哪两个类别经常混淆,如果金典和特仑苏的矩阵格子里数值很高,就得用上一节说的合并策略或者加样本;val_batch0_pred.jpg是验证集第一张图的预测结果可视化,用肉眼扫一遍能发现框偏移、漏检、重复框这类机器指标看不出来的问题。

验证通过后,下一步是部署。这份数据集训练的模型有两个典型部署路径。第一个是边缘设备部署,把best.ptyolo export model=best.pt format=onnx导出成 ONNX,再用 TensorRT 转成 engine 文件跑在 Jetson Orin 或 Jetson Nano 上。一千多张图训练出来的模型,做推理时单帧耗时在 10 到 30 毫秒,完全够收银台或者货架摄像头的实时识别。第二个是服务端部署,用 FastAPI 包一个推理接口,接收图片返回检测框和类别,配合数据库做商品陈列分析。

部署时要特别注意类别 ID 的映射一致性。data.yaml里的names顺序、ONNX 模型输出的索引、后端代码里读到的类别名,三处必须完全一致。我踩过这个坑:训练时names里可乐在第 6 位,导出 ONNX 后模型输出索引 5 对应可乐,但后端代码写死了索引 5 是红牛,结果线上跑了一周,红牛识别率接近 100%,其实是把可乐全识别成了红牛。从那以后我每次部署前都会拿一张标注过的测试图,跑一次推理接口,把输出的类别名和真实目标逐一核对,再放量上线。

如果你打算把这套模型往产品里推,还有一个值得做的优化:调低置信度阈值。默认的conf=0.25对大多数场景够用,但像远距离瓶装饮料这种目标,置信度普遍在 0.2 到 0.3 之间,阈值设太高会漏检。我一般会跑一次验证集,统计所有正确检测框的置信度分布,把阈值设到分布的最低点附近。当然,阈值降低会带来误检增多,实际调参时可以结合部署场景的温度,宁可漏检也不误检的时候(比如结算场景)就调高一点,主打召回的场景就调低。这套调参思路同样适用于你手里的其他检测项目,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询