货架空置缺货检测数据集:4470张双类标签VOC+YOLO格式实战指南

发布时间:2026/10/11 20:43:40

货架空置缺货检测数据集:4470张双类标签VOC+YOLO格式实战指南 简介这份资源是面向零售智能化与计算机视觉方向的超市货架空置缺货检测数据集适用于目标检测模型训练、货架陈列分析及补货预警等场景适合具备一定深度学习基础、需要真实货架图像做实验或项目落地的开发者与研究人员。压缩包共约2000个文件以Pascal VOC格式的xml标注文件和YOLO格式的txt标注文件为主另附说明文档整体约251.17MB图片与标注一一对应可直接用于主流检测框架。数据集包含4470张jpg图片标注类别为Empty-Space与Reduced两类分别对应货架空置与商品减少状态总框数达23775个其中Reduced框16205个、Empty-Space框7570个采用labelImg矩形框标注标注准确合理。目前已有380人学习下载可为货架缺货识别、智能补货提醒等任务提供现成的数据基础与类别参考。1. 货架空置检测4470 张双类标签数据集到底能解决什么超市货架上的空位看起来是个小事做起来才知道有多麻烦。理货员巡场靠肉眼一圈下来十几分钟漏检率还高装个摄像头做自动识别又发现公开数据集里几乎没有专门标注空位这个类别的。大多数检测数据集标的是商品、是人、是车没人认真标过这里本该有货但现在是空的。这个 4470 张、2 类标签、VOCYOLO 双格式的货架空置缺货检测数据集切入的正是这个缝隙。它适合三类人一是做零售巡检机器人或固定摄像头方案的算法工程师需要一个能直接训练、能快速验证 baseline 的数据起点二是做智能货架、无人零售柜的嵌入式开发者想评估在边缘设备上跑轻量检测模型的可行性三是做零售数据分析的产品侧同学想搞清楚缺货检测这件事在视觉层面到底能做到什么精度。数据集本身不解决业务闭环但它把最耗时的标注环节替你省掉了——4470 张真实货架场景图2 类标签VOC 和 YOLO 两种格式都给你拿到就能开跑。2. 数据集拆开看4470 张图、2 类标签、双格式的选型逻辑2.1 为什么是 2 类标签而不是更多货架场景里理论上可以标很多东西商品、价签、货架层板、空位、错位商品。但这个数据集只保留 2 类常见做法是empty空位/缺货区域和goods有货区域或者empty和shelf货架结构。2 类的好处很直接标注一致性高模型收敛快推理时后处理简单。如果你自己扩展类别比如把不同商品品类分开标标注成本会指数级上升而且类别间边界模糊——一包薯片和一包饼干在货架远景里可能只差几个像素。2 类标签的本质是把问题从识别什么商品降维成这里有没有货这对缺货检测这个业务目标来说信息量是够的。提示拿到数据集后先确认类别名和索引映射。VOC 的 XML 里类别是字符串YOLO 的 txt 里类别是整数索引两者必须对齐否则训练时会出现标签对但类别错的玄学问题。2.2 VOC 与 YOLO 格式的差异和转换验证VOC 格式每张图对应一个 XML标注信息是绝对坐标xmin, ymin, xmax, ymax。YOLO 格式每张图对应一个 txt每行是class_id x_center y_center width height全部归一化到 0~1。数据集同时提供两种格式省去了自己转的麻烦但你还是得验证一遍转换是否正确。下面这段脚本用来检查 VOC 和 YOLO 标注是否一一对应以及坐标是否在合理范围内import os import xml.etree.ElementTree as ET def parse_voc(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) boxes [] for obj in root.findall(object): name obj.find(name).text 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) boxes.append((name, xmin, ymin, xmax, ymax)) return w, h, boxes def parse_yolo(txt_path): boxes [] with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) 5: cid, xc, yc, bw, bh map(float, parts) boxes.append((int(cid), xc, yc, bw, bh)) return boxes # 遍历检查 voc_dir annotations_voc yolo_dir labels_yolo img_dir images for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue base xml_file.replace(.xml, ) xml_path os.path.join(voc_dir, xml_file) txt_path os.path.join(yolo_dir, base .txt) img_path os.path.join(img_dir, base .jpg) if not os.path.exists(txt_path): print(f缺失 YOLO 标签: {base}) continue if not os.path.exists(img_path): print(f缺失图片: {base}) continue w, h, voc_boxes parse_voc(xml_path) yolo_boxes parse_yolo(txt_path) if len(voc_boxes) ! len(yolo_boxes): print(f数量不匹配: {base}, VOC{len(voc_boxes)}, YOLO{len(yolo_boxes)}) continue for (name, xmin, ymin, xmax, ymax), (cid, xc, yc, bw, bh) in zip(voc_boxes, yolo_boxes): # 反归一化验证 xc_abs xc * w yc_abs yc * h bw_abs bw * w bh_abs bh * h xmin_calc xc_abs - bw_abs / 2 ymin_calc yc_abs - bh_abs / 2 if abs(xmin_calc - xmin) 2 or abs(ymin_calc - ymin) 2: print(f坐标偏差过大: {base}, VOC({xmin},{ymin}), YOLO反算({xmin_calc:.1f},{ymin_calc:.1f})) print(检查完成)这段脚本的逻辑是对每张图分别读 VOC XML 和 YOLO txt先检查文件是否存在、标注数量是否一致再把 YOLO 的归一化坐标反算回绝对坐标和 VOC 的坐标做对比。偏差阈值设 2 像素是因为标注时可能有取整误差。如果偏差超过 2 像素说明转换脚本有问题需要重新生成。参数说明voc_dir、yolo_dir、img_dir按你实际解压后的目录结构改。类别名到索引的映射需要你根据数据集自带的classes.txt或 README 确认脚本里没硬编码因为不同版本可能不同。2.3 数据分布先摸清再训练4470 张图不算多训练前必须看分布。至少统计三件事每类标注框的数量、每张图的平均框数、空位框的尺寸分布。空位检测有个特点空位区域的面积差异极大一个整层空掉可能是几千像素宽一个单品缺位可能只有几十像素。如果小目标占比高anchor 设置和输入分辨率都要调。import os import matplotlib.pyplot as plt yolo_dir labels_yolo class_counts {} box_areas [] for txt_file in os.listdir(yolo_dir): if not txt_file.endswith(.txt): continue with open(os.path.join(yolo_dir, txt_file), r) as f: for line in f: parts line.strip().split() if len(parts) 5: cid int(parts[0]) bw, bh float(parts[3]), float(parts[4]) class_counts[cid] class_counts.get(cid, 0) 1 box_areas.append(bw * bh) print(类别分布:, class_counts) print(框面积分位数:) for q in [0.1, 0.25, 0.5, 0.75, 0.9]: print(f {q}: {sorted(box_areas)[int(len(box_areas)*q)]:.4f}) plt.hist(box_areas, bins50) plt.xlabel(Normalized box area) plt.ylabel(Count) plt.title(Box area distribution) plt.show()如果中位数面积低于 0.01归一化后说明小目标多训练时输入尺寸建议不低于 640YOLO 的 anchor 可以用 k-means 重新聚类。如果大面积框占主导可以适当降低输入分辨率换速度。3. 用 YOLO 格式跑通第一个缺货检测 baseline3.1 目录结构整理与 data.yaml 配置YOLO 系列训练对目录结构有固定要求。常见做法是按images/train、images/val、labels/train、labels/val组织然后写一个data.yaml指向这些路径。4470 张图按 8:2 划分训练集约 3576 张验证集约 894 张。# 假设解压后原始目录为 raw/ mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val # 简单随机划分实际建议按场景分层避免同一货架出现在训练和验证集 python -c import os, random, shutil random.seed(42) imgs [f for f in os.listdir(raw/images) if f.endswith(.jpg)] random.shuffle(imgs) split int(len(imgs) * 0.8) for i, img in enumerate(imgs): src_img os.path.join(raw/images, img) src_lbl os.path.join(raw/labels, img.replace(.jpg, .txt)) if i split: shutil.copy(src_img, dataset/images/train/) shutil.copy(src_lbl, dataset/labels/train/) else: shutil.copy(src_img, dataset/images/val/) shutil.copy(src_lbl, dataset/labels/val/) print(划分完成) data.yaml内容path: ./dataset train: images/train val: images/val nc: 2 names: 0: empty 1: goodsnc是类别数names的顺序必须和 YOLO txt 里的整数索引一致。如果数据集自带的类别顺序不同以数据集为准改这里。3.2 训练命令与关键参数怎么设以 YOLOv8 为例最小训练命令yolo detect train \ datadataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/shelf \ namebaseline参数逐个说model选yolov8s而不是n是因为空位检测里小目标不少n 的容量可能不够imgsz640是平衡精度和速度的默认值如果显存够可以上 1280对小空位更友好batch16在 8GB 显存上比较稳不够就降到 8lr00.01是 SGD 的常见起点如果用 AdamW 可以降到 0.001patience20表示验证指标 20 轮不提升就早停避免过拟合。训练过程中重点看mAP50和mAP50-95。缺货检测业务上更关心召回率——漏检一个空位的代价比误检一个空位大因为漏检意味着缺货没被发现。如果 recall 偏低可以调低置信度阈值或者增加空位类别的样本权重。3.3 推理与结果可视化训练完拿验证集跑推理看实际效果yolo detect predict \ modelruns/shelf/baseline/weights/best.pt \ sourcedataset/images/val \ conf0.25 \ saveTrue \ projectruns/shelf \ nameval_predconf0.25是默认置信度阈值。实际部署时这个值要根据业务调如果希望尽量不漏降到 0.15如果误检太多升到 0.4。推理结果会保存在runs/shelf/val_pred下带标注框的图可以直接看。如果想批量统计每张图的空位数量可以用 Python 调from ultralytics import YOLO model YOLO(runs/shelf/baseline/weights/best.pt) results model(dataset/images/val, conf0.25, saveFalse) for r in results: empty_count sum(1 for c in r.boxes.cls if int(c) 0) print(f{r.path}: 空位数量{empty_count})这段代码遍历验证集统计每张图里empty类的检测框数量。业务上可以设阈值比如空位数量超过 3 就触发补货提醒。4. 避坑与排查货架空置检测训练里最容易翻车的 5 个点4.1 空位框标到了货架层板上现象模型把货架层板识别成空位推理图上层板被框出来。原因标注时把层板可见区域和缺货区域混为一谈。层板是结构空位是缺货两者视觉上都是没有商品但业务含义完全不同。解决重新检查标注规范空位框应该只覆盖本该有商品但缺失的区域层板本身不标。如果数据集里已经混了训练前用脚本过滤掉宽高比异常的框——层板通常极扁宽高比超过 10:1 的框大概率是层板。4.2 训练集和验证集出现同一货架现象验证集 mAP 很高但换一家超市的图推理效果断崖式下降。原因随机划分时同一货架的不同角度、不同时间照片被分到了训练和验证集模型记住了货架背景而不是空位特征。解决按货架 ID 或拍摄批次分层划分确保同一货架只出现在训练集或验证集之一。如果数据集没提供货架 ID可以用图像相似度聚类后再划分。4.3 小空位漏检严重现象大块空位检测很准单品缺位的小空位几乎检不出来。原因小目标在 640 分辨率下特征太弱YOLO 的 P3 特征图 stride 是 8一个 20 像素的框在特征图上只有 2~3 个像素。解决输入分辨率提到 1280或者用带 P2 层的模型变体。另外可以用 k-means 对空位框重新聚类 anchor让小 anchor 更匹配小空位。数据层面对小空位样本做 oversampling。4.4 类别不平衡导致空位类被压制现象goods类 mAP 正常empty类 mAP 很低模型倾向于把空位也预测成有货。原因有货区域通常比空位区域多得多类别不平衡让模型偏向多数类。解决在 loss 里给empty类更高权重或者用 focal loss。YOLOv8 可以在训练时通过cls参数调分类 loss 权重也可以简单地对含空位的图做重复采样。4.5 推理阈值照搬默认值现象训练指标好看部署后要么漏报多要么误报多。原因conf0.25是通用默认值不是为缺货检测调的。缺货检测的代价矩阵和通用检测不同。解决在验证集上画 precision-recall 曲线根据业务可接受的误报率反推阈值。如果业务要求召回率不低于 95%就找 recall0.95 时对应的 conf 值。这个值通常比 0.25 低不少。5. 从 baseline 到可用提升空位检测精度的几个实操技巧第一个技巧是用背景差分做数据增强。货架空位检测有个天然优势同一货架在不同时间的照片有货和缺货的差异就是空位。如果你能拿到同一货架的时序图可以做差分增强——把有货图的商品区域替换成空位图的对应区域生成更多空位样本。这个做法比随机裁剪、翻转更贴近真实分布。第二个技巧是后处理里加面积过滤和 NMS 调参。空位框通常不会重叠NMS 的 IoU 阈值可以设低一点比如 0.3避免相邻空位被合并。同时过滤掉面积过小的框——小于图像面积 0.1% 的框大概率是噪声。第三个技巧是用验证集做阈值搜索。不要凭感觉设 conf写个循环在验证集上扫from ultralytics import YOLO import numpy as np model YOLO(runs/shelf/baseline/weights/best.pt) confs np.arange(0.05, 0.55, 0.05) for c in confs: results model(dataset/images/val, confc, saveFalse, verboseFalse) tp, fp, fn 0, 0, 0 for r in results: pred_boxes r.boxes.xyxy.cpu().numpy() pred_cls r.boxes.cls.cpu().numpy() # 这里需要和 GT 对比简化写法只统计预测数量 empty_pred sum(1 for x in pred_cls if int(x) 0) # 实际应加载 GT 做匹配此处省略匹配逻辑 print(fconf{c:.2f}, 空位预测数{empty_pred})实际使用时要把预测框和 GT 框做 IoU 匹配算出每个阈值下的 precision 和 recall再选最优点。这个脚本的框架给你匹配逻辑用 pycocotools 或自己写 IoU 都行。第四个技巧是模型集成。如果单模型精度不够可以训两个不同 backbone 的模型比如 YOLOv8 和 RT-DETR推理时做加权框融合。空位检测的场景相对固定集成带来的提升通常比通用检测更明显。最后一个习惯每次改完标注或增强策略先在小验证集上跑 10 个 epoch 看趋势不要直接上 100 epoch。我自己的血泪经验是标注问题导致的精度瓶颈训再久也救不回来早发现早返工。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/11 20:43:40

三菱FX3U与信捷触摸屏三轴搬运程序:结构设计与调试全解析

1. 项目概述与整体设计思路1.1 立项背景与核心需求拆解我最早接触三轴搬运这个项目,是很多年前在车间做设备改造的时候。老实说,三轴搬运在工业现场属于最典型的“入门级自动化应用”——三台步进电机、一个PLC、一个触摸屏,就能完成从A点抓料…

2026/10/11 20:43:40

11124张足球运动员检测数据集:VOC与YOLO双格式选择与实战指南

简介:本资源为足球运动员检测数据集,面向计算机视觉方向的学习者、算法工程师及体育视频分析研究者,可用于目标检测模型的训练、验证与迁移学习实验,尤其适合需要同时使用Pascal VOC与YOLO两种标注格式的开发者。压缩包共2000个文…

2026/10/11 20:43:40

基于OpenCLIP知识蒸馏的零标签图像分类实战源码解析

简介:本资源面向计算机视觉方向的研究人员与开发者,提供一套基于OpenCLIP知识蒸馏实现零标签图像分类的完整项目源码,帮助在缺乏标注数据的场景下训练轻量级分类模型。压缩包共16个文件,约1.42MB,以9个Python脚本为核心…

2026/10/11 21:48:48

大模型时代的具身智能:VLA模型、本地部署与落地避坑指南

简介:这份《大模型时代的具身智能》PDF报告面向人工智能、机器人方向的研究者与学习者,系统梳理了具身智能从古至今的发展脉络与核心技术框架。内容从公元前9世纪偃师造人、阿基塔斯蒸汽飞鸟、达芬奇人形机器人草图讲起,串联1961年Unimate、1…

2026/10/11 21:48:48

基于MCP架构的YOLO远程训练系统:自然语言控制实战指南

简介:基于MCP架构的YOLO训练系统是一套面向目标检测开发者的分布式训练解决方案,核心亮点在于通过客户端-服务器模式将单机YOLO训练拆分为可协作的服务端与客户端组,并利用消息通信协议完成数据与指令传递。用户无需精通编程,可直…

2026/10/11 21:48:48

PC算法Python实战:条件独立检验、骨架学习与因果图构建

简介:一份用Python实现PC(Partial Correlation)算法的完整项目源码,直观展示条件独立检验与因果网络结构学习的核心过程,通过部分相关分析剔除间接关联、识别变量间的直接依赖关系,适合希望从理论与代码层面…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑