news 2026/9/29 15:57:07

智能零售柜商品检测:1000张图、三种标签格式与YOLO11一键训练落地指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
智能零售柜商品检测:1000张图、三种标签格式与YOLO11一键训练落地指南

简介:本资源面向智能零售柜商品检测项目开发者与目标检测学习者,提供真实零售柜监控场景采集的1000张高质量商品图片,覆盖罐装饮料、袋装零食等常见品类,标注标签包含113个商品类别,可作为新零售场景通用商品检测数据的补充。资源包为1个PDF文件,约5.79MB,内附数据集基本情况介绍与获取方式,并说明提供VOC、COCO、YOLO三种主流标注格式,均采用labelimg标注,可直接用于YOLO等算法训练。随资源附赠YOLO11一键训练脚本,支持GPU、CPU及Mac芯片多平台训练方案,并附博主训练结果日志供参考,便于读者快速复现与调参。目前已有529人学习下载,适合需要快速搭建商品检测基线、验证模型效果或补充零售场景数据的中高级开发者参考使用。

1. 智能零售柜商品检测:1000 张图、三种标签格式与 YOLO11 一键训练到底怎么落地

智能零售柜的商品检测,说白了就是让摄像头在货柜里认出「这一格放的是可乐还是矿泉水」。这件事的难点不在模型有多深,而在数据能不能直接用、标签格式能不能对上、训练脚本能不能在 GPU、CPU、Mac 三端都跑起来。我见过太多团队卡在第一步:拿到一批图,标注是 VOC 的 XML,训练代码却要 YOLO 的 txt,中间转格式转出一堆坐标错位,最后模型学了个寂寞。这个标题给出的方案很实在——1000 张零售柜商品图,配 VOC、COCO、YOLO 三种格式标签,再加一个支持 GPU、CPU、Mac 三平台的 YOLO11 一键训练脚本。它解决的不是「模型怎么设计」,而是「数据到模型之间那条路怎么铺平」。适合谁?做智能零售柜、无人货柜、冰柜商品识别的算法工程师和嵌入式部署同学,尤其是手头有数据但被格式和平台折腾过的人。下面我按「数据长什么样 → 三种格式怎么转 → 三平台怎么训 → 坑在哪」的顺序,把这条链路拆开讲。

2. 零售柜商品数据集:1000 张图里到底有什么,为什么三种格式都要备

2.1 零售柜场景的数据特点与标注难点

零售柜商品检测和通用目标检测最大的区别在于:商品排列密集、遮挡严重、同类商品外观差异小、光照反射强。1000 张图这个量级,放在 COCO 上连零头都不够,但在零售柜这种「背景固定、类别有限、目标位置相对规律」的场景里,如果标注质量够高,是能训出一个可用模型的。我一般会先看三个指标:类别数、每类实例数、遮挡比例。零售柜常见类别在 20 到 80 之间,每类至少要有 30 到 50 个实例,否则模型对长尾类别基本没反应。

标注难点集中在两处。第一是边界框贴边:商品紧挨着摆放时,框稍微大一点就框到隔壁商品,小一点又切掉商品边缘,YOLO 系列对框的回归比较敏感,标注不一致会直接反映在验证集的 mAP 抖动上。第二是反光区域:柜门玻璃反光会让商品局部过曝,标注时容易把反光当成商品的一部分,训练时模型学到的是反光纹理而不是商品特征。常见做法是标注时统一按「商品可见轮廓」画框,反光区域不计入,同时在数据增强里加随机亮度扰动来缓解。

1000 张图按 8:1:1 切分,训练集 800 张、验证集 100 张、测试集 100 张。如果类别多,建议按类别分层抽样,保证每个类别在验证集里都有实例。这个切分比例不是死的,数据少的时候可以 7:2:1,验证集大一点方便观察过拟合。

2.2 VOC、COCO、YOLO 三种格式的字段对照

三种格式本质上是同一份标注的不同表达方式,理解字段对应关系,转格式时就不会慌。VOC 是 XML,一个图一个文件,框用 xmin、ymin、xmax、ymax 表示绝对像素坐标。COCO 是一个大 JSON,images、annotations、categories 三个数组,框用 [x, y, width, height] 表示绝对像素,且 x、y 是左上角。YOLO 是一图一个 txt,每行class_id cx cy w h,全部是归一化到 0 到 1 的相对值,cx、cy 是框中心。

格式存储方式框表示坐标类型类别表示
VOC每图一个 XMLxmin, ymin, xmax, ymax绝对像素name 字符串
COCO单个 JSONx, y, width, height绝对像素category_id 整数
YOLO每图一个 txtcx, cy, w, h归一化 0-1class_id 整数

转换时最容易翻车的是 VOC 到 YOLO 的归一化:cx = (xmin + xmax) / 2 / img_w,cy = (ymin + ymax) / 2 / img_h,w = (xmax - xmin) / img_w,h = (ymax - ymin) / img_h。分母是图像宽高,不是框的宽高,这一点搞错,框会整体缩到左上角。COCO 的 category_id 通常从 1 开始,YOLO 的 class_id 从 0 开始,转换时要减 1,否则类别整体偏移一位,训练时 loss 能降但预测全错。

2.3 用脚本把 VOC 批量转成 YOLO 与 COCO

下面这个脚本做三件事:读 VOC XML,生成 YOLO txt,同时汇总成 COCO JSON。放在数据集根目录运行,假设目录结构是images/和annotations/。

import os import json import xml.etree.ElementTree as ET from PIL import Image # 类别列表,顺序决定 class_id,必须与训练时的 data.yaml 一致 CLASSES = ["cola", "water", "chips", "milk", "juice"] class_to_id = {c: i for i, c in enumerate(CLASSES)} def voc_to_yolo(xml_path, img_path, out_txt_path): tree = ET.parse(xml_path) root = tree.getroot() # 读取图像真实宽高,不要用 XML 里的 size,部分标注工具会写错 with Image.open(img_path) as im: img_w, img_h = im.size lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in class_to_id: continue # 跳过未定义类别,避免 class_id 越界 bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 裁剪到图像边界,防止标注越界导致归一化后超出 0-1 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)) cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_to_id[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(out_txt_path, "w") as f: f.write("\n".join(lines)) def build_coco(image_dir, xml_dir, out_json): coco = {"images": [], "annotations": [], "categories": []} for i, c in enumerate(CLASSES): coco["categories"].append({"id": i + 1, "name": c}) ann_id = 1 img_id = 1 for fname in sorted(os.listdir(xml_dir)): if not fname.endswith(".xml"): continue stem = os.path.splitext(fname)[0] img_path = os.path.join(image_dir, stem + ".jpg") if not os.path.exists(img_path): continue with Image.open(img_path) as im: w, h = im.size coco["images"].append({"id": img_id, "file_name": stem + ".jpg", "width": w, "height": h}) tree = ET.parse(os.path.join(xml_dir, fname)) for obj in tree.getroot().findall("object"): name = obj.find("name").text.strip() if name not in class_to_id: continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) coco["annotations"].append({ "id": ann_id, "image_id": img_id, "category_id": class_to_id[name] + 1, # COCO 从 1 开始 "bbox": [xmin, ymin, xmax - xmin, ymax - ymin], "area": (xmax - xmin) * (ymax - ymin), "iscrowd": 0 }) ann_id += 1 img_id += 1 with open(out_json, "w") as f: json.dump(coco, f) if __name__ == "__main__": img_dir = "images" xml_dir = "annotations" yolo_dir = "labels" os.makedirs(yolo_dir, exist_ok=True) for fname in os.listdir(xml_dir): if fname.endswith(".xml"): stem = os.path.splitext(fname)[0] voc_to_yolo( os.path.join(xml_dir, fname), os.path.join(img_dir, stem + ".jpg"), os.path.join(yolo_dir, stem + ".txt") ) build_coco(img_dir, xml_dir, "coco.json") print("done")

逻辑说明:voc_to_yolo逐图读 XML,用 PIL 拿真实宽高做归一化,并对框做边界裁剪,避免越界。build_coco同时遍历 XML 和图像,生成 COCO 的 images、annotations、categories 三段。参数上,CLASSES的顺序就是 YOLO 的 class_id 顺序,必须和训练配置里的 names 完全一致,改一个顺序就要重新生成标签。class_to_id过滤掉未定义类别,防止训练时出现越界索引。运行完检查labels/下 txt 行数是否和 XML 里 object 数一致,不一致就说明有类别被过滤或框被裁没了。

3. YOLO11 一键训练脚本:GPU、CPU、Mac 三平台怎么跑通

3.1 三平台环境差异与依赖安装

GPU、CPU、Mac 三平台的核心差异在 PyTorch 的安装包和推理后端。NVIDIA GPU 走 CUDA,需要装带 cu 版本的 torch;纯 CPU 装 CPU 版 torch 即可;Mac 走 MPS,Apple Silicon 上 torch 的 MPS 后端能加速,但部分算子支持不全,遇到不支持的算子会自动回退 CPU,速度会掉。我一般先确认三件事:Python 版本(3.9 到 3.11 比较稳)、torch 是否装对、ultralytics 版本是否支持 YOLO11。

# NVIDIA GPU 平台,CUDA 12.1 示例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics # 纯 CPU 平台 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics # Mac Apple Silicon pip install torch torchvision pip install ultralytics

装完用一段代码验证后端是否可用,别等训练跑起来才发现用的是 CPU。

import torch print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("mps available:", torch.backends.mps.is_available()) if torch.cuda.is_available(): print("gpu:", torch.cuda.get_device_name(0))

参数说明:torch.cuda.is_available()为 True 才走 GPU,torch.backends.mps.is_available()为 True 才走 Mac 加速。如果两个都是 False,训练会自动落到 CPU,1000 张图在 CPU 上跑 YOLO11n 大概每轮几分钟到十几分钟,能跑但慢。GPU 上同样数据每轮几秒到几十秒,差距明显。

3.2 data.yaml 与训练超参的对应关系

YOLO11 训练靠一个 data.yaml 描述数据位置和类别,路径写错是最常见的翻车点。下面是一个零售柜数据集的配置示例。

path: /data/retail_cabinet train: images/train val: images/val test: images/test nc: 5 names: 0: cola 1: water 2: chips 3: milk 4: juice

path是数据集根目录,train、val、test是相对 path 的路径。YOLO 会自动去找同名 labels 目录下的 txt,所以images/train对应labels/train,目录名必须成对。nc是类别数,names的键必须从 0 连续到 nc-1,中间断号会导致类别映射错乱。改类别时,data.yaml 和生成标签时的 CLASSES 必须同步改,只改一边就是血泪经验。

训练超参里,imgsz零售柜场景建议 640,商品小的话可以上 960,但显存和速度要权衡。batch在 GPU 上按显存给,8G 显存跑 YOLO11n 640 大概能到 16 到 32。epochs1000 张图建议 100 到 300,配合早停。lr0默认 0.01,数据少可以降到 0.001 到 0.005,避免早期震荡。

3.3 一键训练脚本与三平台启动命令

把训练参数写进一个脚本,三平台共用,靠自动检测后端来切换设备。

import torch from ultralytics import YOLO def pick_device(): if torch.cuda.is_available(): return 0 # 第一块 NVIDIA GPU if torch.backends.mps.is_available(): return "mps" # Apple Silicon return "cpu" if __name__ == "__main__": device = pick_device() print("using device:", device) model = YOLO("yolo11n.pt") # 从预训练权重起步,小数据集必备 model.train( data="data.yaml", epochs=200, imgsz=640, batch=16 if device != "cpu" else 4, device=device, workers=4 if device != "cpu" else 2, patience=30, # 30 轮无提升就早停 project="runs/retail", name="yolo11n_retail", pretrained=True, optimizer="auto", cos_lr=True )

逻辑说明:pick_device按 CUDA、MPS、CPU 的优先级选设备,保证同一份脚本三平台都能跑。YOLO("yolo11n.pt")加载预训练权重,1000 张图从零训基本学不出东西,预训练是必须的。batch在 CPU 上降到 4,workers降到 2,避免内存和进程数爆掉。patience=30是早停,验证集 30 轮不提升就停,省时间。cos_lr=True用余弦退火,小数据集上比固定学习率稳。跑完在runs/retail/yolo11n_retail/weights/下拿 best.pt 做推理。

三平台启动命令一致:python train.py。GPU 上想指定某块卡,改device=0或device=1。Mac 上如果 MPS 报算子不支持,把 device 改成 "cpu" 先跑通,再逐步试 MPS。

4. 训练与推理避坑:零售柜商品检测最常见的 5 个翻车点

4.1 现象:loss 一直降但 mAP 不动

原因通常是标签类别错位或框归一化错误。VOC 转 YOLO 时 class_id 没减 1,或者 cx、cy 分母用错,模型学到的是错误映射,loss 能降是因为它在拟合错误目标,验证集自然不动。解决:抽 5 张图,用脚本把 YOLO txt 画回图上,肉眼看框和类别对不对。画框代码用 PIL 的 ImageDraw,把 cx、cy、w、h 反归一化后画矩形,类别名标在框上,一眼就能看出偏移。

4.2 现象:训练报 “no labels found”

原因一般是目录结构不对。YOLO 找标签的规则是 images 路径里的images替换成labels,后缀换成 txt。如果图像在images/train,标签必须在labels/train,且文件名(不含后缀)一致。常见错误是把标签放在labels/train/下但文件名带了_jpg之类后缀。解决:写个检查脚本,遍历 images 下所有图,确认对应 labels 下同名 txt 存在且非空,缺的列出来补。

4.3 现象:GPU 显存够但训练 OOM

原因可能是 imgsz 或 batch 设太大,也可能是 workers 太多导致数据加载进程占内存。零售柜图分辨率高的话,640 的 imgsz 在 8G 显存上 batch 16 可能就顶了。解决:先把 batch 降到 8,再不行降 imgsz 到 512,或者开amp=True混合精度。workers 在 Windows 上设 0 或 2,Linux 上可以 4 到 8,Mac 上 2 比较稳。

4.4 现象:Mac 上 MPS 训练报算子不支持

原因是部分算子在 MPS 后端没实现,PyTorch 会抛错而不是自动回退。解决:设环境变量PYTORCH_ENABLE_MPS_FALLBACK=1,让不支持的算子回退 CPU,速度会慢但能跑完。如果还不行,直接 device="cpu",Mac 上 CPU 训 1000 张图 YOLO11n 大概几小时,能接受。

4.5 现象:验证集 mAP 高但实际柜子里认错

原因是验证集和实际场景分布不一致。1000 张图如果都来自同一个柜子、同一光照,验证集再高也只是过拟合这个柜子。解决:切分数据时按柜子或按光照条件分层,验证集里放不同柜子、不同时段的图。另外推理时把 conf 阈值调高到 0.5 以上,零售柜场景宁可漏检也别错检,错检会导致结算错误。

5. 从 1000 张图到可用模型:我常用的验证与迭代习惯

训练跑完不是终点,我一般会做三件事来确认模型能不能上柜。第一件是混淆矩阵,YOLO 训练完会自动生成confusion_matrix.png,重点看同类商品之间有没有互相混,比如可乐和零度可乐混,说明特征区分度不够,要么加数据要么加类别。第二件是抽 20 张验证集图做推理,把预测框和真实框叠一起看,重点看小目标和遮挡目标的召回,零售柜里被挡一半的商品是常态,召回低就要在数据增强里加随机遮挡。第三件是拿手机拍几张实际柜子的图,不标注直接推理,看模型在真实光照和角度下的表现,这一步最能暴露过拟合。

迭代上,1000 张图训出来的模型如果 mAP50 在 0.85 以上,基本能上柜做辅助识别;要到 0.95 以上,通常得补数据到 3000 张以上,或者用更大模型如 YOLO11s、YOLO11m。我自己的习惯是先用 YOLO11n 快速跑通链路,确认数据和标签没问题,再换大模型刷精度。换模型时 data.yaml 和标签都不用动,只改YOLO("yolo11s.pt")这一行,这是 YOLO 系列最省心的地方。

还有一个容易忽略的点:推理部署时的预处理要和训练一致。训练时 imgsz=640,推理时如果传原图不 resize,YOLO 内部会自己 letterbox,但如果你在外面又做了一次 resize,就会双重缩放导致框偏移。我一般推理直接用model.predict(source, imgsz=640),不自己预处理。最后,模型导出成 ONNX 或 TensorRT 上边缘设备时,记得确认导出时的 opset 和动态轴设置,零售柜边缘盒子算力有限,TensorRT INT8 量化能提速但会掉点,量化后一定要重新跑一遍验证集。

希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 15:55:52

J-Link V9被识别为克隆版?SEGGER V6.22回滚固件恢复教程

J-Link V9用了好几年一直挺稳的,结果某天心血来潮点了SEGGER软件里的“Update Firmware”,然后Keil里直接报错,调试器怎么都连不上目标板——那一刻我才意识到,自己手里这支“老伙计”大概率已经被官方新版软件标记成了克隆版。这…

作者头像 李华
网站建设 2026/9/29 15:54:38

银河麒麟V10上编译e1000e与rtl8125网卡驱动:从环境检查到避坑

简介:面向银河麒麟V10系统维护者与网卡驱动开发者的可编译网卡驱动资源包,解决e1000e与RTL8125两款网卡在国产操作系统上因内核适配导致的编译失败问题。包内已提前处理重复定义删除、函数参数修改等兼容性调整,可直接用于编译安装。资源共56…

作者头像 李华
网站建设 2026/9/29 15:54:12

Eclipse SVN插件完全指南:从选型安装到冲突解决的一线避坑经验

开工前先聊聊:我为什么写这篇Eclipse SVN插件指南 如果你打开这篇博文,大概率正被Eclipse里的SVN插件折磨着——要么安装半天连不上仓库,要么提交代码时弹一堆看不懂的英文报错,要么界面上的图标和菜单全消失,项目右键…

作者头像 李华
网站建设 2026/9/29 15:53:40

Polyline与Polygon实战:地图覆盖物绘制、编辑与坐标转换全解析

1. 项目概述:为什么Polyline和Polygon是地图开发的基本功今年我在给一个物流调度平台做前端地图功能时,接到一个看起来很普通的任务:在业务地图上画出车辆历史行驶轨迹(折线),同时把各配送区域用不同颜色的…

作者头像 李华
网站建设 2026/9/29 15:53:17

手搓Java反序列化Gadget链:CC2、CC4、CC5原理与构造详解

1. 先把Gadget链这回事说透Java反序列化攻防里,最劝退新人的不是反序列化本身,而是Gadget链那堆绕来绕去的类名。CC2、CC4、CC5这些名字,懂的人觉得就几行代码的事,不懂的人看ysoserial源码像看天书。我见过不少人拿着现成POC跑通…

作者头像 李华
网站建设 2026/9/29 15:53:17

纯Shell+curl实现SeaweedFS S3兼容接口签名访问与脚本封装

SeaweedFS跑起来之后,最爽的其实是那套S3兼容接口。内部工具链直接用awscli或者各语言SDK就能对接,可总有那么些场景绕不开手工构造签名:最小化部署的容器里没有Python没有Go,堡垒机上只有curl和openssl,或者要写一个跨…

作者头像 李华