news 2026/9/28 16:43:55

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
水下垃圾检测数据集实战:VOC/YOLO/JSON三格式解析与YOLOv8训练避坑指南

简介:这份水下垃圾检测数据集面向从事水域环境智能监测、水下机器人视觉与目标检测算法实践的开发者与学习者,可用于课程作业、学科设计、竞赛及实际项目中的模型训练与验证。数据集共5328张水下机器人实拍图像,标注精准、分布均匀,涵盖水下生物、塑料垃圾、金属垃圾、木头垃圾、橡胶垃圾、布料垃圾、捕鱼工具垃圾七类目标,贴合真实水下场景的多样性。压缩包内文件总数达21313个,包含5328张jpg原图,以及voc格式xml、yolo格式txt和json三种标注文件各5328份,可满足YOLO、Faster R-CNN等主流检测框架的直接读取与训练需求,整体约127.54MB,目录组织清晰便于检索。目前已有325人学习下载,适合希望快速搭建水下垃圾识别基线、开展算法对比实验或完成相关课题的读者参考使用。

1. 水下垃圾检测数据集怎么选:5328 张、7 类、三种标签格式意味着什么

做水下垃圾检测,第一个卡住大多数人的不是模型结构,而是数据。真实水下图像普遍偏色、浑浊、光照不均,公开可用的标注数据少得可怜,自己下水拍一圈再标一遍,成本高到劝退。所以当有人整理出一份「7 类、5328 张、同时带 VOC(xml)+YOLO(txt)+JSON 三种标签」的水下垃圾检测数据集时,它解决的其实是三件事:省掉采集和标注的人力、直接对接主流检测框架、以及给多任务(检测/分割/可视化)留出扩展口。

这份数据集的核心价值在于「一份图、三套标签」。VOC 的 xml 是 Pascal VOC 时代的通用格式,很多老脚本、可视化工具、标注软件默认吃它;YOLO 的 txt 是归一化后的class cx cy w h,Ultralytics 系(YOLOv5/v8/v11)训练直接读;JSON 则常见于 COCO 风格或自定义结构,方便做统计分析、格式转换和跨框架迁移。7 个类别通常覆盖塑料瓶、塑料袋、渔网、金属罐、玻璃瓶、橡胶、其他杂物这类水下常见垃圾,具体类别名以数据集自带的classes.txt或data.yaml为准,不要凭猜。

适合谁:想快速跑通一个水下垃圾检测 baseline 的学生和工程师、需要做水下机器人/无人船视觉模块的团队、以及想拿真实脏数据练手数据清洗和格式转换的人。下面从格式拆解讲到训练落地,再讲踩坑,尽量让你照着能复现。

2. 三种标签格式拆开看:VOC、YOLO、JSON 各自怎么读怎么写

2.1 VOC xml 的结构与解析要点

VOC 格式一张图对应一个 xml,核心节点是filename、size(宽高)、以及若干object,每个 object 里有name和bndbox(xmin/ymin/xmax/ymax,绝对像素坐标,左上右下)。水下数据集的 xml 常见坑是size里的宽高和真实图片对不上,或者bndbox出现负值、越界。解析时一定要以实际图片尺寸为准做校验。

import xml.etree.ElementTree as ET from PIL import Image def parse_voc(xml_path, img_path): tree = ET.parse(xml_path) root = tree.getroot() # 以真实图片尺寸为准,别信 xml 里的 size w, h = Image.open(img_path).size boxes = [] for obj in root.findall('object'): name = obj.find('name').text.strip() bb = obj.find('bndbox') xmin = float(bb.find('xmin').text) ymin = float(bb.find('ymin').text) xmax = float(bb.find('xmax').text) ymax = float(bb.find('ymax').text) # 裁剪到图像范围内,防止越界框污染训练 xmin, ymin = max(0, xmin), max(0, ymin) xmax, ymax = min(w, xmax), min(h, ymax) if xmax - xmin < 1 or ymax - ymin < 1: continue # 丢弃退化框 boxes.append((name, xmin, ymin, xmax, ymax)) return w, h, boxes

逻辑说明:先读真实尺寸再解析框,是为了防止 xml 里size写错导致后续归一化全错。裁剪和退化框过滤是水下数据的必备动作,因为浑浊场景下标注员容易画出贴边甚至越界的框。参数上,xmin/ymin/xmax/ymax是绝对像素,转 YOLO 时要除以宽高。

2.2 YOLO txt 的归一化规则与类别索引

YOLO 每张图一个 txt,每行class_id cx cy w h,全部是相对图像宽高的 0~1 浮点。class_id从 0 开始,必须和data.yaml里names的顺序严格一致——这是最常见的翻车点:类别顺序错一位,模型学出来的全是错的。转换时用下面的公式,注意cx/cy是框中心,不是左上角。

def voc_to_yolo(boxes, w, h, class_map): lines = [] for name, xmin, ymin, xmax, ymax in boxes: if name not in class_map: continue # 未登记类别直接跳过,避免索引错乱 cid = class_map[name] cx = (xmin + xmax) / 2.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h # 归一化后仍要 clamp,浮点误差可能让值略超 1 cx, cy = min(max(cx, 0), 1), min(max(cy, 0), 1) bw, bh = min(max(bw, 0), 1), min(max(bh, 0), 1) lines.append(f"{cid} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") return lines

参数说明:class_map是从类别名到 0 基索引的字典,务必和data.yaml同源生成,别手写两遍。保留 6 位小数足够,YOLO 官方脚本也是这个精度。clamp 是后悔药,能挡住个别脏标注。

2.3 JSON 标签的两种常见形态与转换

JSON 在水下数据集里通常是两种:一种是 COCO 风格(images/annotations/categories三个数组,框是[x, y, w, h]绝对坐标),另一种是自定义的每图一个对象。COCO 的bbox是左上角加宽高,和 VOC 的左上右下不一样,转换时别搞混。

import json def coco_to_yolo(json_path, class_map): data = json.load(open(json_path, 'r', encoding='utf-8')) img_info = {im['id']: im for im in data['images']} # COCO 的 category_id 往往不是 0 基,需要重映射 cat_id_to_name = {c['id']: c['name'] for c in data['categories']} out = {} for ann in data['annotations']: im = img_info[ann['image_id']] w, h = im['width'], im['height'] name = cat_id_to_name[ann['category_id']] if name not in class_map: continue x, y, bw, bh = ann['bbox'] # 左上角 + 宽高 cx = (x + bw / 2) / w cy = (y + bh / 2) / h out.setdefault(im['file_name'], []).append( f"{class_map[name]} {cx:.6f} {cy:.6f} {bw/w:.6f} {bh/h:.6f}") return out

逻辑说明:COCO 的category_id经常是 1 基甚至跳号,直接拿来当 YOLO 的class_id会错位,所以必须经过cat_id_to_name再映射到自己的class_map。bbox是左上角加宽高,转中心点要加半个宽高。这一步是 json 转换里最容易出错的地方。

3. 从零跑通训练:目录组织、data.yaml 与 YOLOv8 最小命令

3.1 目录结构与划分脚本

YOLO 训练要求固定的目录:images/train、images/val、labels/train、labels/val,图片和标签同名不同后缀。划分时按 8:2 或 9:1,注意同一段视频抽帧的图要放同一侧,否则验证集泄漏,指标虚高。

# 假设原始数据:images/ 下所有 jpg,labels/ 下同名 txt mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val # 用固定种子打乱,保证可复现 ls images/*.jpg | shuf --random-source=<(yes 42) > all.txt n=$(wc -l < all.txt); nval=$((n/5)) head -n $nval all.txt | while read f; do b=$(basename "$f" .jpg) mv "$f" dataset/images/val/; mv "labels/$b.txt" dataset/labels/val/ done tail -n +$((nval+1)) all.txt | while read f; do b=$(basename "$f" .jpg) mv "$f" dataset/images/train/; mv "labels/$b.txt" dataset/labels/train/ done

参数说明:--random-source=<(yes 42)用固定种子让shuf结果可复现,团队协作时这点很重要。nval=$((n/5))是 20% 验证集,数据量小可以调到 10%。移动而不是复制,能避免同一张图同时出现在两侧。

3.2 data.yaml 的写法与类别顺序

path: /abs/path/to/dataset train: images/train val: images/val nc: 7 names: 0: plastic_bottle 1: plastic_bag 2: fishing_net 3: metal_can 4: glass_bottle 5: rubber 6: other

nc必须等于names的条目数,names的顺序必须和生成 txt 时的class_map完全一致。建议把class_map和这份 yaml 用同一个脚本生成,从源头杜绝错位。path用绝对路径,相对路径在不同工作目录下容易找不到。

3.3 训练命令与关键参数

yolo detect train \ data=/abs/path/to/data.yaml \ model=yolov8n.pt \ epochs=100 imgsz=640 batch=16 \ lr0=0.01 lrf=0.01 \ mosaic=1.0 hsv_h=0.015 hsv_s=0.7 hsv_v=0.4 \ degrees=10 translate=0.1 scale=0.5 \ project=runs name=underwater_trash

参数说明:model=yolov8n.pt是轻量 backbone,水下数据量 5000 出头,n 或 s 足够,别一上来上 x。imgsz=640是默认,水下小目标多可以试 960,但显存翻倍。hsv_h/s/v是颜色抖动,水下偏色严重,适当加大hsv_h能提升泛化。mosaic=1.0对小目标友好,但训练后期可以降到 0.5 减少拼接伪影。degrees=10做小角度旋转,水下目标朝向随机,旋转增强有效。lr0=0.01配lrf=0.01是余弦退火的常见组合。

提示:第一次跑先用epochs=10验证流程通不通,确认 loss 正常下降、验证集能出图,再拉满 100 轮,省得白等几小时。

4. 水下场景的避坑与排查:颜色、类别、格式三类翻车

4.1 现象:训练 loss 正常但验证 mAP 极低

原因:验证集和训练集来自同一段视频的相邻帧,内容高度相似,模型记住了背景而不是目标;或者类别索引在 train/val 之间不一致。解决:按视频/场景划分而不是随机划分,检查两侧 txt 的class_id分布是否一致,用脚本统计每类框数量对比。

4.2 现象:模型把背景的蓝色水体框成目标

原因:水下图像整体偏蓝绿,颜色增强hsv_h开太大反而让水体纹理被当成特征;或者负样本(无垃圾的纯水图)太少。解决:把hsv_h降到 0.01~0.015,加入一定比例的无目标背景图作为负样本,让模型学会「什么都没有」也是一种输出。

4.3 现象:xml 转 yolo 后框整体偏移

原因:xml 里的size宽高和真实图片不一致,转换时用了 xml 的尺寸做归一化。解决:一律用PIL.Image.open(img).size取真实尺寸,转换后随机抽 20 张用可视化脚本画框核对,别信标注文件里的元数据。

4.4 现象:JSON 转出来的类别数比预期多

原因:COCO 的categories里可能包含数据集里实际没出现的类别,或者category_id跳号。解决:先统计annotations里实际出现的category_id,再决定class_map,不要直接照抄categories数组。

4.5 现象:训练中途 BN 崩溃、loss 变 NaN

原因:batch 太小(水下图分辨率高时显存吃紧,被迫降到 4 甚至 2),BN 统计不稳;或者学习率过高。解决:换yolov8n或降imgsz保证batch>=8,或改用带 GroupNorm 的变体;把lr0从 0.01 降到 0.005 再试。这类玄学问题,先怀疑 batch 和 lr。

5. 进阶:用混淆矩阵和格式互转脚本把数据集吃透

跑通训练只是开始,真正决定模型上限的是你对这份数据集的理解程度。我一般会做两件事:一是画混淆矩阵看哪两类在互相误判,二是写一个三格式互转的统一脚本,方便随时切换框架。

混淆矩阵在 Ultralytics 训练完会自动生成confusion_matrix.png,但要注意归一化方式。行归一化看「真实类被预测成什么」,列归一化看「预测类里混进了什么」,两个都要看。水下数据里塑料瓶和玻璃瓶、塑料袋和渔网最容易混,前者因为透明材质在浑浊水里轮廓相似,后者因为都是网状/片状。看到这两组高误判,就该针对性补样本或加类别区分特征,而不是盲目加轮数。

import json, xml.etree.ElementTree as ET from pathlib import Path def yolo_to_voc(txt_path, img_w, img_h, class_names, out_xml): root = ET.Element('annotation') ET.SubElement(root, 'filename').text = Path(txt_path).stem + '.jpg' size = ET.SubElement(root, 'size') ET.SubElement(size, 'width').text = str(img_w) ET.SubElement(size, 'height').text = str(img_h) for line in open(txt_path): cid, cx, cy, bw, bh = line.split() cid = int(cid); cx, cy, bw, bh = map(float, (cx, cy, bw, bh)) xmin = (cx - bw/2) * img_w; ymin = (cy - bh/2) * img_h xmax = (cx + bw/2) * img_w; ymax = (cy + bh/2) * img_h obj = ET.SubElement(root, 'object') ET.SubElement(obj, 'name').text = class_names[cid] bb = ET.SubElement(obj, 'bndbox') for k, v in zip(('xmin','ymin','xmax','ymax'), (xmin,ymin,xmax,ymax)): ET.SubElement(bb, k).text = str(int(round(v))) ET.ElementTree(root).write(out_xml, encoding='utf-8')

逻辑说明:反向转换时class_names必须是列表且索引和class_id对齐,int(round())是因为 VOC 的 bndbox 惯例是整数像素。这个脚本配合前面的voc_to_yolo和coco_to_yolo,就能在 VOC/YOLO/JSON 之间自由切换,接不同框架时不用重新标。

验证数据集质量还有一个笨但有效的办法:随机抽 50 张,把三种格式各自解析一遍,画在同一张图上比对。如果三种格式画出来的框位置一致,说明转换链路没问题;如果有偏差,问题一定出在坐标定义(左上右下 vs 左上宽高)或归一化基准上。这个自检我每次拿到新数据集都会做,能省掉后面几小时的 debug。

最后说个习惯:拿到任何带多格式标签的数据集,先写一个stats.py统计每类框数量、每图平均框数、框的宽高分布,再决定用不用、怎么增强。水下垃圾检测里,如果某一类只有几十个框,别指望模型学好,要么合并类别,要么针对性过采样。数据这关过了,模型的事才好谈。希望帮到你。

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

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

Python AI学习路线避坑指南:从环境搭建到PyTorch实战

1. 为什么“保姆级”路线反而最容易把人带进沟里市面上打着“Python AI保姆级学习路线”旗号的内容&#xff0c;我翻过不下几十套。绝大多数长一个样&#xff1a;先装Python&#xff0c;再学NumPy、Pandas&#xff0c;然后PyTorch&#xff0c;最后跑个MNIST手写数字识别&#x…

作者头像 李华
网站建设 2026/9/28 16:42:43

AWS CEO怒批“用AI裁新人”,企业AI落地应重在赋能而非替代

最近看到一条挺有意思的消息&#xff1a;AWS CEO公开批评那些一上来就把AI用在裁员上的企业&#xff0c;尤其是裁掉刚入职的新人&#xff0c;说这是“自掘坟墓最愚蠢的操作”。这句话说得很重&#xff0c;但放在整个行业背景里看&#xff0c;其实一点都不夸张。一段时间以来&am…

作者头像 李华
网站建设 2026/9/28 16:41:52

CLI-Anything:用命令行统一AI工具链,打造终端自动化工作流

1. 为什么“CLI-Anything”这个思路这么香1.1 一句话理解这个项目到底在做什么看到“CLI-Anything”这个名字&#xff0c;我脑子里蹦出来的第一印象是&#xff1a;这不就是“万物皆可命令行”吗。最近AI圈子里Codex CLI、Claude CLI这些工具火得不行&#xff0c;大家开始习惯在…

作者头像 李华
网站建设 2026/9/28 16:41:51

强化学习代码实战:从Q-learning到PPO的算法迁移与调参指南

简介&#xff1a;面向从入门到进阶的强化学习学习者&#xff0c;压缩包按理论章节与实战项目双线编排&#xff0c;系统覆盖马尔可夫决策过程、表格型方法、策略梯度、近端策略优化、深度Q网络基础与进阶技巧、演员评论家、稀疏奖励、模仿学习、深度确定性策略梯度等主流算法&am…

作者头像 李华
网站建设 2026/9/28 16:41:45

Model-Optimizer:面向真实GPU部署的量化剪枝蒸馏工程方法论

1. 项目概述&#xff1a;Model-Optimizer不是工具箱&#xff0c;而是一套可落地的模型瘦身方法论“Model-Optimizer”这个名字听起来像某个官方SDK或商业软件&#xff0c;但实际在工业界和一线AI工程实践中&#xff0c;它从来不是一个开箱即用的黑盒产品——而是工程师面对真实…

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

YOLOv5骨龄检测实战:从数据预处理到部署避坑全解析

简介&#xff1a;一套基于Python和YOLOv5实现的骨龄检测项目资料&#xff0c;面向毕业设计、课程设计及项目开发场景&#xff0c;适合有一定深度学习基础、希望快速掌握目标检测在医疗影像中如何落地的读者。压缩包共25个文件&#xff0c;其中20个为Python源码&#xff0c;涵盖…

作者头像 李华