尧图精选

牛检测数据集:1968张图片VOC与YOLO格式训练实践指南

🕒 发布时间:2026/10/2 18:05:02 📁 来源:尧图网络
简介一套面向目标检测任务的高质量牛只识别数据集主要帮助开发者解决牛只检测场景下训练数据匮乏的问题。数据集中含有1968张真实图像对应的人工标注同时提供VOC与YOLO两种格式的标注文件总计标注框数达到8014个类别名称统一为牛能够直接用于训练主流目标检测网络也可用于智慧牧场、动物行为分析等科研与工程场景。整个资源包共收纳2000个文件核心部分为1968个XML标注文件并附带少量文本说明文件整体压缩后大小约为382.76兆字节解压后目录层级分明图像与标注文件一一对应使用便利。所有标注均由标注人员借助标签工具手工绘制矩形框边界贴合目标且规则统一无需用户二次清洗。该数据集已吸引五百四十九人浏览学习适合目标检测入门者、算法工程师及需要牛只数据支撑项目研发的团队选用。1. 牛检测数据集1968张jpg、8014个牛框VOC和YOLO格式都齐了做智慧牧场、牛场个体识别或者保险验标项目的人第一道坎永远是数据。牛检测数据集这套资源直接给了1968张jpg原图每张图都配了一份Pascal VOC的xml标注和一份YOLO格式的txt标注类别只有cow一个总框数8014个平均每张图约4个框。也就是说你不需要再用labelImg熬夜补标注解压之后可以直接按YOLOv5、YOLOv8或者mmdetection的目录结构丢进去训练。这套数据真正值钱的地方在于场景分布近距离单牛、远距离群牛、牛舍内不同光照条件都有覆盖单类和密集目标叠加拿来建立牛检测baseline很合适。适合三类人一是做农业AI项目想快速跑出基线效果的工程师二是需要一份干净数据来验证模型改进思路的研究者三是想搞懂VOC和YOLO两种标注格式差异的入门学习者。下面我从文件结构开始把这套数据从解压到训练全流程拆开讲。2. 先看压缩包结构文件名编号和三文件对应的门道2.1 文件名规律与“没有分割txt”的边界解压后你会看到一堆说明.txt加xyxr_cow_916.txt、xyxr_cow_539.txt这样的文件。这里要强调一下这些xyxr_cow_*.txt是YOLO格式的标注文件本身每一行代表一个目标的检测框不是文件名清单。对应地同名的jpg图片和同名的xml标注文件都在同一个目录里一张图三种格式一一对应。命名前缀xyxr_cow_后面跟的编号并不是连续递增的从539、786、916直接跳到1536、1725跨度很大。这说明原始图片大概率是视频抽帧或者批量拍摄之后人工筛选过一遍把没有牛、严重模糊和遮挡过度的帧删掉了。留下来的编号断档不影响训练但有个实际问题你不能靠文件名顺序做训练集和验证集划分只能按随机种子或者按场景分层抽样。还有一个重要边界标题里明确写了“不包含分割路径的txt文件”。意思是这张数据集没有VOC官方那种train.txt、val.txt索引文件不会告诉你哪些图片用来训练、哪些用来验证。所以拿到手之后第一件事是自己写划分脚本不要等一个现成的train.txt出现。2.2 用Python读xml坐标、尺寸与标注分布VOC格式的xml是labelImg默认导出的标准结构annotation根节点下面有size和object两个关键块。size里记录图片宽度、高度和通道数object里是类别名cow和bndbox的四个角点坐标。先跑一段解析脚本确认这套数据的实际分布import xml.etree.ElementTree as ET import glob xml_files glob.glob(xyxr_cow_*.xml) total_boxes 0 total_images len(xml_files) sizes {} for xml_path in xml_files: tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) key f{w}x{h} sizes[key] sizes.get(key, 0) 1 for obj in root.iter(object): name obj.find(name).text if name ! cow: raise ValueError(f未知类别 {name} 出现在 {xml_path}) box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) if xmax xmin or ymax ymin: print(f异常框: {xml_path} - {xmin},{ymin},{xmax},{ymax}) total_boxes 1 print(f图片总数: {total_images}) print(f标注总框数: {total_boxes}) print(f尺寸分布: {sizes})这段脚本做了三件事统计xml文件数量、确认所有类别都叫cow、检查有没有xmaxxmin这种反坐标的异常框。跑完你会看到尺寸分布常见监控抽帧图一般是1920x1080或者1280x960如果出现多种尺寸后面训练时要统一用letterbox而不是直接resize这一点第4章会细说。注意脚本里root.iter(object)用的是深层遍历不要用root.findall(object)因为有些变体xml会把object嵌在别的节点里findall只能拿到当前层。这一步排查能提前发现标注质量问题不要跳过去。3. VOC与YOLO互转归一化坐标的换算公式和核对脚本3.1 两种标注格式的坐标系对照VOC格式的坐标是绝对像素值单位是像素比如xmin256/xmin表示目标左边界在图片第256列。YOLO格式的坐标则是归一化的小数范围在0到1之间而且储存的不是角点是中心点坐标加宽高。两种格式的换算关系是固定的四条公式。假设图片宽为W、高为H一个bbox的像素角点为xmin, ymin, xmax, ymax那么x_center (xmin xmax) / 2 / Wy_center (ymin ymax) / 2 / Hwidth (xmax - xmin) / Wheight (ymax - ymin) / H反过来从YOLO格式还原像素角点就是xmin (x_center - width/2) * W其余同理。这套数据本身两种格式都给了为什么还要自己会转换我一般是在这两种场景下用第一校验xml和txt两套标注是否完全一致防止下载文件不完整导致某个txt缺了一行第二如果后续要接mmdetection或者老版本SSD它们只吃VOC格式而新项目又要跑YOLOv8手上有转换脚本随时能重建。对比项VOC xmlYOLO txt坐标形式绝对像素角点xmin/ymin/xmax/ymax归一化中心点x_center/y_center/width/height数值范围0到图片宽高0到1是否含类别名是namecow/name否用整数id 0开头文件对应每图一个xml每图一个txt典型用途检测、分割通用标注YOLO系列训练直接读取3.2 转换脚本与反向核对有了上面公式直接写一个xml到txt的转换脚本跑完可以和你手上已有的txt逐行对比看是否完全一致import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h width (xmax - xmin) / w height (ymax - ymin) / h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines) \n) xml_to_yolo(xyxr_cow_916.xml, xyxr_cow_916_generated.txt, [cow])class_names列表顺序决定类别id因为数据集只有cow一个类索引就是0。转换时保留六位小数和labelImg原始导出的精度一致避免精度丢失导致框的宽高出现微小偏移。生成之后和第2章检查脚本配合把生成txt和原始txt做差集对比就能知道哪些文件的标注被动过手脚或者损坏过。多提一句不要自己在预处理阶段把图片强行resize到固定尺寸然后用旧的归一化坐标直接训练。归一化坐标是相对原图的只要源图被人为缩放坐标必须跟着按同比例重算否则框全部错位。这也正是很多人训练时发现loss下不去、mAP接近0的原因之一。4. 用YOLOv8训练这套牛数据目录、yaml与超参选择4.1 搭出YOLO标准目录并写yamlYOLO系列训练目录结构要求images和labels两个大目录各自下面再分train和val。图片放images标签放labels且同名文件必须保持在同一个相对路径下。数据只有1968张按9:1划分训练集1771张、验证集197张。我建议验证集至少留200张以上197张凑合能用但如果验证集都是单一场景mAP波动会很大。mkdir -p datasets/ox_cow/images/train mkdir -p datasets/ox_cow/images/val mkdir -p datasets/ox_cow/labels/train mkdir -p datasets/ox_cow/labels/val划分脚本用Python写按随机种子做分层抽样import random, os, shutil random.seed(42) jpg_files [f for f in os.listdir(.) if f.endswith(.jpg)] random.shuffle(jpg_files) val_count int(len(jpg_files) * 0.1) val_set set(jpg_files[:val_count]) for jpg in jpg_files: stem os.path.splitext(jpg)[0] if jpg in val_set: dst_img datasets/ox_cow/images/val dst_lbl datasets/ox_cow/labels/val else: dst_img datasets/ox_cow/images/train dst_lbl datasets/ox_cow/labels/train shutil.copy(jpg, os.path.join(dst_img, jpg)) shutil.copy(stem .txt, os.path.join(dst_lbl, stem .txt))random.seed(42)固定随机源保证任何机器上复现同一份划分后面调参对比结果才有意义。这里只复制jpg和txtxml不需要进训练目录YOLO训练只读txt。然后写datasets/ox_cow/ox_cow.yamltrain: ./datasets/ox_cow/images/train val: ./datasets/ox_cow/images/val nc: 1 names: [cow]train和val路径用相对路径还是绝对路径取决于你在哪个目录下执行训练命令。我一般用相对路径这样整个数据集文件夹可以整体搬走换机器也不用改配置。nc: 1和类别名names的顺序必须和txt里第一列的class_id一致cow的id是0不是1写错的话训练会直接把cow当成背景。4.2 训练启动与关键参数解读基础训练命令如下模型用YOLOv8n起步pip install ultralytics yolo detect train datadatasets/ox_cow/ox_cow.yaml \ modelyolov8n.pt \ epochs100 imgsz640 batch16 device0modelyolov8n.pt是官方预训练权重虽然预训练是在COCO上背景不是牛但特征提取层的通用特征能加速收敛。单类检测任务数据量不大yolov8n足够没必要一上来就上YOLOv8x2000张级别的数据跑大模型很容易过拟合。如果追求精度可以用yolov8m但训练时间会翻倍我建议先把n跑通拿到baseline再说。关键超参按这套数据的规模这样设比较稳参数建议值说明epochs100单类数据收敛快50轮后基本稳定100轮足够看趋势imgsz640源图如果是1080p缩放640能保留小目标特征batch16根据显存调整24G显存可到328G显存降到8device0单卡训练无GPU可去掉patience3030轮没提升自动早停省时间这里重点说imgsz。YOLOv8的imgsz不是直接resize是letterbox等比缩放宽度和高度补灰边。这套数据里如果同时存在1920x1080和1280x960两种尺寸letterbox会把它们统一到640x640画布但不改变目标宽高比这是最安全的做法不要自己去预处理resize。4.3 训练完先看混淆矩阵和mAP训练结束后YOLOv8会输出一系列指标重点关注两个mAP50和mAP50-95。mAP50是IoU阈值0.5时的平均精度牛检测这种单类任务做到0.9以上不奇怪mAP50-95要求框的位置非常准一般在0.6到0.8之间。还要看train/box_loss如果最终loss还在缓慢下降而没有平台期说明epochs不够或者学习率没调好可以再加30轮续训。验证集上的推理结果会每个epoch存图不要只看最后的mAP数字。打开runs/detect/train/val_batch0_pred.jpg用肉眼扫一遍预测框框是不是紧贴牛体、有没有把两根柱子框进去、群牛场景有没有漏检。mAP高只能说明整体统计上测得好具体的框质量和标注习惯必须看图说话。这一步做完再回头调参数才有依据。5. 避坑实录牛检测数据集最常见的五个翻车点5.1 类别id写成1导致全部目标被判为背景现象训练能正常跑起来但loss不降验证集mAP约等于0混淆矩阵里所有框都落在background。原因YOLO格式txt每一行的第一列是类别id只有一个类cow时id必须是0。如果你用某些第三方标注转换工具或者合并脚本它们可能从1开始编号等于把cow当成第二个类但yaml里nc: 1加上names: [cow]只认id 0所有id为1的行全部被忽略。解决训练前先批量检查txt首列取值cat datasets/ox_cow/labels/train/*.txt | awk {print $1} | sort -u输出只应该是0。如果出现1说明标准化脚本写错了重新转换即可。5.2 归一化坐标越界导致训练直接报错现象训练到中途打断日志报all bbox coordinates are outside the image bounds或者提示某张图的标注框宽高为负。原因txt里存的不是归一化坐标而是像素坐标除以了错误的尺寸或者有人工改动txt时把x_center和width写成了像素值没除以宽高。解决用一段快速统计脚本检查所有txt的数值范围import glob import numpy as np for txt in glob.glob(datasets/ox_cow/labels/train/*.txt): arr np.loadtxt(txt, usecols(1, 2, 3, 4)) if arr.size 0: continue mn, mx arr.min(), arr.max() if mn 0 or mx 1: print(f越界: {txt}, min{mn}, max{mx})越界文件数量多的话直接用第3章的转换脚本从xml重建全部txt不要手工修。5.3 验证集划分太随意导致指标失真现象第一次训练mAP50有0.92第二次换随机种子变成0.81两次结果差很大不知道信哪个。原因1968张图按9:1随机划分验证集只有197张。如果这197张恰好集中在某一个场景比如都是近距离单牛验证指标就会虚高换一个种子验证集混入大量远距离群牛分数直接掉下来。解决把val_count提高到300张以上用7:3划分验证集多一点或者按文件名的编号区间做分层抽样确保不同场景均匀落在训练和验证两部分。更严格的做法是直接做5-fold交叉验证取五次mAP的均值和标准差但这个成本较高单类任务一般不需要。5.4 图片尺寸不一致且预处理自己resize现象训练时loss能降但推理时在1920x1080原图上检测得好转到1280x960图上框就偏小目标的框偏移明显。原因数据里图片不是统一尺寸某些预处理代码把图片直接cv2.resize成640x640正方形宽高比被拉伸归一化坐标对应的物理位置全变了。解决不要自己resize把原始jpg直接喂给YOLOv8框架会用letterbox补灰边保持比例。如果你确实需要提前压缩图片省显存压完必须同步把xml或txt的坐标按同比例重算具体做法是压缩比例等于目标宽高除以原始宽高两个方向分别算乘进去。5.5 xml里存在异常反框但训练没有发现现象可视化验证集预测结果某个牛框是条横线或者矩形外扩到图片边缘外但准确率指标没有明显下降。原因xml里可能有xmax 图片width或者ymax 图片height的情况转换脚本没有做边界裁剪yolo训练时对轻微越界的框容忍但可视化时会暴露出来。解决在xml转yolo的脚本里加一句边界裁剪xmin max(0, xmin)、xmax min(w, xmax)、ymin max(0, ymin)、ymax min(h, ymax)。保证转换后坐标全部落在图片范围内宁可框小一点不要越界。6. 训练前先做标注体检用脚本可视化抽查所有框6.1 一键可视化脚本训练之前我习惯把所有标注框画在图上过一遍比看任何统计数字都直观。这个脚本读yolo txt反归一化回像素坐标在图上画矩形框有越界或者异常框用红色标出正常框用绿色import cv2 import glob import os from pathlib import Path img_dir datasets/ox_cow/images/train label_dir datasets/ox_cow/labels/train out_dir check_boxes_train os.makedirs(out_dir, exist_okTrue) for txt_path in glob.glob(os.path.join(label_dir, *.txt)): stem Path(txt_path).stem img_path os.path.join(img_dir, stem .jpg) img cv2.imread(img_path) if img is None: print(f缺图: {img_path}) continue h, w img.shape[:2] with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) 5: continue _, cx, cy, bw, bh parts cx, cy, bw, bh map(float, (cx, cy, bw, bh)) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 0, 255) if x1 0 or y1 0 or x2 w or y2 h else (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) out_path os.path.join(out_dir, stem .jpg) cv2.imwrite(out_path, img)脚本逻辑很简单cx - bw / 2算出左边界再乘以图片宽度还原成像素坐标。len(parts) 5的过滤是防止空行或者只有类别没有坐标的损坏行。红色框大量出现说明标签坐标有问题需要回到第3章用xml重建绿色框正常不代表标注一定准还需要人工看贴合度。6.2 抽查标准和阈值参考生成完图以后在本地看图软件里快速翻30到50张重点看三类情况牛体遮挡严重的场景框是否偏大或偏小群牛挨得很近时多个框有没有重叠超过30%完全没有牛的背景图是不是没有标注框。这套数据标注工具是labelImg矩形框习惯一般都比较紧贴目标但如果看到框超出牛体边缘超过5%以上的图比较多说明原始标注质量不够干净训练前要修。体检查完再进入第4章的训练流程。我自己的习惯是拿到任何数据集都强制先跑一遍这个可视化体检脚本再谈训练和调参因为标注质量是整个检测项目里唯一无法靠模型弥补的部分。这套牛检测数据整体标注规范度是可以的但每张图框数从1到接近10不等群牛场景框密集训练前心里有个底后面验证集mAP到多少才算正常才能判断你的模型是真的好还是数据本身简单。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →