简介猪只计数数据集.zip 是一份面向智能养殖与深度学习视觉识别场景的标注数据专为训练猪舍环境中的目标检测模型而准备可用于解决养殖管理中自动清点猪只数量的实际需求。压缩包共1000个文件含500张摄像头视角拍摄的猪舍jpg图像以及500份配套的JSON标注文件标注采用labelme标准以边界框形式定位每头猪在图像中的位置整体大小约631MB。目前已有2718人学习或下载适合研究YOLO、Faster R-CNN、Mask R-CNN等主流检测算法的学生和工程师。得益于真实养殖场景的图像来源拿到数据后可直接开展图像预处理、标准化与随机旋转/裁剪等数据增强实验也可结合迁移学习对预训练模型进行微调借助mAP、精确率和召回率等评估指标能完成从训练、验证到测试的完整算法闭环。整套数据按图片与标注一一对应组织便于批量解析边界框并划分训练集与测试集无论是复现经典检测模型还是处理复杂光照、遮挡场景都能提供可落地的数据支撑。1. 猪只计数数据集先想清楚你要落地的是什么养猪场里最不起眼却最刚需的事是数猪。几百头猪挤在栏里人要点数得数到眼瞎而且猪会动往往数着数着就乱了。这个猪只计数数据集 zip装的就是从真实猪栏场景里采集的图像和逐头猪的标注框目标是让深度学习模型学会“看见猪就框出来框完数一下有几个框”。对做智慧养殖算法方案的人、选目标检测方向做毕设的学生以及想把 YOLO 这类模型落到农业场景的从业者这是直接能用的训练原料。它的难点不在“认识猪”这个概念而在于猪栏里的遮挡、脏污、光照不均会让计数任务从“目标检测”变成“密集场景下的稳定性挑战”这一点从拿到数据集那一刻就要有心理准备。2. 打开压缩包目录结构、标注格式与可视化校验2.1 压缩包里通常装了什么拿到这个数据集第一步不是急着训练而是先摸清目录结构。这类猪只计数数据集常见的组织方式是把图像和标注分开存放同时提前分好训练集、验证集和测试集。我一般会先解压然后用 tree 命令看一层全貌unzip 猪只计数数据集.zip -d pig_count cd pig_count tree -L 2典型的结构是pig_count/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ │ ├── train/ │ ├── val/ │ └── test/ └── README.md图像目录放的是原始图片标注目录放的是与图片同名的标注文件。最重要的一步是检查 images 和 annotations 两个目录下的文件是否一一对应这一步漏了后面训练会让你怀疑人生的。常见做法是写个小脚本比对文件名确保每张图都有标注、每个标注都能找到图数量对不上就说明这份数据不完整最好先补全再开工。2.2 三种标注格式的差异猪只计数数据集的标注格式从行业实践看主要有三种VOC 的 XML、COCO 的 JSON 和 YOLO 的 txt 纯文本。我们经常从开源渠道拿到的数据VOC 和 COCO 格式居多而 YOLO 系列模型训练直接吃 txt 格式。三种格式的核心差异在于坐标的表示方式格式文件类型坐标表示典型适用场景VOCXML左上角 x、y 加宽高单位像素标注工具默认导出、Pascal VOC 系列COCOJSON左上角 x、y 加宽高单位像素附带分割多边形Detectron2、MMDetection 系列YOLOtxt中心点 x、y 加宽高数值归一化到 0~1YOLOv5/v8、Ultralytics 系列理解这三种格式的转换关系是这份数据集能不能用起来的第一步。YOLO 格式的归一化坐标意味着它跟图像分辨率解耦但 VOC 和 COCO 格式的像素坐标一旦缩放图像标注也得跟着变不然框就偏了。拿到数据集第一件事先看它是什么格式再决定要不要做转换。2.3 拿到数据先做可视化校验我拿到一份新的目标检测数据集永远先做可视化不验证直接训练等于闭眼开车。写一个简单的画框脚本随机挑十几张图把标注框画上去肉眼看框跟猪对不对得上。这一步能发现标签错位、坐标归一化错误、框选位置偏移等一系列问题。import cv2 import os import random def draw_yolo_boxes(image_dir, label_dir, output_dir, sample_num20): 将 YOLO txt 标注画到图上用于校验标注是否正确 image_dir: 图像目录 label_dir: 同名的 txt 标签目录 output_dir: 画出框后的结果输出目录 os.makedirs(output_dir, exist_okTrue) img_files [f for f in os.listdir(image_dir) if f.endswith((.jpg, .jpeg, .png))] sample random.sample(img_files, min(sample_num, len(img_files))) for img_name in sample: img cv2.imread(os.path.join(image_dir, img_name)) h, w img.shape[:2] label_path os.path.join(label_dir, os.path.splitext(img_name)[0] .txt) if not os.path.exists(label_path): print(f[skip] {img_name} 无对应标签) continue with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:5]) # YOLO 格式是归一化坐标需要乘回图像宽高 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) # 画框和类别 ID cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls_id), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) out_path os.path.join(output_dir, img_name) cv2.imwrite(out_path, img) print(f[saved] {out_path}) if __name__ __main__: draw_yolo_boxes(pig_count/images/train, pig_count/annotations/train, check_output)这里的关键是坐标换算YOLO txt 里存的是归一化的中心点坐标和宽高画框时先算左上角和右下角再乘图像宽高。我见过不少人直接拿归一化坐标去画框画出来的框缩在图像左上角一小块还以为是数据集坏了其实是换算逻辑没对。校验时重点看三件事框是否贴合猪的身体而不是只框了一半、有没有跨图像的标注错位、类别 ID 是否符合预期。这批检查做完数据才算真正属于你了。3. 用 YOLOv8 把数据集跑起来从 VOC 转 YOLO 到训练出权重3.1 环境准备装什么、验什么训练猪只计数模型我用得最多的是 Ultralytics 的 YOLOv8它把训练、验证、导出封装得比较干净对单类目标检测任务来说足够简单可靠。环境准备只需要一个 Python 环境和 GPU安装命令很直接pip install ultralytics python -c import torch; print(torch.cuda.is_available())第二行命令用来确认 PyTorch 能不能正常调用 GPU。如果打印结果是 False说明 CUDA 版本和 PyTorch 不匹配或者压根没装 GPU 版 PyTorch。不要去问为什么训练这么慢先把这个检查做了。显存方面猪只数据集单张图通常分辨率不会太低我习惯用 8G 以上显存起步不然 batch size 会被压得很小训练效果会打折扣。3.2 把 XML 标注转成 YOLO txt如果数据集给的是 VOC 格式的 XML那就得先转换成 YOLOv8 需要的 txt 格式。这一步的坑在坐标换算和类别索引。我写过一个转换脚本核心逻辑是从 XML 解析像素坐标换算成归一化坐标并写到 txt同时做边界裁剪避免坐标略微超出图像范围导致训练报错。import xml.etree.ElementTree as ET import os def voc_xml_to_yolo_txt(xml_path, txt_path, class_names, img_width, img_height): 将 VOC 格式 XML 转换为 YOLO 格式 txt 注意: VOC 坐标为像素坐标YOLO 需要归一化到 0~1 tree ET.parse(xml_path) root tree.getroot() # 从 XML 中读图像宽高不用外部传入更稳妥 size root.find(size) img_width int(size.find(width).text) img_height int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: # 不在类别列表里的目标直接跳过避免索引越界 print(f[warn] 未知类别 {name} 已跳过) continue cls_id class_names.index(name) xmlbox obj.find(bndbox) x1 float(xmlbox.find(xmin).text) y1 float(xmlbox.find(ymin).text) x2 float(xmlbox.find(xmax).text) y2 float(xmlbox.find(ymax).text) # 边界裁剪防止标注越界 x1 max(0, min(x1, img_width)) y1 max(0, min(y1, img_height)) x2 max(0, min(x2, img_width)) y2 max(0, min(y2, img_height)) # 归一化: 中心点坐标 / 宽高 cx ((x1 x2) / 2) / img_width cy ((y1 y2) / 2) / img_height bw (x2 - x1) / img_width bh (y2 - y1) / img_height lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines)) # 单类任务目标只有 pig类别索引为 0 class_names [pig] xml_dir pig_count/annotations/train txt_dir pig_count/labels/train os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue xml_path os.path.join(xml_dir, xml_file) txt_path os.path.join(txt_dir, xml_file.replace(.xml, .txt)) voc_xml_to_yolo_txt(xml_path, txt_path, class_names, None, None)转换脚本里最容易翻车的点是坐标源有的数据集存的不是 bndbox 而是 points 多边形或者 XML 里嵌套层级不同。我一般会在脚本里加打印抽样两三张检查换算后的数值范围如果 cx 或 bw 小于 0 或大于 1一定是解析逻辑跟实际 XML 结构对不上。边界裁剪那段代码不是多余防御猪只数据里偶发标注框超出图像边缘不裁剪的话训练时有些增强操作会直接报错。3.3 写 data.yaml 并启动训练数据转换好之后需要给 YOLOv8 一份数据配置文件告诉它训练集、验证集在哪类别叫什么。这个 YAML 文件极其简单但路径写错是高频错误# pig_data.yaml path: /home/yourname/pig_count # 改成你本机的数据集根路径 train: images/train val: images/val test: images/test names: 0: pig启动训练的命令yolo detect train \ datapig_data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0参数说明model 用 yolov8s.pts 是 small 版本猪只检测单类任务不需要上 xl 级大模型s 级性价比最高imgsz640 是输入分辨率猪栏图片如果原始分辨率高且猪密集可以试试 960后面会单独说batch16 取决于显存8G 显存跑 640 分辨率用 16 基本是极限再大就 OOM 了。训练开始后Ultralytics 会在 runs/detect/train 目录下保存每个 epoch 的权重和日志不需要额外写回调。3.4 训练日志怎么看训练跑起来之后很多人就干等着等完就完事了。其实日志里最有信息量的两个指标是 box_loss 和 val/box_map。box_loss 是检测框回归的损失值正常会随 epoch 逐步下降并趋于平缓如果 loss 降不下去或者波动剧烈大概率是标注有问题或者学习率设置不合适。val/box_map 是验证集上的 mAP单类检测任务里 mAP50 达到 0.8 以上算可用的模型低于 0.6 就得回头查数据了。我的习惯是每次训练都在第 50 epoch 左右看一眼 runs/detect/train/weights 下是否生成了 best.pt如果验证 mAP 长时间不涨尽早停掉调数据或调参不要干等 100 个 epoch 跑完。4. 让模型在真实猪栏里不翻车数据增强与推理策略4.1 数据增强不是玄学拥挤猪栏需要什么训练数据集就算质量不错直接在真实猪栏场景里跑还是容易暴露问题。猪栏里的情况远比训练集复杂有的猪趴在暗处有的猪彼此叠在一起有的镜头带反光。YOLOv8 自带一批数据增强策略默认配置已经帮你开了 Mosaic、随机翻转、色彩扰动等但对猪只场景我会额外强调两类增强的意义。第一是 Mosaic 拼接增强把四张训练图拼成一张再喂给模型。它的实际价值是让模型在单张图里同时看到多个场景增强对小目标的感知能力。第二是光照扰动猪栏普遍光线昏暗通过 HSV 空间的随机调整模拟暗光环境能显著减少夜间或阴影下的漏检。Ultralytics 里这些参数直接在 yaml 配置里改就行不需要写额外代码from ultralytics import YOLO # 自定义增强参数在训练前写入默认配置 model YOLO(yolov8s.pt) model.train( datapig_data.yaml, epochs100, imgsz640, hsv_h0.015, # 色调扰动幅度 hsv_s0.7, # 饱和度扰动幅度 hsv_v0.4, # 明度扰动幅度猪栏暗光场景调大 mosaic1.0, # Mosaic 开启概率 fliplr0.5, # 水平翻转概率 )参数说明hsv_v 默认是 0.4在暗光场景我习惯调到 0.5 以上让模型见过更多亮度变化mosaic1.0 代表每个 batch 里的图都做 Mosaic 增强从我的经验看训练轮数少于 100 时建议保留默认值不要关掉。数据增强不是堆参数而是针对场景短板做补偿。猪栏场景的短板是光照差和遮挡多那就往这两个方向加扰动空泛地堆一堆增强项只会拖慢训练速度。4.2 输入分辨率与锚框从训练到推理保持一致猪只计数遇到的典型问题是画面里的猪数量和图像尺寸不太匹配一头猪可能在 640 分辨率下只占几十个像素检测器很容易漏。这时候最直接有效的调整是提高输入分辨率。YOLOv8 在推理时会把输入图 resize 到训练设定的 imgsz如果训练用 640推理用 960模型看到的目标尺度分布跟训练时不一样精度反而下降。所以规则很简单训练时设多少 imgsz推理时就用多少。我一般直接在上面的训练代码里把 imgsz 设为 960同时把 batch 降到 8因为分辨率提高后单张图占的显存明显变大。如果你的显存不支持 960 分辨率训练那就训练和推理都保持 640而不是训练用小分辨率、推理临时换大分辨率那是给自己挖坑。4.3 推理阶段的关键NMS 阈值与置信度取舍模型训练好后推理脚本看起来简单但一个隐蔽的问题是重复框。猪身体特征明显同一个目标可能被输出多个重叠框不加后处理直接数框计数会偏大。YOLOv8 默认会做 NMS但置信度阈值设得太低会让大量误检框混进来。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcepig_count/images/test/, conf0.35, # 置信度阈值低于此值的框丢弃 iou0.5, # NMS 的 IoU 阈值重叠超过此值的框被抑制 imgsz960, # 必须与训练时的 imgsz 一致 saveTrue ) # 统计单张图中的猪数量 for idx, r in enumerate(results): boxes r.boxes pig_count_per_image len(boxes) print(fImage {idx}: {pig_count_per_image} pigs detected)参数说明conf0.35 是我在猪只场景的常用起点如果误检多就往上调到 0.5如果漏检多就往下放到 0.25这个值没有绝对最优要靠抽样验证来确定。iou0.5 是 NMS 的标准阈值猪群密集重叠时如果发现本来该是一头猪的检测框被消掉了就把 iou 稍微抬到 0.55保留更多框。这一段逻辑的本质是从检测框到计数不是一锤子买卖阈值的选择直接影响计数精度需要拿着验证集图片反复调。5. 避坑指南标签错位、格式误读与遮挡漏检5.1 标签与图片错位训练到一半发现 loss 不发散现象训练开始的十几个 epochbox_loss 一直稳定在某个值上下抖动val/box_map 始终是 0没有任何上升趋势。原因images 和 labels 文件名拼接规则不一致。比如图片叫 000001.jpg标注叫 000001.txt但代码里用了其他后缀去匹配导致大部分图片对应的标注内容是空文件或者带着别张图的坐标。模型在学习“用错框预测对猪”自然什么都学不会。解决强制走一遍前面 2.1 节说的文件一一对应校验写一个临时脚本统计每个 txt 的行数出现大量 0 行文件就直接说明匹配失败。我在这个坑上翻过车之后养成了习惯任何数据到手上先做三种检查文件数对得上、txt 内容非空、抽样画框肉眼看三步走完再谈训练。5.2 类别索引从 0 开始还是从 1 开始一个索引毁掉整个实验现象训练完成后推理猪是框出来了但类别 ID 显示为 1而类别列表里只有 0 对应 pig程序直接报 index out of range。原因VOC 转 YOLO 脚本里 class_names.index(name) 拿到的索引是 0但有人从 1 开始数写标注时直接写 1。本身 YOLO 格式的类别索引就是从 0 开始的索引写错不会让训练报错但会让评估阶段读数混乱。解决转换脚本里加断言检查转换后的 txt 首列数值是否都在 0 到类别总数减一之间。更稳的做法是转换后立刻用sort -u查看 txt 里实际出现了哪些类别索引cat pig_count/labels/train/*.txt | awk {print $1} | sort -u输出应该是 0如果出现 1 或别的数字就说明索引有问题趁早改脚本别带着错索引跑到训练结束。5.3 密集遮挡场景漏检严重置信度怎么调都白搭现象单张图里 20 头猪叠在一起模型只检测出 12 个框调低置信度阈值之后多出来的不是真猪框而是猪背上的一堆误检。原因密集场景里目标之间相互遮挡模型能看到的只是猪的一部分轮廓。YOLO 这类单阶段检测器对密集小目标本身就偏弱置信度和 NMS 只能做取舍不能根治漏检。解决从两个方向入手。一是提高输入分辨率让每头猪的像素面积变大前面 4.2 节已经说过了二是换用带切片推理的预测方式把大图切成小块分别检测再合并结果这是处理密集场景的常用手段。对猪只计数任务我实际测试下来切片推理比单纯调阈值有效得多下一章展开说。5.4 zip 解压后文件名乱码或文件损坏现象解压猪只计数数据集.zip 后部分图片名出现乱码或者解压中途报 CRC 校验失败某个标注文件读不出来的。原因数据集打包时用了不同操作系统和编码压缩包内文件名编码不一致会导致乱码下载过程不完整或压缩包本身有问题则会导致 CRC 错误。这个跟数据集本身无关但处理不好会卡住后续所有工作。解决先用unzip -t检查压缩包完整性有报错就说明文件本身坏了重新获取一份再说。文件名乱码用 Python 的 zipfile 库配合文件名编码处理来解压常规做法是尝试 cp437 和 utf-8 两种编码import zipfile import os def safe_extract(zip_path, dst_dir): 处理文件名编码导致的乱码问题 cp437 是 zipfile 模块读取标志位默认使用的编码 with zipfile.ZipFile(zip_path) as zf: for info in zf.infolist(): name info.filename try: # 先尝试 UTF-8 解码 decoded name.encode(cp437).decode(utf-8) except UnicodeDecodeError: # 失败则保持原名至少不报错 decoded name target os.path.join(dst_dir, decoded) if info.is_dir(): os.makedirs(target, exist_okTrue) else: with zf.open(info) as src, open(target, wb) as f: f.write(src.read()) safe_extract(猪只计数数据集.zip, pig_count)核心逻辑是 ZIP 规范里文件名默认用 cp437 编码而很多中文数据集的打包工具实际用的是 UTF-8直接用标准解压命令会得到乱码。这个脚本把文件名从 cp437 转回 UTF-8能解决大多数乱码问题。从那以后我拿到任何数据集压缩包第一件事永远是跑完整性检查和编码检查不再直接一键解压。6. 从检测框到精确计数后处理与精度验证技巧有了能用的模型最后一步是把检测框数变成可信的计数结果。直接数框是行不通的密集场景里一个目标多框、误检框、边缘截断框都会影响统计。我的做法是先在 NMS 之后再加一道基于 IoU 的去重逻辑遍历所有框两两计算交并比超过阈值的两个框保留置信度高的那个。YOLOv8 的 NMS 已经做了一层去重但不同位置的重复框仍可能存在补一层不亏。密集场景的验证技巧我用下来效果最明显的是切片推理。把一张大图切成若干小图分别检测后把结果坐标映射回原图再合并去重。对猪只计数这类目标小且密集的场景模型在小图上能看清每头猪的轮廓漏检率明显下降。切片大小按目标尺寸来我一般用 640 的切片加 128 的重叠目标越小切片越小。跑完一张大图的检测耗时会长一些但换取的是计数精度上的提升值不值取决于你的业务场景对精度的要求。评估计数精度时不能只看 mAP要看计数误差。YOLO 自带的验证指标关心的是框匹配质量而计数关心的是“预测的猪头数与真实猪头数的偏差”。我习惯在测试集上逐张统计绝对误差和相对误差算一个平均绝对百分比误差这个指标比 mAP 更直观地反映计数落地效果。真实场景里允许的误差幅度取决于你是数几十头的小栏还是几百头的大栏小栏误差一头都很明显。import numpy as np # 真实计数与预测计数假设有 20 张测试图的手动统计结果 y_true [15, 23, 18, 30, 12, 26, 35, 19, 22, 25, 17, 28, 16, 24, 31, 14, 27, 21, 20, 33] y_pred [14, 24, 18, 29, 12, 25, 34, 20, 21, 26, 18, 27, 16, 23, 30, 15, 27, 22, 20, 32] y_true np.array(y_true, dtypefloat) y_pred np.array(y_pred, dtypefloat) mae np.mean(np.abs(y_true - y_pred)) mape np.mean(np.abs(y_true - y_pred) / y_true) * 100 print(fMAE: {mae:.2f} 头) print(fMAPE: {mape:.2f}%)参数说明MAE 是平均绝对误差直接告诉你平均每张图数差几头猪MAPE 是相对误差百分比用来衡量不同规模栏舍的误差比例。我第一次拿这个指标做评估时发现一个扎心的事实模型 mAP 有 0.85看着不错但 MAPE 到 18%也就是说平均每 20 头猪会数错 3~4 头这在真实盘点里是不能接受的。从那时候起我每次做完猪只检测任务强制自己把从切片推理、去重到计数误差的整条链路走一遍不再拿 mAP 交差。希望这套流程对你也有用数据集的标注质量和你自己的验证方法才是决定技术方案能不能落地的两道关键关口。本文还有配套的精品资源点击获取