传送带异物检测数据集实战:从COCO JSON到YOLO训练

发布时间:2026/9/23 20:45:36
传送带异物检测数据集实战:从COCO JSON到YOLO训练 简介这是一套面向工业传送带异物检测任务的目标检测数据集适合计算机视觉算法工程师、科研人员及高校相关专业学生用于模型训练与效果验证。数据集标注了铁棍、垃圾两类异物全部采用COCO JSON格式能直接接入主流检测框架。zip压缩包共108个文件包括105张JPG原始图像和3个JSON标注文件整体大小仅4.65MB下载和调试都很便捷。从内容预览看图像多为监控视角下的产线画面贴近实际运行场景。目前已有413人浏览学习对于需要快速验证异物检测方案的研究者来说借助这批数据可低成本构建初版模型并可在其基础上扩充样本、微调参数从而提升检测精度降低人工巡检成本。1. 传送带异物检测数据集先搞清楚它是什么再决定怎么用做工业视觉的人都知道传送带异物检测最缺的不是模型而是带标注的现场数据。生产线不能停皮带上的铁棍、垃圾又不会按你的拍摄计划出现一帧一帧从录像里抠图、画框、标类别一个工人标一天也出不了几百张。这份数据集的价值正在这里它是从NVR监控录像里按时间切片抽出来的真实帧标注成COCO JSON格式目标就是铁棍和垃圾两类异物。拿到手之后你不需要再对着空白标注软件发呆可以直接做数据检查、拆分和模型训练。适合谁用准备在产线上快速验证YOLO或Faster R-CNN异检方案的人以及想用真实工业视频帧做算法预研的工程师。它解决的核心问题是帮你跳过采集和标注这两个最耗时的环节直接进入训练和调优。2. 读懂这份数据的底细从文件名到 COCO JSON 的完整拆解2.1 文件命名里的时间线索这是一批来自 NVR 的时序连续帧把文件名拆开看信息量比想象中大。以NVR_ch1_main_20220912160000_20220912170000-dav_005820-642_jpg.rf.b35c48b4...jpg为例NVR_ch1_main表示这是 NVR 录像机的第 1 通道主码流20220912160000_20220912170000是录像的起止时间段也就是 2022 年 9 月 12 日 16:00 到 17:00dav_005820-642是 DAV 录像文件内部的序号与帧号偏移最后那个.rf.后缀则是 Roboflow 平台导出数据集时自动追加的标识。这段命名告诉你三件事。第一数据不是人工摆拍的是真实产线监控画面光照、遮挡、皮带速度都是实际工况。第二同一根铁棍或同一块垃圾在连续帧里会出现多次这意味着如果把时间上相邻的帧同时分进训练集和验证集验证指标会被严重高估。第三.rf.后缀说明这份数据可能经过 Roboflow 平台的某种预处理或导出流程图片尺寸和标注坐标是否被改动过需要和 JSON 里的宽高字段核对。我一般拿到数据后的第一步不是看标注而是按文件名里的时间戳把图片排序画一条时间线看看异物目标在帧间的位移规律。铁棍是长条刚体在皮带上通常沿运动方向平移垃圾则可能是任意形状运动轨迹也不稳定。这个区别直接影响后面要不要做切片推理以及用什么尺寸的锚框。2.2 COCO JSON 的字段结构images、annotations、categories 三张表怎么对应COCO JSON 是整个数据集的骨架它的结构是标准的字典嵌套。顶层有三个关键字段images是图片信息列表每条记录包含id、file_name、width、heightannotations是标注框列表每条包含id、image_id、bbox、area、category_id、iscrowdcategories是类别表通常就id和name两个字段。数据集的categories应该只有两类铁棍和垃圾id一般从 1 开始。bbox的格式是[x, y, width, height]注意是左上角坐标加宽高不是中心点坐标这是新手最容易搞混的地方。area字段的值必须和bbox算出的面积一致很多导出工具会在这个字段上出问题训练时如果检测框架会用area做小目标筛选数值错了会导致一批目标被静默丢弃。import json from pathlib import Path def inspect_coco(coco_path: Path): with open(coco_path, r, encodingutf-8) as f: data json.load(f) cat_map {c[id]: c[name] for c in data[categories]} print(类别映射:, cat_map) img_count len(data[images]) ann_count len(data[annotations]) print(f图片数: {img_count}, 标注框数: {ann_count}) per_cat {} for ann in data[annotations]: name cat_map.get(ann[category_id], 未知) per_cat[name] per_cat.get(name, 0) 1 for name in cat_map.values(): print(f类别 {name}: {per_cat.get(name, 0)} 个框) # 抽查前三条 bbox验证坐标格式 for ann in data[annotations][:3]: x, y, w, h ann[bbox] print(fbbox[{x:.1f}, {y:.1f}, {w:.1f}, {h:.1f}], area{ann[area]:.1f}, fcompare{w*h:.1f}, cat{cat_map.get(ann[category_id])}) if __name__ __main__: inspect_coco(Path(annotations/instances_default.json))这段脚本的作用是快速摸清数据集的底总共多少张图、多少个框、类别是否均衡。cat_map.get(ann[category_id], 未知)这行是防御性写法防止 JSON 里出现了类别表之外的 ID这在手工整理过的数据集里很常见。最后那个抽查 bbox 的循环是为了确认area字段和w*h是否基本一致如果差得离谱说明标注导出时坐标或尺寸被改过后续训练前要整体重新算一遍area。2.3 文本标注文件里的隐藏信息文件名乱码与坐标系变换的坑b35c48b41b3287406e861ab231bdeeb7这种十六进制串是 Roboflow 给每张图片生成的内容哈希用来保证文件唯一性不用管它。真正要注意的是图片尺寸。NVR 主码流常见分辨率是 1920x1080 或者 2560x1440但如果 Roboflow 导出时做过 resize图片会被缩放到 640x640 或 416x416标注坐标也跟着变了。查看图片实际分辨率要和 JSON 里images字段的width、height对齐不一致的话训练时图像加载会报维度错误。另一个隐藏坑是 EXIF 旋转。手机或某些摄像头拍的图片带 EXIF 方向信息监控帧一般没有但经过 Roboflow 管道后可能会被写进去。如果图片被旋转而标注框没跟着转框就全偏了。检查方法很简单用 OpenCV 读图后确认shape再用cv2.imwrite另存一张手动看目标是不是还在框里。import cv2 import numpy as np def verify_image_size(image_path: str, expected_w: int, expected_h: int): img cv2.imread(image_path, cv2.IMREAD_COLOR) if img is None: print(f无法读取: {image_path}) return False h, w img.shape[:2] if w ! expected_w or h ! expected_h: print(f尺寸不一致: {image_path}, 实际 {w}x{h}, JSON 记录 {expected_w}x{expected_h}) return False print(f尺寸校验通过: {image_path}, {w}x{h}) return True这个函数做的是“图片实际形状 vs JSON 记录形状”的硬校验。代码逻辑不复杂但它能拦截一批导出过程中尺寸被改、但标注没同步更新的坏数据。处理这种情况常见做法是统一把所有图片 resize 到固定尺寸同时等比例缩放 bbox 坐标不能只改图不改框。3. 训练前的数据准备拆分、抽样与标注质量校验3.1 按视频时间段拆分数据而不是随机打乱前面提到文件名里带时间戳这直接决定了数据拆分方式。如果直接把所有图片随机分成 train / val / test那么同一段视频里前后几帧的同一根铁棍可能同时出现在训练集和验证集这属于数据泄漏会让验证集 mAP 虚高真实场景上线的效果要打折扣。拆分原则是按视频切片隔离。20220912160000_20220912170000这 16:00-17:00 的一段录像作为一个整体单位要么整段进训练集要么整段进验证集。这样验证集里出现的异物图像和训练集完全不连续评估结果才贴近真实情况。import json import random from collections import defaultdict def split_by_video_segment(coco_path: str, val_ratio: float 0.15, seed: int 42): with open(coco_path, r, encodingutf-8) as f: data json.load(f) # 从文件名提取视频时间段作为分组键 segment_map defaultdict(list) for img in data[images]: fname img[file_name] # 文件名格式 NVR_ch1_main_20220912160000_20220912170000-dav_... seg_key fname.split(-dav_)[0] segment_map[seg_key].append(img[id]) # 按时间段随机划分 segments list(segment_map.keys()) random.seed(seed) random.shuffle(segments) val_segments set(segments[:max(1, int(len(segments) * val_ratio))]) train_ids set() val_ids set() for seg, ids in segment_map.items(): if seg in val_segments: val_ids.update(ids) else: train_ids.update(ids) train_data {k: v for k, v in data.items() if k ! annotations} val_data {k: v for k, v in data.items() if k ! annotations} train_data[images] [img for img in data[images] if img[id] in train_ids] val_data[images] [img for img in data[images] if img[id] in val_ids] train_data[annotations] [ann for ann in data[annotations] if ann[image_id] in train_ids] val_data[annotations] [ann for ann in data[annotations] if ann[image_id] in val_ids] print(f训练集: {len(train_data[images])} 张图, {len(train_data[annotations])} 个框) print(f验证集: {len(val_data[images])} 张图, {len(val_data[annotations])} 个框) with open(train.json, w, encodingutf-8) as f: json.dump(train_data, f, ensure_asciiFalse) with open(val.json, w, encodingutf-8) as f: json.dump(val_data, f, ensure_asciiFalse) if __name__ __main__: split_by_video_segment(instances_default.json)注意train_data {k: v for k, v in data.items() if k ! annotations}这行它把categories等顶层字段保留下来只把annotations拆开。这样生成的 train.json 和 val.json 结构完整检测框架可以直接读。segment_map用-dav_前面的部分做 key因为在文件名里-dav_前后的内容是稳定的这个拆分键对这份数据集有效。3.2 标注质量的三层检查漏标、错位和越界时序帧标注最常见的问题是漏标。一帧图里皮带上明明有根铁棍但 JSON 里没对应框。原因大多是标注员翻页太快或者异物在画面边缘只露出一截。漏标的数据喂给模型等于告诉它“这个位置是背景”训练出来的模型会在现场漏检同类目标。漏标检查可以做粗略的聚类分析。把 bbox 坐标归一化后画散点图如果发现某个区域完全没有框但皮带轨迹必须经过那个区域大概率是漏标了。另一个更实用的方法是连续帧对比同一段视频里异物在相邻帧的 bbox 中心点位移应该平滑如果第 N 帧有框、第 N1 帧突然消失、第 N2 帧又出现那中间那帧大概率漏标。import json import glob import os def find_wrong_size_anns(coco_path: str, img_dir: str, min_side: int 8): with open(coco_path, r, encodingutf-8) as f: data json.load(f) img_id_to_info {img[id]: img for img in data[images]} check_list [] for ann in data[annotations]: x, y, w, h ann[bbox] if min(w, h) min_side: img_info img_id_to_info.get(ann[image_id]) check_list.append({ image: img_info[file_name] if img_info else 未知, bbox: ann[bbox], area: ann[area] }) print(f发现 {len(check_list)} 个疑似过小目标 (最短边 {min_side}px):) for item in check_list[:10]: print(item) find_wrong_size_anns(instances_default.json, images)这里min_side8是经验值。传送带监控画面里一根铁棍如果长度小于 8 像素人眼都难分辨标注框基本是点训练时这类样本只会增加噪声。过小的框可以保留也可以过滤取决于你的现场检测距离但一定要知道它们存在。处理这类问题建议先过滤再训练后面生产环境如果真的需要检测极小目标用切片推理方案更稳妥。3.3 用 CVAT 或 x-anylabeling 复核标注别只信 JSONJSON 文件本身不会说谎但它只反映标注结果的静态状态无法体现“这个框是谁标的、当时怎么想的、为什么这么画”。复核标注最有效的办法是打开标注工具把 JSON 导进去逐帧过。CVAT 的优点是对 COCO JSON 兼容性好导入后能直接看框、改框、补漏标而且支持多人协作x-anylabeling 我对它的评估是更适合单人快速复核它带自动标注辅助可以用已有的检测模型先预标一遍人工只改错的地方。复核时可以只看难受的地方框是否紧贴目标轮廓、是否把皮带边缘的反光当成了异物、是否同一个铁棍被拆成两个框。铁棍这类长条物体的 bbox 长宽比通常在 3:1 以上如果大量标注框接近正方形说明标注的时候框画粗了。这类标注误差虽然不致命但会拉高定位 loss影响收敛速度。4. 从 COCO JSON 到模型训练格式转换与参数选择4.1 COCO 格式不用转成 YOLO 也能训两套方案的取舍拿到 COCO JSON 后思路有两条。第一条是用 Detectron2 或 MMDetection 这类原生支持 COCO 格式的框架直接训优点是不用转换加载即用缺点是环境配置对新手不友好Detectron2 要编译 CUDA 算子MMDetection 要装一堆依赖。第二条是把 COCO 转成 YOLO 格式用 YOLOv8 这类工具链成熟的框架训优点是命令简单、训练快、部署方便缺点是要写转换脚本并且转换过程中容易出坐标系错误。我一般推荐第二条路不仅因为 YOLO 系列的工业落地生态更完整还因为这份数据的异物尺寸偏小YOLOv8 的imgsz参数和增强策略调节起来比 Detectron2 直观。转换脚本的要点是把 COCO 的[x, y, w, h]转成 YOLO 的归一化[cx, cy, w, h]并确保类别 ID 从 0 开始连续编号。import json from pathlib import Path def coco_to_yolo(coco_path: Path, output_dir: Path): with open(coco_path, r, encodingutf-8) as f: data json.load(f) # 建立类别 ID 映射YOLO 要求从 0 开始 cat_id_map {} for i, cat in enumerate(data[categories]): cat_id_map[cat[id]] i img_id_to_info {img[id]: img for img in data[images]} anns_by_img {} for ann in data[annotations]: anns_by_img.setdefault(ann[image_id], []).append(ann) labels_dir output_dir / labels labels_dir.mkdir(parentsTrue, exist_okTrue) for img_id, anns in anns_by_img.items(): img_info img_id_to_info[img_id] w, h img_info[width], img_info[height] if w 0 or h 0: continue txt_path labels_dir / (Path(img_info[file_name]).stem .txt) lines [] for ann in anns: x, y, bw, bh ann[bbox] cx (x bw / 2) / w cy (y bh / 2) / h bw_norm bw / w bh_norm bh / h yolo_cat cat_id_map.get(ann[category_id]) if yolo_cat is None: continue lines.append(f{yolo_cat} {cx:.6f} {cy:.6f} {bw_norm:.6f} {bh_norm:.6f}) if lines: txt_path.write_text(\n.join(lines), encodingutf-8) # 生成类别文件 with open(output_dir / classes.txt, w, encodingutf-8) as f: for cat in data[categories]: f.write(cat[name] \n) print(f转换完成标签文件输出到 {labels_dir}) coco_to_yolo(Path(train.json), Path(yolo_dataset))代码里有几个细节值得强调。第一类别 ID 映射是必须的因为原始 COCO JSON 的类别 ID 可能从 1 开始YOLO 要求从 0 开始不对齐的话模型会少算一类训出来的结果全偏。第二归一化时用的是图片的实际宽高如果图片被 resize 过必须用 resize 后的宽高。第三bw_norm和bh_norm是浮点数保留 6 位小数是为了防止精度损失这个精度在训练中足够了。4.2 训练参数怎么定小目标场景下的 imgsz、anchor 与增强策略模型选 YOLOv8s 还是 YOLOv8m取决于你的推理硬件。传送带异物检测通常部署在工控机或边缘盒子算力有限建议先用 YOLOv8s 跑通流程如果精度不够再换大模型。参数设定上imgsz是第一个要改的。NVR 原图是 1920x1080如果直接缩到 640x640铁棍这种长条形小目标很容易丢失细节。常见做法是imgsz1280对长条目标容忍度高一些但训练速度会明显下降。如果你发现显存不够可以用imgsz960作为中间值。提交训练命令前我的习惯是先手动抽查几张图确认异物在 960 分辨率下仍然像素清晰。anchor 方面YOLOv8 有自动锚框计算机制通常在训练开始时会自动跑一遍 k-means 来适配数据集。但如果数据集中铁棍的长宽比特别极端比如 1:8自动计算出的锚框可能还是不够贴合这时候需要手动指定anchor参数或者用 2x 的rect模式训练避免大量白边填充。yolo detect train \ modelyolov8s.pt \ datadataset.yaml \ imgsz1280 \ batch8 \ epochs100 \ lr00.005 \ augmentTrue \ patience20 \ device0 \ projectconveyor_det \ nameexp1参数说明batch8在 1280 分辨率下差不多占 12GB 显存如果你只有 8GB 显存降到batch4。lr00.005是迁移学习场景下比较稳的初始学习率太大的话 COCO 预训练权重会被快速破坏。patience20表示连续 20 轮验证集指标不提升就提前停止可以省时间。训练前务必确认dataset.yaml里path字段指向正确train和val分别是图片目录路径nc是类别数 2names要写成[iron_bar, trash]这种和标注类别一致的名字。4.3 类别不平衡怎么处理垃圾多铁棍少别让模型只学会一种从实际经验看这类数据集普遍存在类别不平衡垃圾样本可能占比 80% 以上铁棍只在特定时间段出现。直接训练的结果是模型对垃圾的召回率很高铁棍经常漏检但总体 mAP 看起来还行这就把问题藏住了。处理不平衡的套路有两个。第一个是 loss 加权在 YOLOv8 里通过cls参数控制分类 loss 权重稀有类别权重调高。第二个是数据增强对铁棍样本做随机旋转、缩放、平移的过采样。还有一种更符合本数据特点的做法因为铁棍大都是沿传送带方向运动的可以通过调整 Mosaic 增强的拼接方式让同一批次里多出现含有铁棍的图。# dataset.yaml 参考配置 path: /data/conveyor_det train: images/train val: images/val nc: 2 names: 0: iron_bar 1: trash # 在 data.yaml 同级目录建 hyp.yaml 手动改增强参数 # hyp.yaml hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 degrees: 0.0 translate: 0.1 scale: 0.5 fliplr: 0.5 mosaic: 1.0 mixup: 0.2degrees0.0是刻意设置的。传送带上异物相对摄像头基本都是水平姿态旋转增强会让模型学到没有意义的角度变化反而增加训练负担。fliplr0.5水平翻转是安全的传送带左右两端在画面上等效。mixup0.2对目标重叠的图像混合有助于模型在异物互相遮挡时保持判别能力但比例太高会让小目标更难学。5. 避坑传送带异物检测最常见的五个问题5.1 验证集 mAP 很高现场误检却接连不断现象训练时 val mAP 能到 0.85 以上部署到现场后皮带上的污渍、水渍、甚至光照反光都被检测成垃圾误检率 30% 往上。原因数据泄漏加背景欠采样。数据泄漏是拆分方式不对随机拆分导致训练集和验证集存在同一段视频的相邻帧模型记住了目标的位置而不是特征成绩虚高。背景欠采样是标注数据里只有异物目标皮带、托辊、地面这些背景样板不足模型把背景里的纹理误判成目标。解决首先严格按视频时间段重拆数据用第 3.1 节的代码重新划分。其次在训练集中单独加一批背景图也就是没有异物的纯皮带图片配上空的标签文件让模型学会“没有目标就说没有”。现场部署时发现误检优先收集误检样本回灌训练集这是最直接有效的闭环。5.2 同一根铁棍被重复标注训练后模型输出一堆重叠框现象推理时一个长条铁棍上叠了 4 到 5 个检测框NMS 压不掉。原因数据标注时没做实例分割级别的严格框选。铁棍长度跨越 400px标注员可能从一个完整目标上拆出几个框每个框只标一部分。模型学到的是“铁棍的一部分就是铁棍”推理时对同一目标的不同部位同时输出多个高置信度框。解决检查标注数据中重叠框比例用脚本计算同一image_id下 bbox 的 IoUIoU 大于 0.5 的框对要人工复核。这类问题需要在标注阶段统一规范一个完整铁棍标一个框哪怕目标很长。训练参数方面可以把 NMS IoU 阈值从默认的 0.5 调高到 0.7减少相邻框被误合并的概率。5.3 JSON 里 area 和 bbox 对不上训练时内存直接溢出现象用 mmdetection 训练时跑到一半报ValueError: area must be positive或者数据加载时内存异常。原因area字段算出来是 0 或负数。Roboflow 导出时如果标注框坐标出现了x超出图片宽度、width为 0 这类脏数据area也跟着坏。很多框架的CocoDataset会拿area做目标尺度分组数值非法就崩溃。解决在数据进入训练管线前统一用脚本清洗。检查width 0 and height 0x width img_widthy height img_height不符合的框要么删除要么按图片边界裁剪。清洗后再用area w * h重算一遍。这个步骤不能省尤其当数据集经过多种工具流转的时候。5.4 小目标铁棍在缩略图上看不见标注质量无法保证现象复查标注时发现一批铁棍框画得很随意框边界和目标边界差了十几像素甚至框只盖住了目标的一半。原因标注工具里为了看清全图把画面缩小了铁棍在缩略图上就 3、4 个像素标注员很难精确贴合目标边缘。异步帧标注时还会出现目标已经移动、但标注员凭记忆画框的情况。解决标注时强制放大到 100% 以上再画框这是硬性操作规范。另外可以在标注前把长图切成块让标注员在局部大图上作业标完再映射回原图坐标。数据检查时用脚本计算 bbox 内部的边缘密度如果框里 90% 都是纯色背景说明框画得不准。5.5 皮带反光和夜间灯光导致模型白天一个样、晚上一个样现象白天训练测试效果都正常换到夜间或阴天漏检率突然升高或者垃圾和阴影混在一起。原因数据集大多来自白天录像夜间帧极少模型见过的光照域太窄。传送带因为有金属材质反光区域在高光和低光下差异巨大模型学到的亮度特征不具备泛化能力。解决训练时加入随机亮度、对比度增强采样hsv_v和hsv_h的扰动模拟不同光照。更彻底的办法是收集夜间帧用第 3.2 节的漏标检查流程补充标注把夜间样本补进训练集。这类问题靠纯数据增强只能缓解有真实夜间样本才是根治手段。6. 进阶让模型真正稳定上线的验证方法训练完模型不等于能上线。我每次做传送带异检的收尾动作是拿一段完全没参与训练的新录像做回放测试。把录像按 5 帧间隔抽帧用训练好的模型批量推理标记出所有置信度高于 0.25 的检测框然后人工统计两类错误漏检录像里能看到异物但模型没框和误检模型框了但没有异物。漏检率控制在 5% 以下、单帧处理时间低于 50ms 是我个人判断可上线的硬指标。对于长传送带场景单张全图推理通常抓不住细长铁棍我倾向于用 SAHI 这类切片推理工具把 1920x1080 的图切成 640x640 的重叠块分别推理再合并结果。切片重叠率设为 20%可以避免目标被切分后漏检。推理阶段用TensorRT或ONNX Runtime做加速工控机上 YOLOv8s 配 FP16 精度基本能到 40ms 以内。回放测试还有个容易被忽略的细节异物在皮带上滚动时朝向会变铁棍在某一帧是水平的下一帧可能是斜的。回放时我会特意挑几段异物经过镜头正下方、角度变化剧烈的片段验证。从那以后我每次做完模型都会固定走一遍“新录像回放 → 统计漏检误检 → 抽检角度变化帧”这组流程不跑完这套测试就不敢报效果。传送带异检这种场景现场环境永远比训练集丰富数据标注的完整性和场景覆盖度决定了模型的上限希望帮到你。本文还有配套的精品资源点击获取