ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

鹅数据集VOC和YOLO格式目标标注370张:直接进训练管线

2026/10/1 17:45:00 拓冰建站 浏览量
鹅数据集VOC和YOLO格式目标标注370张:直接进训练管线 简介本资源为一份面向目标检测学习者的鹅类图像数据集采用VOC与YOLO双格式标注适合从事禽类识别、农业智能化或计算机视觉入门实践的开发者与研究人员使用。压缩包共1111个文件包含370张jpg图片、370个xml标注文件与371个txt标注文件整体约26.82MB图片大小在1-500KB之间解压后即可直接用于训练与验证。所有图片均由labelImg人工标注类别统一为goose标注过程强调边界框选准确、目标覆盖完整并通过一致性检查减少漏标与误标。目前已有85人学习下载。数据集目录结构清晰图片、xml与txt分文件夹存放便于快速接入YOLO或VOC标准流程也可用于格式转换、模型微调与检测效果对比等场景为鹅类目标检测任务提供即用型数据基础。1. 鹅数据集 VOC 和 YOLO 格式目标标注 370 张一份能直接进训练管线的现成货手上有个鹅群计数或者鹅只行为分析的需求卡在第一步——没有标注数据。自己扛着相机去养殖场拍两天回来再用 labelImg 一张张框370 张图够你框到怀疑人生。这份「鹅数据集 VOC 和 YOLO 格式目标标注 370 张」就是冲着这个痛点来的370 张 jpg 原图配齐了 VOC 的 xml 和 YOLO 的 txt 两套标注类别只有一个——goose。压缩包解开就是三个文件夹图片、xml、txt 各归各的不用装额外工具就能直接喂给 YOLOv5/v8 或者转成 COCO 跑别的框架。适合谁做禽类检测的算法同学、搞智慧养殖落地的工程团队以及拿目标检测练手但不想在标注上耗时间的人。它不解决模型结构问题但能让你把精力从「造数据」挪到「调模型」上这个价值对一线来说很实在。2. 拆开压缩包先看什么目录结构、标注格式与选型理由2.1 三个文件夹的职责划分与文件对应关系解压之后你会看到类似这样的结构不同人打包习惯略有差异但核心就三块goose_dataset/ ├── JPEGImages/ # 370 张 jpg 原图命名如 goose_276.jpg ├── Annotations/ # 370 个 VOC 格式 xml与图片同名 └── labels/ # 370 个 YOLO 格式 txt与图片同名图片命名是goose_数字.jpg这种风格数字不连续说明是从更大池子里筛出来的不是摆拍连拍。三个文件夹靠文件名对齐goose_41.jpg对应goose_41.xml和goose_41.txt。拿到手第一件事不是急着训练而是抽查对齐率——用下面这段脚本跑一遍确认没有孤儿文件。import os img_dir goose_dataset/JPEGImages xml_dir goose_dataset/Annotations txt_dir goose_dataset/labels imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} xmls {os.path.splitext(f)[0] for f in os.listdir(xml_dir) if f.endswith(.xml)} txts {os.path.splitext(f)[0] for f in os.listdir(txt_dir) if f.endswith(.txt)} print(图片数:, len(imgs), xml数:, len(xmls), txt数:, len(txts)) print(有图无xml:, imgs - xmls) print(有图无txt:, imgs - txts) print(有xml无图:, xmls - imgs)逻辑很直白用文件名主干做集合运算把三类文件各自的主干集合拿出来比对。参数上唯一要注意的是扩展名大小写——有些打包工具会把.JPG写成大写endswith(.jpg)就漏了稳妥点可以统一f.lower().endswith(.jpg)。跑完如果三个集合完全相等说明这份数据整理得干净可以直接进下一步如果有差异先别删文件大概率是命名带空格或者多了个副本手动核一下。2.2 VOC 与 YOLO 双格式并存的实际意义很多人会问既然要训 YOLO为什么还留一份 VOC 的 xml这不是冗余是留后路。VOC 的 xml 是「原始标注记录」里面存的是绝对像素坐标xmin, ymin, xmax, ymax还带图片宽高、类别名、甚至标注时的难度标记。YOLO 的 txt 是「训练就绪格式」存的是归一化后的class_id cx cy w h中心点加宽高全部除以图宽图高。两者的关系是单向可逆的——xml 能无损转成 txt但 txt 转回 xml 会丢掉图片尺寸以外的元信息。实际干活时这个双格式能省事你想换框架比如从 YOLO 切到 Detectron2 或者 MMDetection它们吃 COCO 或 VOC这时候直接拿 xml 转不用回头重新标注。反过来如果你只想快速跑 YOLOv8txt 已经躺在那儿了改个data.yaml路径就能开训。选型上我的建议是以 xml 为存档以 txt 为消耗品。txt 训练时可能被各种脚本改写、增强、切分改坏了不心疼xml 留着兜底。2.3 用 labelImg 标注的痕迹与质量初判这份数据是用 labelImg 标的从 xml 里能看出典型特征folder标签通常写的是图片所在文件夹名path是标注时的绝对路径size里有 width、height、depth。这些字段对训练没用但能帮你判断标注是否规范。比如size里的宽高如果和实际图片对不上说明标注时图片被缩放过坐标就偏了。快速抽检可以写个小脚本把 xml 里的框画回图上肉眼扫几张import xml.etree.ElementTree as ET import cv2 def draw_voc(img_path, xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() img cv2.imread(img_path) for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) x1 int(bbox.find(xmin).text) y1 int(bbox.find(ymin).text) x2 int(bbox.find(xmax).text) y2 int(bbox.find(ymax).text) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, name, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(out_path, img) draw_voc(goose_dataset/JPEGImages/goose_276.jpg, goose_dataset/Annotations/goose_276.xml, check_276.jpg)这段代码把 xml 里的框和类别名画到原图上输出一张检查图。参数上cv2.rectangle的线宽设 2 够看清putText的位置放在框上方避免遮挡。抽 10 到 20 张不同编号的图跑一遍重点看三件事框有没有明显偏移、有没有漏标图里有鹅但没框、有没有多标框里没鹅。标注遵循里提到的「一致性检查」就是这个意思只不过原话是让人对比咱们用脚本先过一遍效率更高。3. 从 VOC 到 YOLO转换脚本、data.yaml 配置与训练接入3.1 手写 xml 转 txt 脚本与坐标归一化细节虽然数据集里已经带了 txt但你要做数据增强、重新划分训练验证集或者想自己控制类别映射还是得会转。下面这个脚本把 VOC 的 xml 批量转成 YOLO 的 txt逻辑是读 xml 拿绝对坐标除以图片宽高做归一化再按class cx cy w h写出去。import os import xml.etree.ElementTree as ET from PIL import Image classes [goose] # 类别列表顺序决定 class_id xml_dir goose_dataset/Annotations img_dir goose_dataset/JPEGImages out_dir goose_dataset/labels_from_xml os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue stem os.path.splitext(xml_file)[0] img_path os.path.join(img_dir, stem .jpg) if not os.path.exists(img_path): print(缺图跳过:, stem) continue w, h Image.open(img_path).size tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 归一化并转中心点格式 cx (x1 x2) / 2.0 / w cy (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h # 边界裁剪防止标注越界导致训练报错 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) bw min(max(bw, 0.0), 1.0) bh min(max(bh, 0.0), 1.0) lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines))几个关键点值得说透。第一classes列表的顺序就是 class_id这份数据只有 goose 一个类所以 id 恒为 0但如果你后面要加「鸭」「鸡」做多类顺序一旦定了就不能改改了所有旧标签全错。第二归一化用的是 PIL 读出来的真实宽高不是 xml 里写的size因为 xml 里的尺寸偶尔会因为标注工具版本问题不准以原图为准最稳。第三加了边界裁剪min(max(...))把值卡在 0 到 1 之间。别小看这一步标注时手抖把框拖出图外归一化后会出现负数或大于 1 的值YOLO 训练时直接给你抛个 assert 失败排查半天。第四保留 6 位小数够用YOLO 官方也是这个精度写太多位纯属浪费磁盘 IO。3.2 data.yaml 怎么写与目录切分YOLOv5/v8 靠一个data.yaml描述数据在哪、有几类、类名是什么。这份数据 370 张量不大建议按 8:2 切训练和验证别单独再切测试集否则验证集太小指标抖动厉害。切分脚本import os import random import shutil random.seed(42) # 固定种子保证每次切分一致 img_dir goose_dataset/JPEGImages lbl_dir goose_dataset/labels base goose_yolo for split in [train, val]: os.makedirs(f{base}/images/{split}, exist_okTrue) os.makedirs(f{base}/labels/{split}, exist_okTrue) stems [os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(stems) cut int(len(stems) * 0.8) train_stems, val_stems stems[:cut], stems[cut:] for split, subset in [(train, train_stems), (val, val_stems)]: for stem in subset: shutil.copy(f{img_dir}/{stem}.jpg, f{base}/images/{split}/{stem}.jpg) shutil.copy(f{lbl_dir}/{stem}.txt, f{base}/labels/{split}/{stem}.txt) print(train:, len(train_stems), val:, len(val_stems))random.seed(42)是血泪经验——不固定种子每次跑切分结果不一样你调参时以为模型变了其实是数据换了指标对比全是玄学。切完目录结构是goose_yolo/images/train、goose_yolo/labels/train这样成对出现YOLO 默认会去 images 同级找 labels路径别写错。对应的data.yamlpath: ./goose_yolo train: images/train val: images/val nc: 1 names: 0: goosepath是根目录train和val是相对路径。nc是类别数这里 1。names用字典或列表都行字典更直观。写完拿 YOLO 官方校验命令过一遍确认没有路径错误再开训。3.3 接进 YOLOv8 训练与首轮参数建议环境配好之后训练命令就一行yolo detect train datagoose_yolo/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16参数逐个说modelyolov8n.pt用 nano 版370 张图用大模型纯属浪费n 版足够跑出可用结果想涨点再换 s。epochs100是起步值数据量小100 轮通常能看到收敛配合早停不会过训。imgsz640是 YOLO 默认输入尺寸鹅在图中占比如果偏小可以提到 960 试试但显存要够。batch16在 8G 显存上比较稳爆显存就降到 8。训练起来后盯两个东西cls_loss和mAP50。cls_loss 持续下降说明分类在学mAP50 涨到 0.8 以上这份数据就算跑通了。如果 mAP 卡在 0.3 不动先别怀疑模型回头查标注——大概率是框太松或者漏标鹅这种目标边界模糊框松一点 IoU 就掉。4. 避坑与排查这份鹅数据集最容易翻车的五个地方4.1 图片与标注文件名对不上导致静默丢样本现象训练日志里train的图片数比预期少比如切了 296 张进去日志显示只有 280。原因YOLO 加载时会跳过找不到对应 txt 的图片而且默认不报错静默跳过。解决训练前用 2.1 节的对齐脚本跑一遍或者直接看labels/train和images/train的文件数是否相等。不等就补别指望框架提醒你。4.2 标注框越界引发训练中断现象训练跑几十步突然报AssertionError: normalized coordinates out of range或者类似越界提示。原因xml 里某些框的坐标超出了图片边界转 txt 时归一化后出现负数或大于 1。解决用 3.1 节脚本里的边界裁剪逻辑min(max(...))卡住。如果已经生成了 txt写个脚本扫一遍所有 txt把越界行找出来定位到具体图片回 labelImg 修。4.3 单类数据集误配 nc 导致维度不匹配现象模型加载时报size mismatch for model.24.m.0.weight之类的维度错误。原因data.yaml里nc写成了别的数或者names里塞了多个类但实际只有 goose。解决这份数据nc必须是 1names只留 goose。改完 yaml 重新跑别去动模型权重。4.4 图片尺寸差异大导致增强后目标变形现象训练时 mAP 波动大同一批验证图有时准有时不准。原因370 张图尺寸不统一letterbox 填充后鹅的宽高比被改变模型学到的形状特征不稳定。解决训练前统计一下图片尺寸分布如果长宽比差异超过 2:1考虑统一 resize 到相近尺寸再训或者开 mosaic 增强让模型适应形变。这份数据图片大小 1-500KB尺寸大概率杂值得先看一眼。4.5 验证集切分随机性造成指标不可复现现象同样的代码跑两次mAP 差好几个点。原因切分时没固定随机种子每次训练验证集不一样。解决3.2 节脚本里的random.seed(42)必须保留而且切分只做一次把切好的目录固定下来后续所有实验都用同一份别每次训练重新切。5. 把 370 张用出 3700 张的效果增强策略与标注复核技巧数据量小是这份数据集的天花板370 张训个能用的检测器没问题但想再往上提点得在增强和标注质量上抠。YOLO 自带的增强参数里mosaic和mixup对小数据集最管用mosaic 把四张图拼一张等于每轮看到的组合数翻几倍mixup 做透明度叠加能缓解过拟合。开法是在训练命令里加mosaic1.0 mixup0.1mosaic 默认就是 1mixup 从 0.1 起步太高会让鹅和背景糊成一团。但增强是双刃剑。鹅的形态特征——长脖子、扁嘴、体型——在 mosaic 拼接后可能被裁掉一半模型学到的就是残缺特征。我的习惯是前 50 轮开满增强让模型见世面后 50 轮关掉 mosaic 和 mixup用原图微调让模型回归真实分布。这个「先增强后收敛」的节奏在多个小数据集上验证过比全程开增强的 mAP 高 2 到 3 个点。标注复核这块370 张全人眼过一遍不现实但可以用模型反查。拿训到一半的模型对训练集做推理把预测框和标注框 IoU 低于 0.5 的图挑出来这些就是「模型觉得不对」的样本大概率标注有问题。脚本思路from ultralytics import YOLO import os model YOLO(runs/detect/train/weights/best.pt) results model.predict(goose_yolo/images/train, save_txtTrue, conf0.25) # 对比 save_txt 输出的预测与原始标注IoU 低的挑出来人工看 # 具体比对逻辑略核心是拿预测当第二双眼睛conf0.25是置信度阈值调低能捞出更多可疑样本调高只看高置信的。跑完把低 IoU 的图列个清单重点看是不是漏标了小鹅、或者框只框了身子没框到头。鹅群密集时漏标很常见补上这些漏标比加一百张新图管用。还有个技巧是「类别名统一」。这份数据类别是 goose但如果你后续混入其他来源的鹅数据别人可能标成Goose、goose_、鹅合并时全乱。统一转小写、去空格、去下划线在转换脚本的classes列表里做映射别等到训练报未知类别才回头改。最后说个习惯。我每次拿到新数据集不管多干净都强制走一遍「对齐检查 → 抽检画框 → 越界扫描 → 切分固定种子」这四步跑完才开训。这四步加起来不到十分钟但能省掉后面几小时的排查。这份鹅数据集整理得算规整VOC 和 YOLO 双格式省了不少事370 张的量适合快速验证想法真要上生产还得按上面的路子扩。希望帮到你。本文还有配套的精品资源点击获取