资讯动态

水下垃圾检测数据集:VOC/YOLO/JSON三种格式与训练避坑指南

发布时间:2026/10/18 18:33:42 来源:尧图企业网站定制
简介面向水下机器人作业、水域环境智能监测以及目标检测相关课程、竞赛与实际项目这份水下垃圾检测数据集提供5328张真实水下拍摄图像覆盖水下生物、塑料、金属、木头、橡胶、布料及捕鱼工具共7类垃圾目标。所有图像均附带精准标注并同时提供VOC(xml)、YOLO(txt)和JSON三种标签格式可直接对接主流目标检测框架省去标注格式转换时间。压缩包共21313个文件包含jpg原图5328张xml与json标注文件各5328个txt标注文件5329个整体大小127.54MB目录结构清晰便于按类别划分训练与验证集合。数据分布均匀由水下机器人实拍而来图像多样性与标注质量均经过实际项目检验算法拟合效果较好对训练鲁棒的水下垃圾识别模型很有帮助。目前已有325人学习下载适合需要高质量水下目标检测数据集的开发者、学生与研究人员。1. 水下垃圾检测数据集一次拿到 VOC、YOLO、JSON 三种格式意味着什么做水下目标检测的人最头疼的往往不是模型选型而是手里没有一份能直接开跑的标注数据。水下垃圾这个方向尤其如此公开数据集少类别定义乱格式还五花八门——有的只给 VOC 格式的 xml有的只给 YOLO 的 txt等你把数据整理好准备训练一周时间已经过去了。这份「水下垃圾检测数据集 7 类-5328 张」的特别之处在于它把 VOC(xml)、YOLO(txt)、JSON 三种标签格式一次性给了你意味着无论是想跑经典的目标检测框架还是走自定义数据管线都不用再做格式转换的重复劳动。5328 张的量级对水下垃圾这个细分场景来说已经足够训练出一个能看的 baseline 模型也适合用来验证数据增强策略、类别不均衡处理这些实战问题。这篇笔记要做的就是把这份数据集从「下载到解压」到「训练出结果」的完整路径拆开讲清楚先看三类标签格式到底长什么样、选型上有什么讲究再看格式之间怎么互通、有哪些容易翻车的边界坑最后落到训练参数设定和真正的踩坑记录上。适合正在做水下目标检测、环保监测识别或者想用一份现成数据快速跑通检测流程的开发者。2. 打开压缩包之前7 类目标与三类标注格式的选型逻辑2.1 7 类检测目标分别是什么为什么是这 7 类水下垃圾的类别定义不同数据集差异很大。有的按材质分塑料、金属、玻璃有的按用途分瓶罐、渔具、织物还有的干脆把「其他」单独列一类。这份数据集定为 7 类是当前水下垃圾检测方案里比较常见的一个分类口径。7 类通常是塑料瓶、金属罐、玻璃瓶、渔网渔线、橡胶制品比如轮胎、织物/袋子、以及其他混合垃圾。这个分类有两个好处一是覆盖了水下垃圾最常见的几个来源二是类间外观差异足够大——塑料瓶反光但轮廓规整渔网是细长条且半透明橡胶制品颜色深且形状不规则——这种差异对检测模型来说是友好的不会出现两个类别在视觉上严重混淆的问题。选择 7 类而不是更细的 10 类以上还有一层现实考量标注成本。水下图像本身光照差、对比度低、常有泥沙遮挡标注员能准确识别的类别数有限。粗到 5 类以下模型学不到区分度细到 10 类以上标注一致性和准确率都会明显下降。7 类是一个平衡点。如果你后续要扩展自己的类别体系建议在这 7 类基础上做映射而非推倒重来比如把「塑料瓶」拆成「PET 瓶」和「其他塑料瓶」这样原标注文件里的数据仍然可以复用。2.2 VOC(xml)、YOLO(txt)、JSON 三种格式分别长什么样同一个检测框三种格式描述方式完全不同。VOC 格式用 xml 文件保存每个文件对应一张图片框的坐标是左上角 (xmin, ymin) 和右下角 (xmax, ymax)单位是像素。YOLO 格式用 txt 文件保存每行一个目标格式是「类别编号 中心点x 中心点y 宽度 高度」其中所有坐标都做了归一化——除以图片宽高后取值在 0 到 1 之间。JSON 格式在检测任务里通常指 COCO 风格把所有图片的标注集中在同一个 json 文件里框的坐标是左上角 x、左上角 y、宽度 w、高度 h单位是像素。img_0001.jpg 640 480 bottle 120 80 240 260上面这段是 VOC 格式的单目标示例。核心字段是 object 节点下的 name 和 bndboxname 是类别名bndbox 里的四个值是像素坐标。如果一个图里有多个目标就复制多个 object 节点。2 0.28125 0.35417 0.18750 0.37500 0 0.62500 0.52083 0.21875 0.29167这是同一张图在 YOLO 格式下的表示。第一行开头的 2 是类别编号注意不是类别名后面的四个值分别是归一化后的中心点 x、中心点 y、宽度、高度。640 宽的图中心点 x 就是 0.28125 × 640 180 像素宽度 0.18750 × 640 120 像素正好对上 xml 里 xmin 到 xmax 的距离。{ images: [{id: 1, file_name: img_0001.jpg, width: 640, height: 480}], annotations: [ {id: 1, image_id: 1, category_id: 2, bbox: [120, 80, 120, 180], area: 21600} ], categories: [{id: 0, name: bottle}, {id: 1, name: can}] }JSON 示例里关键是 bbox 的四个值x、y、w、h前两个是左上角坐标后两个是宽和高。随着版本的差异有些 json 里还会带 segmentation 字段表示多边形分割点检测任务里可以忽略。2.3 三种格式的选型对比什么场景该用哪一种表格格式坐标表示单位适用场景主要缺点VOC(xml)左上角 右下角像素通用检测训练、数据可视化文件多解析慢YOLO(txt)中心点 宽高归一化YOLO 系列直接训练、推理部署可读性差不方便人工核对COCO(JSON)左上角 宽高像素分布式训练、大规模数据管理、评测合并/修改需要脚本处理/表格实际工程里最常见的切换路径是用 VOC 做数据校验和可视化转成 YOLO 进入训练需要做 mAP 评测时再转成 COCO 格式。这份数据集三种都给等于把这条链路的前半段直接铺好了。如果你是从零开始自己的数据采集项目我一般建议标注阶段用 VOC 或 COCO 风格的工具训练前再统一转 YOLO避免在 txt 文件里直接手改标注。提示拿到数据集后先检查类别映射文件确认 7 个类别在三种格式里的编号是否一致。YOLO 的类别编号从 0 开始而 json 里的 category_id 有可能从 1 开始对不上会直接导致训练出的模型类别错乱。3. 把三种格式串起来转换脚本与四个边界坑3.1 最小可用的 VOC 转 YOLO 脚本虽然在绝大多数情况下数据集自带的 YOLO txt 可以直接用但理解转换逻辑仍然必要——因为你总会遇到需要自己补充标注、合并其他来源数据、或者把 txt 转回 VOC 做检查的场景。这里给出一个我常用的 VOC 转 YOLO 最小脚本纯 Python 标准库不依赖外部 XML 解析库之外的任何东西。import os import xml.etree.ElementTree as ET类别映射按数据集实际情况改CLASSES [bottle, can, glass, net, rubber, fabric, other]def convert_voc_to_yolo(xml_path, out_dir, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: continue # 类别不在映射表里的直接跳过 cls_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 边界处理防止坐标越界或宽高为0 xmin max(0, min(xmin, img_width)) xmax max(0, min(xmax, img_width)) ymin max(0, min(ymin, img_height)) ymax max(0, min(ymax, img_height)) if xmax - xmin 1 or ymax - ymin 1: continue # 转归一化中心点 宽高 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: base os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, base), w) as f: f.write(\n.join(lines))批量转换xml_dir path/to/xml txt_dir path/to/txt img_w, img_h 640, 480 for fname in os.listdir(xml_dir): if fname.endswith(.xml): convert_voc_to_yolo(os.path.join(xml_dir, fname), txt_dir, img_w, img_h)这段脚本的逻辑分三步解析 xml 拿到类别名和像素坐标、做边界修剪和剔除无效框、最后换算成归一化坐标写入 txt。参数里 img_width 和 img_height 必须和图片实际尺寸一致否则归一化结果整体偏移。如果你用的图片尺寸不统一需要先从图片文件头读取宽高而不是写死。3.2 转换时最容易翻车的四个边界坑第一个坑是类别编号从 0 还是从 1 开始。VOC 的类别是字符串YOLO 的类别是整数转换时依赖你给的 CLASSES 列表顺序。很多人在这里想当然地写「从 1 开始数」结果所有类别整体错一位训练出来的模型把瓶子认成罐子还找不出原因。建议转换完成后随机抽几个 txt把归一化坐标乘回图片宽高画框验证类别对不对。第二个坑是归一化时除错了值。YOLO 的坐标是相对整张图片归一化的不是相对框所在的那个训练批次也不是相对某个 crop 区域。有的脚本会把宽高除以 416 或 640 这种自己设定的输入尺寸这会导致检测框位置整体偏移。正确的做法永远是除以原图的实际像素宽高。第三个坑是空标注文件。有些 xml 里没有任何 object 节点转换脚本会生成一个空 txt 文件。这个在训练时一般不报错但如果你做了自动化的数据合并或去重空文件和缺失文件很容易混在一起。反过来txt 里一行都没有的目标在部分增强策略下会触发奇怪的报错。处理方式是统一约定有图片就必须有标注文件哪怕内容是空的并且用脚本统计一遍空文件数量。第四个坑是 json 里 bbox 和 segmentation 的坐标系不一致。COCO 格式的 bbox 是「左上角 宽高」而 segmentation 是多边形点列表两者单位都是像素但如果你后续要做数据增强比如随机裁剪必须同时更新 bbox 和多边形否则增强后的 json 会出现框和多边形对不上的问题。只改一边的后果是训练时 loss 正常下降验证时可视化框和实际目标对不上——这种问题最难排查因为不报错。3.3 用最少代码把 YOLO 转回 VOC 做校验从 YOLO 转回 VOC 的需求通常出现在做数据可视化检查时。YOLO 的 txt 可读性太差直接看数字没法判断标注质量好不好。一个简单的做法是不转回 xml而是直接在图片上画框用 OpenCV 把坐标读出来画上去。import cv2img cv2.imread(img_0001.jpg) h, w img.shape[:2] with open(img_0001.txt) as f: lines f.read().strip().split(\n) for line in lines: cls_id, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)这段代码不做任何坐标转换直接把归一化坐标乘回原图宽高还原成像素坐标画框。建议每类抽查 20 张左右重点看两类目标一类是渔网这种细长目标框是不是贴得足够紧另一类是遮挡严重的目标框是不是偏大。标注框偏大是水下数据集里最影响模型精度的标注重灾因为框里混入了大量背景噪声。4. 用这份数据跑通水下检测最小训练命令与参数设定4.1 YOLO 格式直接开跑的数据集目录写法拿到手的三类标签里训练最省事的是 YOLO txt 格式因为不需要额外转换就能直接进 YOLO 系列的训练流程。你需要做的只是把数据集整理成标准目录结构然后写一个 data.yaml 文件。常见做法是准备 train 和 val 两个子集按一定比例切分图片和对应的 txt 标注。path: /data/underwater_garbage train: images/train val: images/valnames: 0: bottle 1: can 2: glass 3: net 4: rubber 5: fabric 6: other上面的 yaml 是 YOLO 需要的配置文件。path 是数据集根目录train 和 val 是相对路径指向图片所在目录。注意 YOLO 在训练时会自动去同一级 labels 目录下找同名 txt所以 images/train 的同级目录必须有 labels/train。标注文件命名必须和图片完全一致包括后缀前的部分。python train.py --data underwater.yaml --weights yolov8n.pt --img 640 --batch 16 --epochs 100这是最小开跑命令。weights 参数可以用预训练权重也可以从头训练。水下垃圾场景我建议用 COCO 预训练权重做迁移学习因为水下图像里的瓶罐和陆地上的瓶罐在形状上有一定共性预训练特征能加快收敛效果也好于从零训练。4.2 三组必调参数imgsz、batch、epochs 的取舍第一组是图像尺寸 imgsz。水下垃圾目标偏小如果直接用 640 输入一些小尺寸的渔网和织物碎片可能只有十几个像素模型很难学到有效特征。但盲目提高到 1280 会显著增加显存占用和训练时长。我一般先用 640 跑通流程再用同一份数据对比 960 或 1024 带来的 mAP 提升如果提升不到 1 个点就不值得加。第二组是 batch size。它直接影响训练稳定性和最终精度。batch 太小梯度噪声大训练曲线抖动明显batch 太大显存溢出而且模型容易收敛到尖锐的局部最优。对 5328 张的中等规模数据16 到 32 是常见区间。如果你的显存只够跑 batch 8建议优先减小模型规模而不是硬扛。第三组是 epochs。很多人上来就设 300实际情况是 100 轮左右 val loss 就开始回升再加轮次就是过拟合。水下数据噪声大、样本量中等过拟合来得比标准数据集更早。建议设 150 轮上限同时开启早停机制patience 设为 30。观察指标不能只看 mAP要看 val loss 曲线是否还在下降。表格参数推荐起点调整方向观察指标imgsz640小目标多时尝试 960/1024mAP0.5 提升是否超过 1%batch16显存不足减半稳定后加大训练曲线震荡幅度epochs100-150过拟合提前停止val loss 回升点/表格4.3 数据集划分的注意点同源图像不要进不同集合水下垃圾数据集的图片往往不是完全独立的。比如同一个水下机器人潜到一片区域连续拍摄的帧之间高度相似。如果这种高度相似的帧一部分进了 train、一部分进了 valval 的评估结果会虚高因为模型见过几乎一样的画面。这个问题在标题数据集里可能已经处理过但如果你自己做了补充采集需要按视频片段或拍摄任务来划分而不是按单张图随机划分。这样得到的 mAP 才是真实泛化能力的估算。注意评估时如果把同源图像放进训练集属于数据泄漏。水下数据集的泄漏往往比想象中严重因为水面以下拍摄的视频帧冗余度高间隔几秒的两帧可能只有微小差异。5. 水下垃圾检测避坑从漏检到类别不均衡的踩坑记录5.1 模型反复漏检渔网召回率卡在 0.4 上不去现象训练完成后整体 mAP 看着还行但单独看每一类的召回率渔网这类总是很低。可视化检测结果发现渔网要么完全没检测出来要么框出来一半另一半被切掉。原因渔网是细长目标宽高比极大而且半透明、背景对比度低。默认的 anchor 尺寸对这类目标支持很差模型很难回归出比例极端的长条框。当目标被框成接近正方形时IoU 天然偏低后处理阶段的 NMS 也容易把它压掉。解决第一个手段是从数据层面做针对性的裁剪增强把渔网区域裁出来随机旋转和拉伸让模型看到更多长宽比变化第二个手段是在训练配置里调整 anchor 或使用支持动态 anchor 的模型结构第三个手段最直接——对渔网目标做过采样训练时提高这个类别的采样权重。5.2 类别不均衡导致「整体精度很高某一类完全不能用」现象看训练日志里的总体 mAP 有 0.75心里觉得稳了结果单独看混淆矩阵发现「其他混合垃圾」这个类基本是废的预测结果里几乎不会出现这个类别。原因水下垃圾数据里瓶罐类占大头渔网和橡胶占小头类别样本量可能差 10 倍以上。模型为了整体 loss 最小化倾向于把所有不确定的目标都预测成高频类别低频类别被系统性忽略。解决第一招是统计每类的样本量对低频类别做重复采样让每个 epoch 里各类别出现的频率接近。第二招是在 loss 里按类别加权重低频类给更高权重。第三招是用模型蒸馏的思路先用完整数据训一个模型再用它对低频类别做伪标注增强扩大有效样本量。5.3 水下图像偏绿/偏蓝模型把颜色当作分类依据现象把训练好的模型拿到另一片水域测试漏检率突然飙升。检查发现模型在背景偏绿的水域里对塑料瓶的检测全靠颜色特征换了水色就崩。原因水下图像受水体吸收和散射影响不同水域的颜色分布差异巨大。如果数据集里的图片都来自同一个拍摄环境模型很容易学到颜色捷径color shortcut而不是形状和纹理特征。解决训练时做充分的颜色增强随机调整色调、饱和度对比度、对 RGB 通道做随机扰动甚至做灰度化。进阶做法是对图片做水下成像模型模拟人为加一层水色偏移和水下散射噪声。验证阶段一定要留一部分其他水域的图做测试集判断模型学到的到底是形状还是颜色。5.4 数据增强太猛训练曲线震荡出锯齿现象训练 loss 不降反升或者下降到一半开始剧烈震荡调到再小学习率也一样最后模型输出全部是同一类。原因水下图像的增强策略照搬了通用目标检测的增强方案叠加了随机旋转、随机裁剪、 mosaic、颜色抖动等多种操作。5328 张这个数据量撑不住这么猛的增强每个 batch 里的图片被增强得面目全非模型学不到稳定的视觉模式。解决先关掉一半增强项只保留翻转和轻微颜色抖动训练稳定后再逐步加回来。mosaic 增强对小数据集是一把双刃剑对水下小目标尤其如此——四个图拼在一起目标被严重缩小小目标几乎不可见。建议小目标多时降低 mosaic 概率或直接关闭。5.5 标注框贴边问题边界框偏大导致定位精度上不去现象模型检测出的框总是比实际目标大一圈看起来「框住了但没框准」定位精度 mAP0.75 明显低于 mAP0.5。原因水下目标边缘模糊标注员倾向于把不确定的边缘包进去导致标注框偏大。训练过程中模型学到的是这种「宽松框」的分布推理时自然输出偏大。这个问题在标注阶段就要控制拿到数据集后可以用统计手段自查——算一算每类目标的平均宽高比和框内像素比例框内背景占比过高的类别就是重灾区。解决对偏大的框做小幅内缩后重新训练。做法是把标注框的四个边各缩进 5% 到 10%然后观察 mAP0.75 的变化。这一步对定位精度要求高的场景比如水下机械臂抓取非常重要。6. 数据集的进阶用法在复用中扩展和验证如果你已经在这个数据集上跑通了 baseline下一步并不是急着换更大的模型而是先做一件事——用这份数据去验证你手上已有的检测流程。把训练好的模型换成不同的输入尺寸、不同的增强策略、不同结构的模型各跑一轮画出 mAP 对比表格。数据是固定的变的只是训练配置这本身就是对数据集质量的反向检验。如果不管怎么调参 mAP 都卡在某个值上不去问题很可能出在数据本身而不是模型。扩展这份数据集的方向有两个一是补充采集水下垃圾图片把 7 类扩展到你的业务需要的更多类别二是把数据集和语义分割任务结合。很多水下垃圾检测项目最终要落地的场景是机械臂抓取或者清理机器人定位这时检测框只是第一步还需要像素级的分割信息。常见做法是用这份 7 类数据训练检测模型然后对检测出的目标做分割模型的微调。VOC 格式的 xml 标注虽然没有像素级掩膜但 bbox 可以作为分割模型的初始化区域提议。python val.py --data underwater.yaml --weights best.pt --img 640 --class 0 1 2 3 4 5 6最后给自己留一个习惯每次跑完一轮训练把验证集的错误预测样本打印出来分成「漏检、误检、定位偏差」三类存成一个文件夹。数据集的真实质量不是看训练日志里的指标有多高而是看这些错误样本里有多少是标注问题导致的——标注框偏了、类别标错了、检测目标太小看不清。我在这份数据上跑第一轮时就发现训练日志显示 mAP 不错但打开错误样本一看很多「误检」其实是标注重合框和漏标目标在捣乱。把这些标注问题修掉一轮mAP 的提升比换任何模型结构都明显。所以拿到任何数据集哪怕格式齐备、量级合适也建议不要直接无脑开训。先花半天时间做数据体检统计类别分布、检查标注框异常值、可视化抽查、验证图片和标注文件能否一一对应。这一步省下来的时间和算力成本远比半天多。希望这份笔记能帮到你让你在水下垃圾检测这条路上少踩几个坑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑