新闻详情

新闻详情

首页 / 资讯中心 / 详情

VOC垃圾数据集转YOLO格式:从XML标注到darknet训练全攻略

发布时间:2026/9/26 10:15:12来源:尧图网络
VOC垃圾数据集转YOLO格式:从XML标注到darknet训练全攻略
简介面向目标检测与垃圾分类识别任务的Pascal VOC格式数据集可直接用于YOLOv3/v4/v5及Darknet框架训练。数据包共44891个文件压缩后约791.41MB核心包含14963张jpg原图、14963个xml标注文件及14965个txt文本文件其中txt包括YOLO格式标签与1个类别文件结构清晰便于直接划分训练集、验证集与测试集。标注由labelImg工具完成以矩形框精细标记44类生活垃圾类别名称全英文并额外提供中英文对照表有效避免中文乱码和编码兼容问题。目前已有1900人学习下载适合刚接触目标检测的学生、研究者以及需要构建垃圾识别模型的应用开发者。作者明确说明仅提供准确合理的标注数据不保证训练模型精度用户可基于此数据集自行调整网络结构、超参数与增强策略以适配不同硬件和场景需求。1. 拿到手就能训一份14963张的标准VOC垃圾数据集做垃圾分类检测的同学八成都有过这种经历网上下载一个标注齐全的垃圾图片集打开一看发现是 COCO 大目录硬切出来的碎片或者 XML 里类别名大小写飘忽不定坐标还有负数光清洗就要花掉一整个周末。这份 14963 张的垃圾 VOC 数据集最大的价值是它严格按 PASCAL VOC 的目录规范组织图片、标注一一对应拿到手就能直接走 YOLOv3/v4/v5 的 darknet 训练流程。你要做的只有三件事确认类别清单、把 VOC 标注转成 YOLO 要的 txt、改好 cfg 里的 classes 和 filters。它适合正在做智慧环卫、垃圾分类、可回收物分拣这类方向的检测工程师也适合拿真实标注数据练手的目标检测新人。2. VOC 数据集的底细目录结构、XML 标注与类别统计网上自称 VOC 格式的数据集不少真正按标准目录摆好的却不多。这份 14963 张的垃圾数据集把训练要用的文件都放在了 VOCdevkit 目录下先把结构拆开看一遍后面所有操作才不会跑偏。2.1 目录结构Annotations、JPEGImages 与 ImageSets 的对应关系打开数据集根目录标准的 VOC 布局长这样VOCdevkit/VOC2024/ ├── Annotations/ # 14963 个 XML 标注文件 ├── JPEGImages/ # 14963 张 JPG 原图 └── ImageSets/ └── Main/ # 官方自带的 train.txt / val.txt 划分文件三个目录的对应关系很简单Annotations 里的每个 XML 和 JPEGImages 里的图片通过文件名关联比如000001.xml描述的就是000001.jpg一张图对应一个 XML这个数据集里 14963 张图片全部有标注没有空标注文件。ImageSets/Main 里如果带了划分文件可以直接用但我实际做项目时更推荐自己重新划一遍训练集和验证集后面第 3.3 节会给出划分脚本。关键点在于 Annotations 和 JPEGImages 必须严格一一对应缺任何一边转换脚本都会报错或者静默跳过。2.2 解读 XML 标注一个目标对应一个 object 节点VOC 的 XML 标注格式是固定的随便打开一个文件结构大致如下annotation folderVOC2024/folder filename000001.jpg/filename size width640/width height480/height depth3/depth /size object namecardboard/name truncated0/truncated difficult0/difficult bndbox xmin73/xmin ymin112/ymin xmax328/xmax ymax435/ymax /bndbox /object /annotation几个节点是转换脚本要用的核心XML 节点内容转换时的作用folder/filename数据集名、文件名可以忽略只用文件名定位图片size/width, height原图宽高归一化坐标时的分母object/name目标类别名映射成类别 IDobject/difficult是否难例darknet 不认这个标记转换时直接跳过bndbox/xmin, ymin, xmax, ymax外接矩形像素坐标换算成 YOLO 的中心点加宽高需要注意的是一张图里每出现一个目标就有一个object节点如果一张图里有五袋垃圾就有五个 object。有些数据集里 foreign key 标注得比较随意difficult节点可能缺失解析时要做默认值处理。2.3 先摸清类别底细一个脚本统计全部标签拿到数据集第一件事不是急着转格式而是先统计类别。很多垃圾数据集的类别名带着空格、大小写混用甚至同一类物体出现两种写法这些都会在后面的类别映射里埋雷。import os import xml.etree.ElementTree as ET xml_dir VOCdevkit/Annotations class_counter {} for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.findtext(name).strip() class_counter[name] class_counter.get(name, 0) 1 print(各类别目标总数) for name, count in sorted(class_counter.items(), keylambda x: -x[1]): print(f{name}: {count}) print(f共 {len(class_counter)} 个类别{len(os.listdir(xml_dir))} 个 XML 文件)这段脚本会把所有 XML 里出现的类别统计出来按数量从高到低排。统计结果里出现的每个name就是后续trash.names文件的一行行的顺序决定了类别 ID。要先跑一遍这个脚本确认类别总数再决定 cfg 里的classes填多少。如果发现类似cardboard和Cardboard这种大小写变体说明标注有脏数据转换前需要先统一。3. 把 VOC 转成 YOLO 训练格式转换脚本与四个边界坑darknet 训练 YOLO 用的不是 XML而是每张图一个同名 txt每行一个目标格式是“类别 ID 中心点 x 中心点 y 宽度 w 高度 h”这些值全部做了归一化。这一步卡住的人绝大多数不是不会写转换逻辑而是栽在坐标公式和边界细节上。3.1 坐标转换原理从像素坐标到归一化坐标VOC 存的是一对对角点坐标xmin, ymin, xmax, ymax而 YOLO 需要的是中心点加宽高并且要除以原图的宽和高归一化到 0 到 1 之间。换算公式是x_center ((xmin xmax) / 2) / image_width y_center ((ymin ymax) / 2) / image_height box_width (xmax - xmin) / image_width box_height (ymax - ymin) / image_height单看公式不复杂但有几个边界坑实际转换时很容易翻车坐标越界有些人工标注框会延伸到图片外面导致归一化后的值大于 1 或者为负darknet 读进去轻则出框怪重则训练崩溃转换时要对结果做 clip。宽高为 0某些目标标注退化成了点或线w或h小于 1 像素这样的目标要直接跳过。ID 从 0 开始YOLO 的类别 ID 是从 0 编号的不是从 1这个和第 2.3 节的类别排序直接相关。txt 与 jpg 必须同名darknet 根据图片路径找对应 txt不在同一个目录也会读不到训练时直接报错。3.2 完整转换脚本XML 到 TXT下面是完整的转换脚本包含类别清单生成、坐标换算、越界处理和难以确认的 difficult 跳过逻辑import os import xml.etree.ElementTree as ET xml_dir VOCdevkit/Annotations # 原始 XML 目录 jpg_dir VOCdevkit/JPEGImages # 原图目录 out_dir VOCdevkit/labels # 输出的 YOLO txt 目录 os.makedirs(out_dir, exist_okTrue) def collect_classes(): names set() for f in os.listdir(xml_dir): if not f.endswith(.xml): continue root ET.parse(os.path.join(xml_dir, f)).getroot() for obj in root.findall(object): names.add(obj.findtext(name).strip()) return sorted(names) class_names collect_classes() with open(trash.names, w, encodingutf-8) as f: f.write(\n.join(class_names) \n) def convert_one(xml_file): xml_path os.path.join(xml_dir, xml_file) root ET.parse(xml_path).getroot() size root.find(size) img_w float(size.findtext(width)) img_h float(size.findtext(height)) if img_w 0 or img_h 0: print(f图片尺寸为0跳过{xml_file}) return 0 lines [] for obj in root.findall(object): difficult obj.findtext(difficult, 0) if int(difficult) 1: continue name obj.findtext(name).strip() if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) w xmax - xmin h ymax - ymin if w 1 or h 1: continue xc min(max((xmin xmax) / 2 / img_w, 0.0), 1.0) yc min(max((ymin ymax) / 2 / img_h, 0.0), 1.0) bw min(max(w / img_w, 0.0), 1.0) bh min(max(h / img_h, 0.0), 1.0) lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if lines: txt_path os.path.join(out_dir, xml_file.replace(.xml, .txt)) with open(txt_path, w) as f: f.write(\n.join(lines)) return len(lines) converted 0 for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue jpg_file xml_file.replace(.xml, .jpg) if not os.path.exists(os.path.join(jpg_dir, jpg_file)): print(f警告找不到对应图片跳过{xml_file}) continue if convert_one(xml_file) 0: converted 1 print(f转换完成: {converted} 张图片得到有效标注) print(f类别清单: {class_names})脚本分三段逻辑先遍历所有 XML 收集类别名并写入trash.names再逐文件解析 XML 做坐标换算最后按同名规则写出 txt。注意class_names.index(name)用的顺序就是trash.names里的行序两者天然对应不会错位。min(max(...))那四行就是坐标越界裁剪all 转出来的中心点、宽高都在 0 到 1 之间。这个脚本不只对 darknet 有用YOLOv5、v8 训练自己的数据集时吃的也是这种 txt后面要换工具链的话转换逻辑完全不用改。3.3 训练集与验证集划分9:1 随机且不重不漏标注转完了接下来按 9:1 把 14963 张图划分成训练集和验证集。划分时要保证两个文件列表没有交集而且不能让某一张图既出现在 train 又出现在 val。import os import random jpg_dir VOCdevkit/JPEGImages train_txt data/trash/train.txt val_txt data/trash/val.txt os.makedirs(os.path.dirname(train_txt), exist_okTrue) files [f for f in os.listdir(jpg_dir) if f.endswith(.jpg)] random.seed(42) random.shuffle(files) split_idx int(len(files) * 0.9) with open(train_txt, w) as f: for name in files[:split_idx]: f.write(os.path.abspath(os.path.join(jpg_dir, name)) \n) with open(val_txt, w) as f: for name in files[split_idx:]: f.write(os.path.abspath(os.path.join(jpg_dir, name)) \n) overlap set(files[:split_idx]) set(files[split_idx:]) print(f训练集 {split_idx} 张验证集 {len(files) - split_idx} 张重叠 {len(overlap)} 张)这里把输出路径写成了绝对路径训练时就不会出现 darknet 找不到文件的幺蛾子。random.seed(42)固定随机种子保证每个人复现出来的划分结果一致。按 14963 张计算训练集 13466 张、验证集 1497 张。划分完之后检查重叠数量正常应为 0。4. darknet 训练前的最后配置cfg 修改、data 文件与日志判断标注转好了训练就能跑吗还差几步。YOLOv3/v4/v5 在 darknet 环境下训练最常翻车的不是数据而是 cfg 参数。改 classes 却忘改 filters 这种事几乎每个群里每天都有人踩。4.1 cfg 文件改三处classes、filters、输入尺寸以 YOLOv3 为例打开 cfg 后要改的参数可以列成一张表参数修改位置取值规则classes3 个 [yolo] 层内等于第 2.3 节统计出的类别总数 Nfilters每个 [yolo] 层前最近的一个卷积层(N 5) * 3width / height[net] 层必须是 32 的倍数如 416、608subdivisions[net] 层显存不够时调大如 16→64filters的计算公式很多人不理解每个预测框要输出 5 个基础量中心点 x、y、宽 w、高 h、objectness再加上 N 个类别的置信度得到N 5乘上每个尺度的 3 个 anchor就是(N 5) * 3。比如类别数 4filters 就是 27类别数 8filters 就是 39。YOLOv4、YOLOv5 的 cfg 换汤不换药计算方式一样。修改时建议用编辑器全局搜索先搜classes把三处全部改成 N再搜filters把每个 yolo 层前最近的卷积层的 filters 都改了别只改一处。输入尺寸也是新手容易随意填的填 608 虽然能提升小目标检测能力但显存占用大幅上涨填 416 则是速度和精度的折中。垃圾检测里很多目标是小物件可以先从 416 起步。4.2 创建 trash.data 与启动训练darknet 训练还差一个数据描述文件内容很简单classes 4 train /data/trash/train.txt valid /data/trash/val.txt names /data/trash/trash.names backup /data/trash/backup/names指向第 3.2 节生成的trash.names里面每行一个类别名行序就是类别 ID。backup目录要提前创建否则训练时模型没法自动存权重。一切就绪后训练命令是mkdir -p /data/trash/backup ./darknet detector train data/trash.data cfg/yolov3-trash.cfg darknet53.conv.74 -mapdarknet53.conv.74是官方预训练骨干权重第一次训练用它能让模型快速收敛如果训练中断了把这个位置换成backup/xxx_last.weights就能断点续训。-map参数让训练过程每隔一段迭代自动在验证集上算 mAP方便观察模型趋势。显存紧张时把 cfg 里的subdivisions从 16 调成 32 或 64一批图拆成更多小份送进显卡总 batch 不变只是变慢了。4.3 训练日志怎么读avg loss 与 mAPdarknet 训练时刷屏的日志里真正要盯的是两个值avg loss和mAP0.5。训练集 13466 张、batch64 的情况下一个 epoch 大约 210 次迭代。前两三百次迭代loss 在 2.x 附近波动是正常的只要不是一路涨都不用慌。当avg loss稳定降到 1.0 以下说明模型已经学到基本特征。这时开始关注 mAP一般会从零点几缓慢爬升。什么时候停不是看 loss 掉到多低而是看验证集 mAP 连续几十个 epoch 不再增长就可以停了继续跑只会过拟合。如果跑了上千次迭代 loss 还在 2 以上优先回去检查标注转换而不是调学习率。5. 常见问题与避坑六个训练翻车现场的排查记录这一节列几个用 VOC 转 darknet 训练时特别常见的坑每条按“现象、原因、解决”写清楚。我不是没踩过这些坑基本都亲自翻车过对照自己的工程看一眼能少折腾好几个晚上。5.1 训练刚开始就报错Failed to open image现象训练启动后几秒控制台连续刷Failed to open image: data/trash/xxx.jpgloss 完全不动。原因train.txt里写的是相对路径而 darknet 的工作目录和你的项目目录不一致或者 txt 里写的是 Windows 盘符路径换到 Linux 上直接失效。解决用第 3.3 节脚本里的os.path.abspath()生成绝对路径重新生成train.txt和val.txt确保每一行都是完整可访问的.jpg路径。5.2 改了 classes 忘了 filters模型直接崩现象启动训练后加载权重时报维度不匹配或者干脆报Segmentation fault程序秒退。原因只改了三个classes没有同步改 yolo 层前面卷积层的filters导致最后一层输出的通道数和下一层能接受的对不上。解决全局搜filters把每个 yolo 层前最近的 filters 改成(N 5) * 3四个类别就填 27八个类别就填 39。改完先不加载预训练权重跑一下./darknet detect确认能正常启动再加权重。5.3 类别 ID 从 1 开始编第一类永远学不会现象训练不报错loss 也正常下降但测试时第一个类别完全不识别第二个类别却经常把第一类的目标也框出来。原因生成 txt 时有人认为类别编号应该从 1 开始结果所有标注的 ID 整体偏移了一位第一个类别成了“无中生有”的 ID 0。解决类别 ID 固定从 0 开始。统计完类别后trash.names里的行序就是 ID转换脚本里用class_names.index(name)获取 ID不要自己手工加 1。5.4 loss 正常但 mAP 为 0txt 坐标出了边界现象训练过程 loss 正常下降跑验证集 mAP 却是 0预测图上一个框都没有。原因标注坐标没有归一化或者中心点和宽高公式算错写进 txt 的是原始像素值甚至负数darknet 读进去后过滤掉了所有预测。解决打开一个转换好的 txt 检查前面几行应该是这样的数值for f in VOCdevkit/labels/*.txt; do awk {if($21||$31||$41||$51) print FILENAME: 坐标越界} $f done这个循环会把所有越界文件列出来。正常文件里全部数值都应该在 0 到 1 之间如果查出来大片越界回到第 3.2 节检查公式里的除法是否漏了img_w和img_h。5.5 显存溢出CUDA out of memory现象训练刚启动就报 CUDA OOM或者跑几十个 iteration 崩一次保住小命全靠手速。原因cfg 默认batch64、subdivisions16等于一次送 4 张图进去算图片尺寸 608 时显存开销被放大好几倍。解决把subdivisions从 16 调到 64这不会改变总 batch只是把 64 张图拆成小批次送进去再把random0关掉多尺度训练还不行就把width和height从 608 降到 416。实测 8GB 显存用 416 subdivisions64 能顺利跑起来。5.6 训练到一半断电只能从头再来现象训练跑了一夜断电断网导致进程中断之前存的权重只有backup里的中间结果重启后有人又拿darknet53.conv.74从零开始训。原因忘了 darknet 支持断点续训重新用预训练权重等于把之前的进度全丢了。解决中断后直接拿backup/xxx_last.weights当预训练权重启动./darknet detector train data/trash.data cfg/yolov3-trash.cfg backup/yolov3-trash_last.weights -map想更稳一点可以顺手把 cfg 里的learning_rate从 0.001 降到 0.0001避免续训时步子迈太大越过之前的收敛点。6. 验证走一遍mAP 计算、单图测试与新增类别的迁移训练训练完的权重不能直接丢进文件夹就完事先跑一次验证拿到 mAP再做单图测试确认效果心里才有底。darknet 的验证和测试命令都集成在同一个二进制里只是入口参数不同。6.1 计算验证集 mAP./darknet detector valid data/trash.data cfg/yolov3-trash.cfg backup/yolov3-trash_last.weights -map这个命令会把验证集里每张图的预测结果写到 result 目录同时输出 mAP0.5。第一次跑建议开着-map方便拿到一个可量化的基线。后面对比不同训练策略时只看这个数字就行。6.2 单图测试./darknet detector test data/trash.data cfg/yolov3-trash.cfg backup/yolov3-trash_last.weights -thresh 0.25 data/test.jpg-thresh控制置信度阈值0.25 是常用值低于它的预测框会被过滤掉。输出的predictions.jpg就是带框结果先扫一眼框的位置对不对再回来看准确率数字。如果小目标漏检严重可以逐张图试 0.1 的阈值确认模型本身学到了而不是阈值卡的太死。6.3 新增一个垃圾类别怎么办项目做大了迟早要加新类别。把新类别的图片标注成 VOC 格式放进 Annotations 和 JPEGImages重新跑一遍第 3.2 节转换脚本——它会自动刷新trash.names和 labels。然后改 cfgclasses加 1filters按新类别数重算加载旧权重继续训练但学习率要降到原来的十分之一。这样旧类别的特征不会被打乱新类别也能在几十个 epoch 内收敛。我之前把四类垃圾扩到八类时旧类别 mAP 只掉了 1 个百分点左右新类别上来就有可用效果。从那以后我每次拿到 VOC 数据集第一件事永远是跑类别统计脚本确认classes、filters、trash.names行序这三处完全对齐再启动训练。这套检查顺序帮我避免了大半的坑希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

温度传感器如何提升智能家居的用户体验? 2026/9/26 11:09:50

温度传感器如何提升智能家居的用户体验?

温度传感器提升智能家居用户体验的核心逻辑就一句话:实时、准确的温度数据 → 设备自动、及时的调节 → 舒适和节能兼得。智能空调、智能窗帘、地暖这些设备"聪明不聪明",很大程度上取决于背后那颗温度传感器够不够快、够不够准。 家居环境的舒…

阅读更多 →
2026版AI智能体爆发风口,小白程序员大模型转行指南 2026/9/26 11:09:50

2026版AI智能体爆发风口,小白程序员大模型转行指南

深耕技术行业、或是零基础刚入行的小伙伴应该都深有感触:当下传统互联网、前后端开发岗位内卷愈发严重,求职竞争白热化,简历投递大多石沉大海。不仅新人难入行,老程序员的职业晋升、薪资涨幅也逐渐触顶,职业发展陷入瓶…

阅读更多 →
Coze 智能体 + 工作流实战:内容自动生成与插图输出配置指南 2026/9/26 11:09:50

Coze 智能体 + 工作流实战:内容自动生成与插图输出配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
老项目接入MCP的渐进式收益:TaoToken统一Key通道下的配置骨架与验证 2026/9/26 11:09:44

老项目接入MCP的渐进式收益:TaoToken统一Key通道下的配置骨架与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
嵌入式开发如何‘先混进去’:从应届生到主力工程师的实战路径 2026/9/26 11:09:44

嵌入式开发如何‘先混进去’:从应届生到主力工程师的实战路径

1. 这句话背后的真实行业生态:为什么“先混进去”不是玩笑,而是生存策略“其实嵌入式开发岗位,都是先混进去再说!”——这句话最近在技术社区、校招群和应届生论坛里反复刷屏,语气带着自嘲,但底下全是点赞和…

阅读更多 →
Agent开发入门到实战,小白也能快速掌握大模型核心技术! 2026/9/26 11:09:44

Agent开发入门到实战,小白也能快速掌握大模型核心技术!

本文强调Agent开发是大模型领域的核心技能,建议程序员不要局限于基础应用,而应学习独立开发智能Agent。文章提出了一个分四阶段的学习路线:第一阶段为基础入门,理解核心概念;第二阶段为掌握原理与范式;第三…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉