简介这份目标检测猪群数据集面向从事计算机视觉与深度学习实践的开发者、农业智能化研究者及高校学生用于训练和评估猪群识别与定位模型可服务于农业监控、动物健康监测与农场管理等场景。压缩包共2000个文件以1448张jpg原始图像和1448个xml标注文件为主整体约31.79MB目录分为Annotations、JPEGImages及一个用途待定的新建文件夹结构清晰便于按模块取用。标注由labelimg工具生成每张图片对应一份XML记录边界框坐标与类别标签可直接作为Faster R-CNN、YOLO、SSD等模型的训练输入。目前已有767人学习下载适合需要现成标注数据快速验证算法、搭建猪群检测基线或扩充农业视觉项目样本的读者参考使用。1. 猪群目标检测数据集从标注文件到训练可用的完整链路猪场里数猪这件事靠人眼盯监控基本不现实——猪会挤在一起、会趴着不动、会在夜里变成一团模糊的灰影。这份「目标检测猪群数据集」要解决的就是这个场景把上千张猪群图像和对应的 XML 标注整理成标准目录结构直接喂给 YOLO、Faster R-CNN 或 SSD 这类检测器训练。数据集本身不复杂Annotations 放 labelimg 生成的 XMLJPEGImages 放原图另有一个用途待确认的「新建文件夹」。适合两类人一是想跑通目标检测全流程但缺真实场景数据的新手二是做农业视觉监控、需要快速验证模型可行性的从业者。下面按「结构拆解 → 格式转换 → 训练接入 → 避坑 → 进阶验证」的顺序把这份资源拆到能直接复现的程度。2. 目录结构与标注格式先搞清楚手里到底有什么2.1 三个目录的实际分工拿到压缩包先别急着解压到桌面用tree或文件管理器看清楚层级。按项目描述结构是目标检测猪群数据集/ ├── Annotations/ # labelimg 生成的 XML 标注文件 │ ├── 102.xml │ ├── 1412.xml │ └── ... ├── JPEGImages/ # 原始 JPG 图像 │ ├── 102.jpg │ ├── 1412.jpg │ └── ... └── 新建文件夹/ # 用途未明确需人工确认Annotations 和 JPEGImages 是 PASCAL VOC 的标准命名习惯不是这个数据集独创的。关键约束是XML 文件名必须和 JPG 文件名一一对应102.jpg对应102.xml缺一个都会在训练时被跳过或报错。项目正文里列出的102.jpg、1412.jpg、182.jpg、1067.jpg、1421.jpg、1424.jpg、596.jpg、1079.jpg、1162.jpg、571.jpg只是示例实际数量以解压后为准。「新建文件夹」这个名字是中文在 Linux 训练环境里容易出编码问题。我一般会先把它重命名为extras或直接移出数据集根目录避免后续脚本遍历时把无关文件卷进来。2.2 XML 里到底存了什么labelimg 生成的 XML 遵循 PASCAL VOC 格式核心字段如下字段含义训练时的作用folder图片所在目录名一般忽略路径以实际为准filename对应 JPG 文件名用于匹配图像size/width,height图像宽高坐标归一化、resize 校验object/name类别标签猪群数据集里通常是pig或中文object/bndboxxmin,ymin,xmax,ymax边界框像素坐标object/difficult是否难样本评估时是否忽略用一段 Python 快速统计类别分布和框数量确认标注没有大面积缺失import os import xml.etree.ElementTree as ET from collections import Counter ann_dir 目标检测猪群数据集/Annotations class_counter Counter() box_counter 0 empty_files [] for fname in os.listdir(ann_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, fname)) root tree.getroot() objs root.findall(object) if not objs: empty_files.append(fname) for obj in objs: name obj.find(name).text class_counter[name] 1 box_counter 1 print(类别分布:, class_counter) print(总框数:, box_counter) print(无目标文件:, empty_files[:10], 共, len(empty_files))这段脚本做三件事遍历所有 XML、统计每个类别的框数、找出没有标注目标的空文件。参数说明ann_dir改成你解压后的实际路径如果类别名是中文class_counter会原样输出后续训练前需要统一映射成英文。空文件不一定是错误——有些图可能确实没有猪但如果空文件占比超过 10%就要怀疑标注是否完整。2.3 图像与标注的一致性校验在投入训练前必须确认每张 JPG 都有对应 XML反之亦然。常见做法是写一个集合差集检查import os img_dir 目标检测猪群数据集/JPEGImages ann_dir 目标检测猪群数据集/Annotations imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.lower().endswith(.jpg)} anns {os.path.splitext(f)[0] for f in os.listdir(ann_dir) if f.endswith(.xml)} print(有图无标注:, sorted(imgs - anns)[:20]) print(有标注无图:, sorted(anns - imgs)[:20])如果「有图无标注」数量很大说明这批图是负样本或漏标如果「有标注无图」存在训练时直接报文件找不到。我一般会把这两类文件先挪到_orphan目录保证训练集干净。3. 从 VOC XML 到 YOLO 格式转换脚本与参数核对3.1 为什么不能直接拿 XML 训练 YOLOYOLO 系列v5/v8/v11默认读取的是每张图一个.txt文件每行格式为class_id x_center y_center width height且坐标是归一化到 0~1 的。而 labelimg 的 XML 存的是绝对像素坐标xmin,ymin,xmax,ymax。两者之间差一次转换转换错了模型学到的框会整体偏移这是血泪经验里最常见的一类翻车。转换公式x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height3.2 完整转换脚本import os import xml.etree.ElementTree as ET # 类别映射按你的实际类别名修改 classes [pig] img_dir 目标检测猪群数据集/JPEGImages ann_dir 目标检测猪群数据集/Annotations out_dir labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(ann_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 边界裁剪防止标注越界导致归一化后超出 0~1 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) xc (xmin xmax) / 2.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) out_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))逻辑说明脚本逐 XML 解析读取图像宽高做归一化对坐标做上下界裁剪最后按 YOLO 格式写入labels/。参数说明classes必须和 XML 里的name完全一致大小写敏感out_dir建议放在数据集根目录下和images/平级。如果 XML 里类别是中文「猪」要么改脚本里的classes [猪]要么先批量替换 XML 文本后者风险更高不推荐。3.3 划分训练集与验证集YOLO 训练需要train.txt和val.txt每行是图像绝对路径或相对路径。常见做法是 8:2 划分import os import random img_dir 目标检测猪群数据集/JPEGImages imgs [f for f in os.listdir(img_dir) if f.lower().endswith(.jpg)] random.seed(42) random.shuffle(imgs) split int(len(imgs) * 0.8) train_imgs imgs[:split] val_imgs imgs[split:] with open(train.txt, w) as f: for name in train_imgs: f.write(os.path.abspath(os.path.join(img_dir, name)) \n) with open(val.txt, w) as f: for name in val_imgs: f.write(os.path.abspath(os.path.join(img_dir, name)) \n) print(训练集:, len(train_imgs), 验证集:, len(val_imgs))random.seed(42)保证每次划分一致方便复现。如果数据集里存在同一头猪的连续帧随机划分会导致训练集和验证集高度相似验证指标虚高。更稳妥的做法是按拍摄时段或场景分组划分但这份数据集没有提供分组信息只能先按随机划分跑通。4. 接入 YOLO 训练配置文件与启动命令4.1 数据集 YAML 怎么写YOLOv8/v11 用一份 YAML 描述数据路径和类别path: /home/user/pig_dataset train: train.txt val: val.txt nc: 1 names: 0: pig参数说明path是数据集根目录train/val是相对于path的 txt 路径nc是类别数猪群数据集通常为 1names的键必须从 0 开始连续。如果转换脚本里classes [pig]这里就写0: pig两边不一致会直接报类别越界。4.2 启动训练与关键参数yolo detect train \ datapig_dataset.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ projectruns/pig \ nameexp1参数说明modelyolov8n.pt是轻量预训练权重适合先验证流程imgsz640是输入分辨率猪群图像如果分辨率很高可以试 960 或 1280但显存占用会明显上升batch16在 8GB 显存上通常能跑爆显存就降到 8 或 4epochs100是起步值看验证集 mAP 是否还在涨再决定要不要加。训练日志里重点看box_loss和mAP50前者不降说明标注或学习率有问题后者不涨说明数据量或类别难度到瓶颈了。4.3 用 labelimg 补标或修正如果校验阶段发现漏标直接用 labelimg 打开JPEGImages目录继续标labelimg 目标检测猪群数据集/JPEGImages \ 目标检测猪群数据集/Annotations/predefined_classes.txtpredefined_classes.txt里写一行pig这样每次画框默认就是猪不用手动选类别。注意 labelimg 默认保存为 VOC XML和现有格式一致不会引入额外转换。标完后重新跑一遍第 2.3 节的差集校验确认没有新增孤儿文件。5. 避坑与排查这份数据集最容易翻车的五个点5.1 现象训练报「No labels found」原因YOLO 找不到.txt标签或者train.txt里的图像路径和labels/目录没有按约定对应。YOLO 的默认规则是把图像路径里的images替换成labels如果你把标签放在别处它不会自动找。解决要么把labels/放在和images/平级的目录下要么在 YAML 里显式指定labels路径。最稳的做法是保持images/train/和labels/train/的镜像结构而不是用 txt 列表。5.2 现象mAP 一直是 0 或极低原因类别映射错了。XML 里是中文「猪」转换脚本里写的是pigcls_name not in classes导致所有框被跳过生成的 txt 是空的。解决先跑第 2.2 节的类别统计脚本确认 XML 里的真实类别名再同步修改转换脚本和 YAML 的names。不要凭猜测写类别。5.3 现象框整体偏移或大小不对原因归一化时用错了宽高。常见错误是拿 XML 里的size字段但那个字段可能和实际图像尺寸不一致labelimg 有时会写错。解决用 OpenCV 或 PIL 重新读取图像真实宽高而不是信任 XML 里的sizefrom PIL import Image w, h Image.open(img_path).size如果 XML 的size和真实尺寸差异超过 1 像素以真实尺寸为准。5.4 现象中文路径导致脚本报编码错误原因「新建文件夹」和可能的其他中文目录名在 Linux 或某些 Python 环境下触发UnicodeDecodeError。解决统一重命名为英文或者在脚本开头加# -*- coding: utf-8 -*-并用os.listdir时显式指定编码。更彻底的做法是整个数据集路径不含中文。5.5 现象验证集指标虚高实际推理一塌糊涂原因训练集和验证集里有同一场景的连续帧模型记住了背景而不是猪的特征。解决如果数据集有拍摄时间或视频来源信息按来源分组划分没有的话至少把random.seed固定并人工抽查验证集图像确认没有和训练集高度相似的帧。这个坑在农业数据集里特别常见因为很多图是从同一段监控视频里抽的。6. 进阶验证用混淆矩阵和单图推理确认模型真的学到了猪训练跑完不等于模型能用。我一般会做两件事看混淆矩阵、跑单图推理。混淆矩阵在 YOLO 训练结束后自动生成在runs/pig/exp1/confusion_matrix.png。猪群数据集只有一类矩阵是 2x2pig background。重点看 background 那一列如果大量猪被预测成背景说明漏检严重可能是标注框太小或模型容量不够如果 background 被大量预测成猪说明误检多可能是负样本不足。单图推理用命令行yolo detect predict \ modelruns/pig/exp1/weights/best.pt \ source目标检测猪群数据集/JPEGImages/102.jpg \ conf0.25 \ saveTrue参数说明conf0.25是置信度阈值调低会召回更多框但误检增加调高则相反。猪群密集场景建议先试 0.25看效果再微调。输出图在runs/detect/predict/下直接打开对比原图重点看三类情况挤在一起的猪有没有被拆成多个框、趴着的猪有没有被漏掉、远处的小目标有没有被检出来。如果漏检集中在某个尺度可以试多尺度训练在训练命令里加scale0.5让模型见到更多尺寸变化。如果误检集中在某个背景区域比如料槽、栏杆把这些图作为负样本加入训练集重新跑一轮。从那以后我每次拿到新数据集都强制先跑一遍类别统计和图像-标注差集校验再动训练命令。这两步花不了五分钟但能省掉后面几个小时的无效训练。希望帮到你。本文还有配套的精品资源点击获取