简介:面向工业质检与智能安防场景的传送带异物检测数据集,可识别铁棍、垃圾等常见异物,适合从事目标检测算法训练、产线异常监控及边缘部署的开发者与研究人员使用。资源包共108个文件,以105张jpg现场图像为主,另附3个json标注文件,采用COCO JSON格式组织,可直接对接主流检测框架进行模型训练与验证;压缩包整体约4.65MB,体积轻便,便于快速下载与本地调试。图像取自真实传送带监控画面,覆盖不同时段与异物形态,为小样本场景下的数据增强与迁移学习提供基础素材。目前已有413人学习下载,具备一定参考热度。读者可据此搭建异物检测基线模型,完成类别标注解析、训练集与验证集划分及推理效果评估,也可用于课程设计、毕业项目或产线视觉方案的原型验证。
1. 传送带异物检测数据集:从监控抽帧到 COCO JSON 的落地路径
工厂传送带最怕的不是停机,而是铁棍、垃圾这类异物混进物料流,轻则卡住滚筒撕裂皮带,重则打坏下游设备。很多做视觉检测的团队卡在第一步:没有贴合真实产线的标注数据。这份传送带异物检测识别数据集,采集自 NVR 监控通道的连续录像抽帧,文件名里带着NVR_ch1_main_20220912160000_20220912170000这样的时间戳和帧序号,说明它是按小时段切分、逐帧导出的真实工业画面。标注采用 COCO JSON 格式,可识别铁棍、垃圾两类目标,适合直接喂给 YOLOv8、MMDetection、Detectron2 这类主流框架做训练和微调。如果你正在做异物检测、数据标注或工业质检方向,这份资源能省掉最耗时的采集与标注环节,把精力留给模型调优和部署。
2. COCO JSON 标注结构拆解:字段含义与传送带场景的适配点
拿到一份 COCO JSON,第一件事不是急着转 YOLO,而是先读懂它到底标了什么、怎么标的。传送带异物检测这个场景有它的特殊性:目标小、背景纹理重复、异物和物料颜色接近,标注质量直接决定模型上限。这一章把 JSON 结构拆开,讲清楚每个字段在传送带语境下意味着什么。
2.1 images、annotations、categories 三张表的关系
COCO JSON 顶层是三个核心数组:images记录每张图的文件名、宽高和唯一id;annotations记录每个标注框的image_id、category_id、bbox和area;categories定义类别名和id的映射。三者靠 id 关联,缺一不可。
{ "images": [ { "id": 1, "file_name": "NVR_ch1_main_20220912160000_20220912170000-dav_005736-161_jpg.rf.e455dd23d6c7730174a7cc32f9f18ced.jpg", "width": 1920, "height": 1080 } ], "annotations": [ { "id": 1, "image_id": 1, "category_id": 1, "bbox": [820, 430, 96, 210], "area": 20160, "iscrowd": 0 } ], "categories": [ {"id": 1, "name": "iron_rod", "supercategory": "foreign_object"}, {"id": 2, "name": "garbage", "supercategory": "foreign_object"} ] }bbox是[x, y, width, height],原点在左上角,单位是像素。area是框面积,训练时用于区分大小目标。iscrowd为 0 表示单实例标注,传送带异物一般不会出现密集遮挡,这个值基本恒为 0。categories里的supercategory可以统一写成foreign_object,方便后续扩展更多异物类别。
2.2 传送带场景下 bbox 标注的三个适配点
第一,异物形态差异大。铁棍通常是细长条,宽高比可能到 1:8 甚至更极端;垃圾则形状不规则,边界模糊。标注时铁棍要贴紧两端,垃圾要框住可见主体,不要为了凑面积把背景框进去。
第二,监控画面有畸变和压缩噪声。NVR 抽帧的 JPG 经过 H.264 压缩,边缘有块效应,标注框如果压到目标边缘,训练时模型容易学到压缩伪影。常见做法是框比目标实际边界外扩 2 到 4 像素,给噪声留余量。
第三,时间戳文件名隐含帧间关系。同一小时段内dav_005736-161和dav_005820-642是不同帧,如果做视频级检测,可以按文件名前缀分组,避免同一段连续帧被随机划分到训练集和验证集造成数据泄漏。
2.3 用 pycocotools 做一次标注体检
在训练之前,先跑一遍统计,确认没有空图、没有越界框、类别分布是否失衡。
from pycocotools.coco import COCO import collections coco = COCO("annotations/instances_train.json") # 统计每类标注数量 cat_counter = collections.Counter() for ann in coco.dataset["annotations"]: cat_counter[ann["category_id"]] += 1 for cid, name in [(c["id"], c["name"]) for c in coco.dataset["categories"]]: print(f"{name}: {cat_counter[cid]} 个标注") # 检查越界框 img_map = {img["id"]: img for img in coco.dataset["images"]} bad = 0 for ann in coco.dataset["annotations"]: img = img_map[ann["image_id"]] x, y, w, h = ann["bbox"] if x < 0 or y < 0 or x + w > img["width"] or y + h > img["height"]: bad += 1 print(f"越界框数量: {bad}")这段脚本输出两个关键指标:类别标注数和越界框数。如果铁棍和垃圾数量差距超过 5:1,训练时建议开类别权重或者对少数类做过采样。越界框必须清零,否则转 YOLO 格式时会直接报错或产生负坐标。
注意:pycocotools 在 Windows 上安装偶尔需要编译工具链,如果
pip install pycocotools失败,可以换pycocotools-windows或者直接用 conda 装。
3. 从 COCO JSON 转 YOLO 格式:转换脚本与四个边界坑
COCO 是通用标注格式,但实际训练时 YOLOv8 吃的是每张图一个.txt的归一化坐标。这一章给出一份可直接跑的转换脚本,并把转换过程中最容易翻车的四个边界情况讲透。
3.1 转换脚本:COCO 到 YOLO txt
import json import os from pathlib import Path def coco_to_yolo(json_path, img_dir, out_dir): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) # 建立 image_id -> 图片信息 的映射 img_map = {img["id"]: img for img in data["images"]} # 建立 category_id -> 连续索引 的映射(YOLO 要求从 0 开始) cat_ids = sorted([c["id"] for c in data["categories"]]) cat2idx = {cid: i for i, cid in enumerate(cat_ids)} os.makedirs(out_dir, exist_ok=True) # 按 image_id 聚合标注 ann_by_img = {} for ann in data["annotations"]: ann_by_img.setdefault(ann["image_id"], []).append(ann) for img_id, img_info in img_map.items(): w, h = img_info["width"], img_info["height"] lines = [] for ann in ann_by_img.get(img_id, []): x, y, bw, bh = ann["bbox"] # 归一化并转为中心点坐标 cx = (x + bw / 2) / w cy = (y + bh / 2) / h nw = bw / w nh = bh / h # 裁剪到 [0,1],防止越界 cx, cy = min(max(cx, 0), 1), min(max(cy, 0), 1) nw, nh = min(max(nw, 0), 1), min(max(nh, 0), 1) cls = cat2idx[ann["category_id"]] lines.append(f"{cls} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}") stem = Path(img_info["file_name"]).stem with open(os.path.join(out_dir, stem + ".txt"), "w") as f: f.write("\n".join(lines)) coco_to_yolo( "annotations/instances_train.json", "images/train", "labels/train" )逻辑说明:先把category_id重映射成从 0 开始的连续整数,因为 YOLO 的类别索引必须连续,COCO 里如果 id 是 1 和 2,直接拿来用会多一个空类。然后按image_id聚合标注,逐图生成 txt。坐标转换公式是中心点 = (左上角 + 宽高/2) / 图像尺寸,这是 YOLO 的标准格式。
参数说明:json_path是 COCO 标注文件路径,img_dir是图片目录(脚本里其实只用了文件名,没读图,但保留参数方便扩展),out_dir是 txt 输出目录。归一化后保留 6 位小数足够,再多是浪费。
3.2 四个边界坑:空图、越界、类别映射、文件名匹配
空图坑:COCO 里有些图可能没有任何标注,转换后会生成空 txt。YOLO 训练时空 txt 是合法的负样本,但如果你的数据集里空图比例过高,模型会偏向预测背景。建议统计一下空图比例,超过 20% 就考虑剔除一部分。
越界坑:监控抽帧的标注偶尔会有框超出图像边界,归一化后出现负数或大于 1 的值。脚本里做了裁剪,但裁剪会改变框的实际覆盖区域。更稳妥的做法是在标注阶段就修正,转换时只做检测和报警,不静默裁剪。
类别映射坑:如果 COCO 的categories里 id 不连续,比如只有 1 和 3,直接当索引用会出错。脚本里用sorted加枚举重建映射,保证从 0 开始连续。转换后建议打印一次cat2idx,确认铁棍和垃圾对应的索引和你训练配置里的names一致。
文件名匹配坑:COCO 的file_name可能带路径前缀,而 YOLO 要求 txt 和图片同名同目录结构。脚本用Path(file_name).stem取纯文件名,但如果你的图片实际存放路径和 JSON 里的file_name不一致,需要手动对齐。常见做法是转换后跑一遍校验:遍历 labels 目录,确认每个 txt 都能在 images 目录找到同名图片。
3.3 转换后的校验脚本
from pathlib import Path img_dir = Path("images/train") lbl_dir = Path("labels/train") img_stems = {p.stem for p in img_dir.glob("*.jpg")} lbl_stems = {p.stem for p in lbl_dir.glob("*.txt")} print(f"图片数: {len(img_stems)}, 标签数: {len(lbl_stems)}") print(f"有图无标签: {len(img_stems - lbl_stems)}") print(f"有标签无图: {len(lbl_stems - img_stems)}")输出里「有标签无图」必须为 0,否则训练时 YOLO 会找不到图片直接报错。「有图无标签」是负样本,数量可控就没问题。
4. 用这份数据集训练 YOLOv8:配置、参数与显存控制
数据转好了,接下来是训练。这一章以 YOLOv8 为例,给出从 data.yaml 到启动命令的完整配置,并说明传送带异物检测场景下几个关键参数怎么设。
4.1 data.yaml 与目录结构
YOLOv8 要求固定的目录结构,图片和标签分开放,路径在 yaml 里声明。
path: /data/conveyor_foreign train: images/train val: images/val test: images/test names: 0: iron_rod 1: garbagepath是数据集根目录,train/val/test是相对路径。names的顺序必须和转换脚本里的cat2idx一致,铁棍是 0,垃圾是 1。如果顺序反了,模型会把铁棍预测成垃圾,指标看着还行但实际全错。
目录结构建议这样组织:
conveyor_foreign/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml4.2 训练命令与关键参数
yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=30 \ project=runs/conveyor \ name=exp01参数说明:model选yolov8s而不是n,因为传送带异物目标偏小,s 的特征提取能力更稳,显存够就上 s 或 m。imgsz=640是默认值,如果铁棍在画面里只占几十像素,可以提到 960 或 1280,但显存占用会翻倍。batch=16在 8G 显存上跑 640 尺寸基本安全,12G 可以上 32。patience=30表示 30 轮验证指标不提升就早停,避免过拟合。lr0=0.01是初始学习率,如果 loss 震荡厉害,降到 0.005。
4.3 小目标检测的增强策略
传送带上的铁棍往往又细又长,默认增强可能不够。在训练配置里可以加几项针对性增强:
yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=960 \ batch=8 \ mosaic=1.0 \ scale=0.5 \ copy_paste=0.3 \ project=runs/conveyor \ name=exp02_smallmosaic=1.0是默认开启的四图拼接,对小目标友好。scale=0.5允许随机缩放,增强尺度鲁棒性。copy_paste=0.3是分割任务常用的增强,检测任务里也能用,把目标复制粘贴到其他位置,增加小目标出现频率。注意imgsz提到 960 后batch要相应降到 8,否则显存溢出。
提示:训练前先用
yolo detect train ... epochs=1跑一轮,确认数据加载和类别映射没问题,再开完整训练。这一步能省掉几小时的无效等待。
5. 避坑与排查:标注、转换、训练里最容易翻车的五件事
这一章把实际做异物检测项目时踩过的坑集中列出来,每条按现象、原因、解决写,方便对照排查。
现象一:训练 loss 正常下降,但验证集 mAP 始终在 0.1 以下。原因:类别映射错位。COCO 的category_id和 YOLO 的类别索引没对齐,模型学的是错的标签。 解决:转换后打印cat2idx,和 data.yaml 的names逐行核对。再抽查几张图的 txt,用可视化脚本把框画回原图,确认铁棍框在铁棍上。
现象二:模型把传送带边缘的阴影预测成铁棍。原因:标注时把传送带边缘的暗纹也框进去了,或者负样本不足。 解决:回查标注,剔除误标;同时增加不含异物的纯背景图作为负样本,比例控制在总数据的 10% 到 15%。
现象三:转换脚本报KeyError: image_id。原因:annotations 里引用了 images 中不存在的image_id,通常是标注文件被手动改过或合并时出错。 解决:在转换脚本开头加校验,遍历 annotations 检查image_id是否都在img_map里,不在就打印出来人工处理。
现象四:训练到一半显存溢出(CUDA out of memory)。原因:imgsz或batch设太大,或者 dataloader 的workers过多导致内存泄漏。 解决:先把batch减半,再降imgsz。workers在 Windows 上建议设 0 或 2,Linux 上可以设 8。如果还溢出,用yolo detect train ... amp=False关掉混合精度试试。
现象五:验证集指标很好,但实际产线部署后漏检严重。原因:训练集和验证集来自同一小时段的连续帧,数据泄漏导致指标虚高。 解决:按时间段划分数据集,比如用 16:00 到 17:00 的帧做训练,17:00 到 18:00 的帧做验证,确保验证集的时间段和训练集不重叠。文件名里的时间戳就是天然的分组依据。
6. 进阶技巧:用时间戳做数据划分与难例挖掘
这份数据集的文件名里藏着一条容易被忽略的信息:NVR_ch1_main_20220912160000_20220912170000明确标出了录像的小时段。用这个时间戳做数据划分,比随机划分更贴近真实部署场景,也能避免连续帧泄漏。我一般会按小时段分组,前几个小时的帧做训练,中间小时做验证,最后小时做测试。这样验证集上的指标更接近产线实际表现,不会出现「训练时 0.95,上线后 0.6」的落差。
具体操作上,先用脚本按文件名前缀分组:
import re from pathlib import Path from collections import defaultdict img_dir = Path("images/all") groups = defaultdict(list) for p in img_dir.glob("*.jpg"): # 提取时间戳段,如 20220912160000_20220912170000 m = re.search(r"(\d{14}_\d{14})", p.name) if m: groups[m.group(1)].append(p.name) for ts, files in sorted(groups.items()): print(f"{ts}: {len(files)} 张")输出会告诉你每个小时段有多少帧。然后按时间段分配:取前 60% 的小时段做训练,中间 20% 做验证,最后 20% 做测试。如果某个小时段帧数特别少,可以合并相邻时段。
难例挖掘是另一个提分点。训练完第一版模型后,用它在验证集上推理,把漏检和误检的图挑出来,人工重新标注或修正,再加入训练集做第二轮微调。传送带场景的难例通常是:铁棍和传送带边缘平行导致混淆、垃圾和物料颜色接近、监控画面过曝或过暗。把这些难例喂回去,第二轮 mAP 通常能涨 3 到 8 个点。
还有一个习惯:每次转换完数据,我都会用yolo detect val跑一遍预训练模型,看 baseline 指标。如果 baseline 低得离谱,说明数据或配置有问题,先排查再训练,别急着调参。从那以后我每次拿到新数据集,都强制走一遍「统计 → 转换 → 校验 → baseline」四步,省下的返工时间比调参多得多。希望帮到你。
本文还有配套的精品资源,点击获取