☰
草原耗牛行为识别数据集:VOC与YOLO格式转换及YOLOv8训练实战
2026/10/2 2:38:57 网站建设 项目流程

简介:面向牛行为识别、智慧养殖等场景的研究者与算法工程师,提供一套草原耗牛行为检测数据集,覆盖吃草、打架、站立、躺、移动、交配等8类典型行为,合计8674张图片,并配套VOC与YOLO两种标注格式,可供目标检测模型训练、验证与算法对比直接使用。压缩包内共2000个文件,主要包括jpg图片、xml标注文件和txt标签文件,其中xml对应VOC格式、txt对应YOLO格式,目录结构清晰,便于按需切换。包体大小约694.83MB,已有230人次学习下载。数据集总计包含18423个标注框,各类行为框数分布已给出,尤其eating、standing、yak等类别样本较充足,适合作为实际项目中的预训练或迁移学习数据;不过图片清晰度一般,部分截图存在取帧间隔,需根据任务适当进行清洗或增强。

1. 草原耗牛行为识别数据集:yolo+voc格式,8674张图里的8类行为

做动物行为分析,最头疼的从来不是模型选型,而是带标注的数据集。尤其是草原耗牛这类场景,吃草、打架、站立、躺卧、移动、交配等动作姿态差异大,公开数据集又少,想验证一个检测流程往往要从零标数据。这份数据集直接把8674张录像截图打包好,每张图都配了VOC格式的xml和YOLO格式的txt,8个类别合计18423个框,覆盖吃草、打架、舔舐、躺卧、交配、移动、站立和耗牛本体。图片清晰度一般,但胜在数量足、类别全,适合正在用YOLOv8训练检测模型的从业者拿来练手。新手能跟着流程跑通,熟手能拿它做类别不均衡和标注质量的实验。

2. 数据集结构与标签分布:先摸清你的训练资本

2.1 三个文件夹各管什么

压缩包解压后有三个文件夹,JPEGImages、Annotations、labels,分别放jpg图片、xml标注文件和txt标注文件。三个文件夹按文件名一一对应,比如sl_images4387.jpg对应sl_images4387.xml和sl_images4387.txt,8674张图全部配对完整,不存在漏标或错位。

xml文件是VOC格式,每个object节点下有name和bndbox,框坐标是绝对像素值。txt文件是YOLO格式,每行是class_id加归一化后的中心点x、中心点y、宽、高。两种格式都保留的好处很明显,你想用MMDetection或PaddleDetection时直接走VOC,想用YOLOv5或YOLOv8时直接走txt,省掉最没价值的手工转换。实际项目中很多团队只交付其中一种格式,这份资源是把两边都补齐了。

2.2 8类标签的框数统计

标签依次为eating、fighting、licking、lying、mating、moving、standing、yak。从框数看,类别分布极不均衡。eating最多,6897框,standing有3710框,yak有3468框,lying和moving各有2071和1594框。但fighting只有408框,licking只有200框,mating更是只有75框。这个分布直接决定了训练策略,少数类如果处理不当,模型基本学不出来。

标签框数占比
eating689737.4%
fighting4082.2%
licking2001.1%
lying207111.2%
mating750.4%
moving15948.7%
standing371020.1%
yak346818.8%
合计18423100%

看这张表,前两行就说明问题了。eating一个类占了三分之一以上,mating连百分之一都不到。训练时如果按默认权重来,模型会倾向把所有目标都预测成eating,因为梯度几乎全被多数类主导。还有一个细节,yak这个类跟其他行为标签不是同一维度的,它代表的是“画面里有耗牛但行为不明确”的情况,别把它当干扰项删掉,否则训练背景会变得很脏。

2.3 图片分辨率和取帧特点

摘要里注明图片是录像截图,清晰度一般。这类数据的特点是相邻帧高度相似,如果直接按文件顺序切分训练集和验证集,很可能出现验证集里大量跟训练集同场景的图,评估结果虚高。取帧间隔问题还导致部分图片实际是间隔一定时间后的截图,不是连续帧,动作连续性差。所以后续做数据划分时要尽量避免按文件名顺序硬切,这部分在避坑章里详细说。

3. VOC与YOLO格式互转:三种常见场景下的脚本与参数

3.1 为什么需要自己写转换脚本

虽然数据包里两种格式都有,但你在实际使用中还是会遇到需要互转的场景。比如你想用albumentations做增强,它原生读VOC格式的xml;想用YOLOv8训练,又需要txt。再比如拿到手后发现某张图的txt文件行数跟xml里object数量对不上,就得自己写脚本去校验和修复。这类转换脚本属于检测项目的基础工具,反复会用到。

3.2 xml转txt的Python脚本

下面这段代码是把VOC格式的xml转成YOLO格式的txt。核心逻辑是读取xml里的bndbox坐标,算出中心点和宽高,再除以图片的宽和高做归一化。

import xml.etree.ElementTree as ET import os classes = ["eating", "fighting", "licking", "lying", "mating", "moving", "standing", "yak"] def convert_xml_to_yolo(xml_path, txt_path, image_width, image_height): tree = ET.parse(xml_path) root = tree.getroot() with open(txt_path, 'w') as f: for obj in root.iter('object'): cls = obj.find('name').text if cls not in classes: continue cls_id = classes.index(cls) bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) w = xmax - xmin h = ymax - ymin cx = xmin + w / 2 cy = ymin + h / 2 f.write(f"{cls_id} {cx/image_width:.6f} {cy/image_height:.6f} {w/image_width:.6f} {h/image_height:.6f}\n")

这里两个关键参数必须从图片实际尺寸获取,image_width和image_height分别是原图的宽和高。很多人贪省事写死一个分辨率,换张图就全歪了。另外注意,YOLO格式要求的是中心点坐标,不是左上角坐标。第一次写转换脚本时很容易把xmin和ymin直接写进txt里,这种错误会导致训练时所有框都偏到左上角。

3.3 txt转xml的Python脚本

反过来转的场景主要出现在可视化排查时。YOLO训练出的预测结果或者从txt读出的标注,想画到图上检查就得先转回VOC格式的绝对坐标。这段代码把归一化的中心点和宽高还原成四个顶点坐标,同时记录下来图片尺寸和文件名。

import os classes = ["eating", "fighting", "licking", "lying", "mating", "moving", "standing", "yak"] def convert_yolo_to_xml(txt_path, xml_path, image_width, image_height, image_name): with open(txt_path, 'r') as f: lines = f.readlines() xml_content = f"""<annotation> <filename>{image_name}</filename> <size> <width>{image_width}</width> <height>{image_height}</height> <depth>3</depth> </size> """ for line in lines: parts = line.strip().split() if len(parts) != 5: continue cls_id = int(parts[0]) cx = float(parts[1]) * image_width cy = float(parts[2]) * image_height w = float(parts[3]) * image_width h = float(parts[4]) * image_height xmin = int(cx - w / 2) ymin = int(cy - h / 2) xmax = int(cx + w / 2) ymax = int(cy + h / 2) cls_name = classes[cls_id] xml_content += f""" <object> <name>{cls_name}</name> <bndbox> <xmin>{xmin}</xmin> <ymin>{ymin}</ymin> <xmax>{xmax}</xmax> <ymax>{ymax}</ymax> </bndbox> </object> """ xml_content += "</annotation>" with open(xml_path, 'w') as f: f.write(xml_content)

这段代码容易出错的地方是坐标换算后的取整。归一化坐标乘回原图尺寸后得到的是浮点数,如果不转成int直接写进xml,后面很多CV库读VOC时会因为字段类型报错。还有一点,换算后发现xmin为负或者xmax超出图片宽度时,说明原始标注本身就有问题,转出来的框在可视化时会露出马脚。

3.4 批量转换与文件完整性校验

单张转换只适合排查问题,实际项目里要批量处理。批量脚本里我一般会额外做一步完整性校验,确保每张jpg都有对应的xml和txt。下面这段shell命令可以快速找出缺标注的图片。

for f in JPEGImages/*.jpg; do base=$(basename $f .jpg) if [ ! -f labels/$base.txt ]; then echo "$base 缺txt"; fi if [ ! -f Annotations/$base.xml ]; then echo "$base 缺xml"; fi done

跑一遍这个循环,所有缺文件的情况都会直接打出来。正常来说这份数据不会有缺失,但经过你自己的增删改之后就不一定了。完整性校验应该在训练前做一次,别等到loss跑到一半才发现有图片喂不进模型。

4. 用YOLOv8训练草原耗牛行为识别:流程与调参

4.1 准备数据目录与data.yaml

YOLOv8训练自己的数据集,第一步是把数据组织成指定结构。常见做法是建images和labels两个大目录,各自下面再分train和val子目录。train里面放训练集图片,val里面放验证集图片,对应的txt文件也按同样结构放。下面是data.yaml的格式。

train: /absolute/path/to/dataset/images/train val: /absolute/path/to/dataset/images/val nc: 8 names: ['eating', 'fighting', 'licking', 'lying', 'mating', 'moving', 'standing', 'yak']

这里的路径一定要写绝对路径。坑在于,相对路径在换机器跑的时候经常找不到文件,YOLOv8报错还比较模糊,新手很容易在这个地方浪费时间。nc必须是8,names顺序必须和txt里的class_id一致,顺序错了模型训练时就会把类别标签张冠李戴。

4.2 数据划分脚本

划分数据集时要同时移动图片和txt,这两个文件必须保持同名同路径。我一般按9比1切分,8674张图大约7800张训练,870张验证。下面这段脚本用随机打乱的方式划分。

import os import shutil import random random.seed(42) image_dir = "JPEGImages" label_dir = "labels" train_image_dir = "images/train" val_image_dir = "images/val" train_label_dir = "labels/train" val_label_dir = "labels/val" for d in [train_image_dir, val_image_dir, train_label_dir, val_label_dir]: os.makedirs(d, exist_ok=True) img_files = [f for f in os.listdir(image_dir) if f.endswith('.jpg')] random.shuffle(img_files) val_count = int(len(img_files) * 0.1) for i, img in enumerate(img_files): label = img.replace('.jpg', '.txt') if i < val_count: shutil.copy(os.path.join(image_dir, img), val_image_dir) shutil.copy(os.path.join(label_dir, label), val_label_dir) else: shutil.copy(os.path.join(image_dir, img), train_image_dir) shutil.copy(os.path.join(label_dir, label), train_label_dir)

random.seed(42)很重要,它保证每次运行结果一致。对比实验时如果每次划分都不一样,模型效果的差异就分不清是模型改动导致的还是数据变动导致的。这条血泪经验在调参阶段非常关键。

4.3 训练命令与关键超参数

我用yolov8n起步,因为参数少、训练快,先验证流程能不能走通。命令如下。

yolo train model=yolov8n.pt data=data.yaml epochs=100 imgsz=640 batch=16 device=0

参数说明一下,imgsz=640是常见输入尺寸,这个数据集图片清晰度一般,640足够。batch=16取决于GPU显存大小,显存不够时改成8,否则会报CUDA out of memory。device=0指第一张GPU,没有GPU就改成cpu,但训练时间会长很多,一百轮可能要跑几个小时。epochs=100对这个数据量是够的,我实际跑下来到六十轮左右val loss就开始不再下降了。

yolo预训练模型下载是自动完成的,第一次执行时会从官方仓库拉权重。如果拉不下来,就去网上找对应版本的yolov8n.pt文件,手动放到项目目录下,再把model参数改成那个文件的路径。这里有个细节,YOLOv8的预训练模型文件名里带了版本号,用错版本会报维度不匹配的错误。

4.4 类别不均衡的调参对策

训练前必须考虑mating只有75框这个问题。直接跑默认训练的结果就是,large类指标还行,但mating的mAP50可能为0。常见做法有两个方向,一个是数据层面,对少数类做复制增强,把mating的图片复制几份加进训练集,相当于过采样。另一个是损失函数层面,给少数类分配更高的权重。

在YOLOv8里可以通过改配置实现类别权重,或者干脆在训练参数里传class_weights。但说实话,75个框的类别再怎么加权重也难有本质提升,它连不同姿态的多样性都覆盖不了。更实际的处理是把mating单独拿出来做二分类,或者接受它效果差这个现实,别让这个少数类拖累整体训练节奏。

5. 避坑指南:从格式到分布的常见翻车点

5.1 现象:训练时报标签索引越界

训练刚开始就报IndexError,提示标签的class_id超出范围。出现这个问题的常见原因是data.yaml里的names顺序和txt文件里的class_id对不上。比如你把names里的顺序改成了alphabetic,但txt里的标签id还是按原始顺序写的。

解决方法是先读一条txt文件看内容,print出第一列数字,确认是0到7。再看data.yaml的names是不是和压缩包里的顺序一致。这份数据本身的标签顺序是固定的eating、fighting、licking、lying、mating、moving、standing、yak,你自己去改顺序就是给自己挖坑。

5.2 现象:图片和标签数量对不上,训练集少了一部分

训练开始前统计,发现images里有8000张图,但labels里只有7900个txt。这种情况基本都出在数据划分阶段,脚本里只复制了图片,忘了复制txt。尤其是手动整理过文件夹的人,特别容易漏。

解决方法是训练前跑一遍完整性校验。用前面提到的那段shell循环,检查每一张jpg是不是都有对应的txt和xml。跑出来的缺失列表会告诉你具体是哪些文件出了问题,补拷过去就行。这个操作应该成为固定习惯,别相信自己的记忆。

5.3 现象:验证集mAP很高,但实际视频推理时漏检严重

训练完跑val,mAP50有0.8多,感觉还不错。结果拿去推理一段没参与训练的录像,发现站立和吃草漏掉一大半。原因很可能是数据划分方式有问题。这份数据是录像截图,相邻帧之间的场景重复度极高,如果按文件名顺序硬切,比如前百分之九十做训练集,后百分之十做验证集,验证集里其实包含大量和训练集同时段甚至同场景的图,评估结果虚高。

解决方法是不要按简单顺序切分,而是按时间间隔抽样。比如每间隔十帧取一帧进验证集,打乱相近帧之间的耦合关系。或者干脆把文件名里的数字间隔跳着拿,让验证集的场景分布和训练集尽量不同。

5.4 现象:loss下降很快,但精确率就是上不去

训练日志里loss曲线很漂亮,前二十轮就降到很低,但验证集上吃草和站立的精确率只有百分之六十多。这种情况通常是标注质量有问题。因为图片是录像截图,取帧间隔问题导致一部分标注框落在动作模糊的中间态上,看起来边界不清晰,导致模型学了个模棱两可的框。

解决方法是先做可视化检查。用VOC格式的xml把标注框画到原图上,随机抽两百张图一张张看。看到框明显偏离或者大小不对的,直接删掉那条标注。我一般会把可视化脚本固定下来,每次拿到新数据集都先跑一遍。

5.5 现象:mating类只在训练开始时有点动静,之后一路为零

mating全数据集只有75个框,而且在几百张图里分布得稀疏。训练时这个类在整个loss里占比太小,梯度更新基本被eating和standing主导。你看到的现象就是,训练了二十轮后打印每个类的AP,mating始终是零。

解决方法是先尝试数据过采样,把包含mating的图片在训练集里复制三到五份。如果这样还是不行,就得换个思路。把mating单独拿出来做二分类检测,或者用半监督方式给这个类补充更多标注。别指望一两轮调参能把一个只有75个框的类别救活。

6. 验证模型:mAP、混淆矩阵和漏检分析

6.1 用val集跑一次完整评估

训练完成后,用一条val命令拿到每个类别的详细指标。

yolo val model=runs/train/exp/weights/best.pt data=data.yaml

输出里最重要的Per Class表会列出每个类别的mAP50和mAP50-95。对这套耗牛行为数据,我只看重mAP50,因为标注框本身不追求像素级精确,mAP50-95的严格重合度要求并不适合这个场景。建议单独看fighting、licking、mating这三行的数值,它们代表少数类的真实水平。

6.2 混淆矩阵到底说明了什么

运行结果里带一张confusion_matrix.png,很多人误以为它显示的是检测框的分类结果,其实它展示的是预测框和真实框的匹配关系。对角线高代表分类准确,非对角线有值说明这类行为经常被误判成另一类。

在这个数据集里,lying和standing是最容易互相混淆的两个类,因为耗牛躺卧和站立从侧面视角看确实接近。如果你发现这种混淆很严重,先别急着调模型,去检查一下训练集里这两类的图片角度分布,大概率是侧面视角过多、俯视视角太少导致的。模型学的不是行为语义,只是姿态外观。

6.3 用一段没参与训练的录像做实测

跑完val只是第一步,真实场景往往更残酷。我会特意准备一段没参与训练和验证的录像,最好包含几个打架和移动的场景,然后用训练好的模型做推理。

看实测结果时重点关注帧间稳定性。模型可能在某一帧检出了mating,下一帧又丢了,再下一帧又出现。这种闪烁意味着模型只是碰巧检对了,没有学到稳定的特征。如果反复出现,可以尝试把相邻帧的检测结果做时序平滑,让连续帧里都出现的框才能输出。这个技巧虽然简单,但能让实际使用体验明显提升。

从那以后,每次拿到这类行为识别数据集,我都强制先跑一遍标签分布统计和可视化检查,把少数类和模糊框处理清楚再进训练流程。这套习惯帮我避开了大半玄学问题,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询