尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOV5生猪检测数据集制作指南:从采集标注到训练避坑

YOLOV5生猪检测数据集制作指南:从采集标注到训练避坑 简介猪圈摄像头场景下的生猪检测数据集按YOLOV5目录格式整理可直接作为目标检测训练数据使用。面向目标检测学习者与智慧养殖场景开发者尤其适合需要密集目标检测样本的实战项目。压缩包共2000个文件以1066个txt标注文件、933张jpg图像为主另含1个Python可视化脚本总体积约97.43MB。图像分辨率为640至1080每张图片包含多个目标训练集929张图片及对应标签、验证集136张图片及对应标签类别为pig。可视化脚本可随机读取图片并绘制边界框便于快速预览标注效果。资源已有609人学习能够用于YOLOV5系列模型训练、数据增强实验或迁移学习基线节省自行采集标注的时间。1. 猪圈摄像头下的生猪检测这份 YOLOV5 数据集到底要解决什么养猪场装摄像头的成本早就不高了真正卡住落地的是算法侧的数据——猪圈场景和公开数据集里的猫狗、行人完全是两回事俯视视角、铁栏杆遮挡、猪与猪互相叠压、粪污反光、灯光昏暗还有随时凑到镜头前的猪鼻子带来的极端近距离畸变。你说的“YOLOV5目录格式数据集1类别”本质就是把这类真实场景下的猪图像按 YOLOV5 训练器能直接读取的 images/labels 目录结构组织好只用猪一个类别去训练检测模型。适合谁想用自己的猪场摄像头数据训一个可用生猪检测模型、但不清楚标注规范和目录划分方式的人。这份笔记不聊论文只讲怎么把摄像头里的原始视频变成能被 YOLOV5 训练器直接吃进去的数据集以及这个过程中必然踩到的坑。2. 数据采集与标注规范猪圈场景的图不是随便截的2.1 摄像头安装角度与图像筛选决定了数据集的上限生猪检测的数据集质量七成由采集环节决定。猪圈摄像头通常是俯视或近俯视安装这和 COCO、VOC 里的平视目标差异极大。我一般建议在栏舍走道上方 2.53 米处安装角度略向下倾斜这样单头猪在画面里的像素面积比较稳定不会出现一头猪占满全图、另一头猪只有几十个像素的极端情况。采集时间要覆盖白天、夜间补光、喂食前、喂食后这几个典型时段否则模型很容易在某个光照条件下翻车。原始素材通常是视频流抽帧后要做筛选。我见过最省事的做法是每秒抽 1 帧然后用感知哈希算法做去重把画面基本不变的帧丢掉。筛选时重点去掉三类图严重运动模糊的、镜头被猪拱歪导致画面大部分是墙或地面的、以及完全无人无猪的空栏图。空栏图不是完全没用但单独分一类做背景负样本更合适混进目标检测数据集里只会浪费训练时间。import os import cv2 from PIL import Image import imagehash def dedup_frames(frame_dir, threshold10): seen_hash {} remove_list [] for fname in sorted(os.listdir(frame_dir)): fpath os.path.join(frame_dir, fname) try: img Image.open(fpath) h imagehash.phash(img) for existing_h, existing_f in seen_hash.items(): if h - existing_h threshold: remove_list.append(fpath) break seen_hash[h] fpath except Exception as e: remove_list.append(fpath) # 损坏图片直接删 for f in remove_list: os.remove(f) print(fremoved {len(remove_list)} duplicate/corrupt frames)这段脚本的思路是计算每张图的感知哈希值两张图的汉明距离小于 threshold 就视为重复帧后出现的被删掉。threshold 一般取 10取值越小去重越严格如果发现删完以后一段连续动作的中间帧没了就把 threshold 调大一点比如 15。2.2 用 LabelImg 标出猪的边界框一个类别也要守规矩标注工具用 LabelImg 就够不需要上 CVAT 这类重型平台。安装和启动命令很简单但真正决定数据集可用性的是标注规范。虽然只有一个类别标注时仍然要统一三个约定一是“猪”的边界框必须贴合可见身体不包括铁栏杆和食槽二是猪与猪互相遮挡时被挡住的猪只要可见面积大于 30% 就要单独标框小于 30% 就忽略这是个硬性约定否则同一张图不同人标的框差距巨大三是贴在镜头上的猪鼻子、猪耳朵这种极端特写如果画面里没有完整的猪身干脆不要标这种目标对检测器来说是不可学习的。LabelImg 默认输出 Pascal VOC 的 XML 格式但我们要的是 YOLO 的 txt 格式。在软件左侧选择 PascalVOC 或 YOLO 输出格式选 YOLO 后每个标注会自动生成一个同名 .txt 文件每行内容为“类别id x_center y_center width height”四个坐标值都是相对于图片宽高的归一化小数。类别 id 只有 1 个所以永远是 0。标注时把图片放大到能清楚看到猪的轮廓再画框。猪圈摄像头分辨率低于 720p 的建议直接放弃或做超分辨率预处理因为小猪和远处猪的像素太小标注出来也是噪声。2.3 标注质量抽检别信自己的手要信脚本标几百张图以后人会产生惯性容易把边界框画大或画小。每标完一批我习惯跑一遍抽检脚本统计每个框的宽高比分布和面积分布。猪的体型决定了边界框宽高比通常在 0.5 到 2.0 之间面积占比一般不会超过整张图的 40%。如果发现某个 batch 里出现大量宽高比大于 3 的框十有八九是把栏杆或地面阴影框进去了。import os import numpy as np def check_labels(label_dir, img_dir): ratios [] areas [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(fbad line in {fname}: {line.strip()}) continue _, x_c, y_c, w, h map(float, parts) if w 0 or h 0 or x_c 0 or x_c 1 or y_c 0 or y_c 1: print(fout of range in {fname}: {line.strip()}) continue ratios.append(w / h) areas.append(w * h) ratios np.array(ratios) areas np.array(areas) print(fratio: p5{np.percentile(ratios,5):.2f} p95{np.percentile(ratios,95):.2f}) print(farea: p5{np.percentile(areas,5):.4f} p95{np.percentile(areas,95):.4f}) check_labels(labels, images)这段脚本检查两件事标签文件里每行是否有 5 个合法数值且坐标在 01 之间框的宽高比和面积百分位是否落在合理范围。出现异常值先别急着改数据回到对应图片上看一眼是标注错误还是场景本身特殊比如猪躺平了宽高比会很大。这种抽检是数据集发布前最便宜的后悔药。3. YOLOV5 目录格式拆分train/val 划分与脚本实现3.1 YOLOV5 目录结构images 和 labels 必须严格对位YOLOV5 的数据集目录约定看起来简单但照着做错的人不少。标准结构是数据集根目录下有 images 和 labels 两个文件夹各自内部再按 train、val 划分注意不是把 train/val 放在根目录下再各含 images/labels。我直接给出推荐布局pig_dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── ... │ └── val/ │ ├── 000501.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ └── ... │ └── val/ │ ├── 000501.txt │ └── ... └── pig.yamlimages/train 里的每张 jpg 必须能在 labels/train 里找到同名同前缀的 txt反过来也一样。这里的“同名”指的是主文件名完全一致扩展名不同不要紧。训练时 YOLOV5 通过把 .jpg 替换成 .txt 来找标签文件一旦主文件名对不上那张图会被自动当作无目标图片而且不会报错——这是最阴的坑后面避坑章节细说。3.2 数据划分脚本8:2 划分 同名校验一步到位标注完成后最常见的操作是把所有图片和标签按 8:2 或 9:1 随机划分到 train/val。划分时有一个硬性要求图片和它对应的标签必须同时进 train 或同时进 val不能因为 random 两次就分家。正确做法是对文件名集合做一次随机打乱再按比例切分。import os import random import shutil src_images path/to/all_images src_labels path/to/all_labels dst_root pig_dataset val_ratio 0.2 random.seed(42) all_files [f[:-4] for f in os.listdir(src_images) if f.endswith(.jpg)] random.shuffle(all_files) val_count int(len(all_files) * val_ratio) val_set set(all_files[:val_count]) for split in [train, val]: os.makedirs(f{dst_root}/images/{split}, exist_okTrue) os.makedirs(f{dst_root}/labels/{split}, exist_okTrue) moved {train: 0, val: 0} for name in all_files: split val if name in val_set else train img_src f{src_images}/{name}.jpg lbl_src f{src_labels}/{name}.txt if not os.path.exists(lbl_src): print(fWARN: {name}.jpg has no label, skipped) continue shutil.copy(img_src, f{dst_root}/images/{split}/{name}.jpg) shutil.copy(lbl_src, f{dst_root}/labels/{split}/{name}.txt) moved[split] 1 print(ftrain{moved[train]} val{moved[val]})这段脚本的核心是只打乱一次文件名列表然后根据集合判断归属从根源上杜绝图标签错位。随机种子固定为 42保证每次跑结果一致方便复现。注意这里用了复制而不是移动目的是保留原始标注数据万一划分后发现问题还能重来。如果图片数量很大几万张可以改成 shutil.move 省磁盘空间但第一次做建议复制。3.3 data.yaml 文件类别名和路径的写法有讲究YOLOV5 训练时需要一个 yaml 文件来告诉训练器数据在哪里、有几类、类名是什么。这个文件写错是训练报错的重灾区。标准内容如下# pig.yaml train: ./pig_dataset/images/train val: ./pig_dataset/images/val nc: 1 names: [pig]train 和 val 指向的是 images 目录而非 labels 目录YOLOV5 会自动找同级 labels 目录。我建议用相对路径而不是绝对路径因为项目目录换机器后绝对路径必挂。nc 是类别数这里固定为 1names 列表长度必须等于 nc否则训练会崩。类名可以随便起不影响训练但最好用有意义的英文名。这个文件在训练时通过 --data 参数传入路径写错会报 FileNotFoundError路径写对但 yaml 语法错误会报解析异常这两种错见得多。提示把你的数据集和 YOLOV5 项目放在同一级目录下yaml 里的相对路径就永远不会因为换机器而失效。4. 用 YOLOV5 训练并验证生猪检测最小可行命令与参数4.1 训练命令从 yolov5s 起步跑通链路再谈精度目录和 yaml 就绪后第一次训练的目标不是追求精度而是跑通 pipeline。用官方 YOLOV5 仓库最小命令如下cd yolov5 pip install -r requirements.txt python train.py \ --data ../pig_dataset/pig.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 50 \ --name pig_experiment这条命令里每个参数都值得解释。--weights yolov5s.pt 会下载 COCO 预训练权重用迁移学习方式在猪数据集上微调这是数据集几百张时最快收敛的路径--img 640 是训练输入尺寸猪圈摄像头原图如果是 1080p640 能保留足够细节又不会让显存爆掉--batch 16 取决于显存8GB 显卡建议 816GB 以上可以 16--epochs 50 对单类别数据集通常够了如果 val 精度还在稳步上升可以追加。训练日志里重点关注 box_loss 和 obj_loss 的下降曲线如果 20 轮后还在持续下降说明数据量够可以加轮次如果曲线横盘震荡问题多半在数据本身。显存不够时优先降 batch 而不是降 img因为检测器对输入分辨率更敏感。batch4 虽然训练慢一点但至少能跑通。4.2 验证命令用 val.py 拿到 mAP 和混淆矩阵训练完以后不要直接拿去部署先用验证集测一遍。YOLOV5 提供了独立的验证脚本和导出的权重python val.py \ --data ../pig_dataset/pig.yaml \ --weights runs/train/pig_experiment/weights/best.pt \ --img 640 \ --conf 0.25 \ --iou 0.45 python detect.py \ --weights runs/train/pig_experiment/weights/best.pt \ --source ../pig_dataset/images/val \ --conf 0.25 \ --save-txtval.py 输出的指标里mAP0.5 是最先看的单类别任务里一般跑到 0.9 以上才算合格。mAP0.5:0.95 是更严格的指标对小幅位错敏感猪圈场景的遮挡多这个指标低于 0.6 是常态不用太焦虑。混淆矩阵会显示猪这一类有没有被漏检或误检成背景。detect.py 跑一遍 val 图片输出带框图片人眼扫一遍比任何指标都直观——这也是你判断标注质量好坏的最后一关。4.3 第一次训练最少需要多少张图单类别检测的收敛速度比多类别快得多。我的经验是猪圈场景下800 张训练图、每张平均 35 头猪就能训出一个 mAP0.5 在 0.85 以上的可用模型如果只有 300 张模型能跑通但泛化差换一个栏舍就漏检。低于 200 张就不要想训练了先去补数据。数据增强只能延缓过拟合不能创造信息。5. YOLOV5 数据集制作避坑清单5 个血泪教训5.1 标签类别 id 从 0 开始不是从 1 开始现象训练正常开始loss 下降但验证时一个目标都检测不出来conf 全在 0.01 以下。原因标注时把唯一一个类别设成了 1而 YOLOV5 的类别索引从 0 开始。标签文件里写的“1”实际对应的是第二类而 yaml 里 nc1 只有一个类于是所有标签都被当成无效类别丢弃。解决检查 labels 下所有 txt 的第一列数字确保全部是 0。grep -r ^1 labels/ | head -20这条命令找出所有第一列为 1 的标签行。如果有输出用 sed 批量替换回 0或者重新标注。这类错是标完图以后最容易翻车的点。5.2 图片和标签文件名不一致训练器不会提醒你现象训练日志里显示“warning: 找不到 labels...”部分图片被跳过训练出的模型精度极低。原因数据集来自多个摄像头或由多人标注图片改名了但标签没同步改或者 Windows 下遇见过文件名大小写问题。解决训练前用脚本强制校验。def verify_alignment(img_train, lbl_train): imgs {f[:-4] for f in os.listdir(img_train) if f.endswith(.jpg)} lbls {f[:-4] for f in os.listdir(lbl_train) if f.endswith(.txt)} only_img imgs - lbls only_lbl lbls - imgs print(fimgs without label: {len(only_img)}) print(flabels without img: {len(only_lbl)}) return only_img, only_lbl原理就是把两个文件名集合做差集。只要 only_img 或 only_lbl 不为空就必须处理——把缺失的补上或者把多出来的删掉。这一步不做后面的训练质量无从谈起。5.3 data.yaml 路径写绝对路径换机器就崩现象在自己的电脑上训练一切正常代码推到服务器或者同事电脑上跑报 FileNotFoundError。原因yaml 里写的是 /home/username/pig_dataset 这类绝对路径换机后路径不存在。解决统一用相对路径yaml 放在数据集根目录下train 和 val 写相对于数据集根目录的路径。YOLOV5 训练命令从项目根目录执行时相对路径天然有效。5.4 猪圈场景光线变化大验证集要按天数而不是按帧随机分现象验证集 mAP 很高模型上线后白天好用夜间全废。原因验证集是打乱随机抽的同一头猪不同帧可能分别进了 train 和 val模型“记住”了场景而不是学到了泛化特征。解决按日期划分数据集比如前 5 天的视频抽帧做 train最后 1 天的抽帧做 val。这样验证集才真正模拟了模型没见过的新时间点、新光照条件下的表现。5.5 过度依赖预训练权重忽略了自己数据的 domain gap现象加载 COCO 预训练权重训练loss 很快就降下来但实际场景测试效果远不如预期。原因COCO 里没有俯视猪这种目标预训练权重提供的先验对猪圈场景帮助有限。解决第一轮用 50 轮训练出的权重作为第二轮训练的初始权重继续用同一数据集再训 50 轮。这个操作的收敛速度明显快于从头训练而且模型见过的猪特征第二轮会被强化。6. 从数据集到可用模型数据增强调参与难例挖掘数据集做完了训练也能跑了最后这一步决定了模型能不能真正投入使用。最简单有效的是用 YOLOV5 自带的数据增强参数在训练命令里直接调。猪圈场景最突出的两个问题是光照不均和母猪压在小猪身上造成的密集遮挡。对应地我一般在 hyperparameters 里提高 hsv_s饱和度和 hsv_v明度的扰动范围让模型对不同光照不那么敏感fliplr 保持默认 0.5因为猪的左右对称性很好水平翻转等于白嫖一倍数据。增强参数不是越大越好hsv_v 超过 0.5 后猪的颜色会失真模型反而学歪。难例挖掘是另一个回报极高的步骤。第一次训练完把 val 集里 conf 在 0.30.6 之间的漏检图片找出来人工重新看一遍通常会发现两类问题一是标注漏了这张图里明明有猪但当时没标模型是“照着错的学”二是严重遮挡导致确实难检这类图单独复制出来人工再标一遍并把它们的重点区域在训练时多裁几次。我一般会为这种难例专门建一个目录并把它们设置成更高的采样权重。这比盲目加数据量有效得多因为一张难例在图里贡献的 loss 可能是普通图的几十倍。模型部署前的最后一个验证我习惯用一段当时的监控视频截取连续 3 分钟、包含喂食前后两个阶段来跑 detect.py。指标只能告诉你模型整体水平视频会告诉你模型在真实帧间表现——有没有抖动、漏检是不是集中在某个固定区域、检测框是否稳定贴合猪身。如果视频测试暴露出某个固定区域频繁漏检回去核对这部分图像在数据集里的占比大概率是严重不足。我自己做过的项目里被坑最多的永远是目录结构里的一个小字母——labels 拼成 lables或者 train/val 放错层级。这类问题不检查数据分布根本看不出来模型默默把错误数据学进去了。多做一步脚本校验等于给自己留了张后悔药。希望这些经验帮到你让你的猪圈摄像头数据集少走几趟弯路。本文还有配套的精品资源点击获取
返回列表