ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

奶牛和水牛计算机视觉数据集:从零构建到YOLOv8训练全流程

奶牛和水牛计算机视觉数据集:从零构建到YOLOv8训练全流程 简介这份奶牛和水牛计算机视觉数据集面向从事目标检测与分类的算法工程师、农业科研人员及高校师生聚焦印度品种的奶牛与水牛识别任务可用于农业监控、野生动物研究、生态保护及品种形态分析等场景。资源包共2000个文件以1749个txt标注文件、250张jpg图像和1个yaml配置文件为主压缩包约103.35MB图像统一为640×640像素标注采用YOLOv8格式包含类别标识与中心坐标、宽高信息并已通过EXIF方向剥离完成自动定向未做图像增强以保留原始状态。目前已有110人学习下载。读者可借助该数据集直接训练和测试深度学习或传统视觉模型开展目标检测与分类实验也可用于分析不同地区牛只的形态特征为遗传学分析、品种改良及疾病监控等研究提供标准化、高质量的数据支撑。1. 奶牛和水牛计算机视觉数据集从零构建一套可训练的牛只识别数据牧场里做智能化管理绕不开一个尴尬的现实牛脸识别、牛只计数、发情行为检测这些任务公开数据集几乎全是国外荷斯坦奶牛的放到国内南方水牛场模型精度直接掉一大截。奶牛和水牛计算机视觉数据集这个方向解决的就是「没有贴合自己场景的标注数据」这个卡脖子问题。它适合两类人一类是计算机视觉入门、想找一个完整项目练手的同学从采集、标注、格式转换到 yolov8训练自己的数据集 全流程走一遍另一类是养殖企业或农业科技团队的技术负责人需要评估自建牛只数据集到底要投入多少人力、踩哪些坑。这篇文章不讲空泛概念只讲我实际搭过的一套流程怎么采、怎么标、怎么转格式、怎么训、怎么验证以及哪些环节最容易翻车。2. 奶牛和水牛数据集到底要标什么任务拆解与标注体系设计2.1 先定任务再定标注粒度很多人一上来就买标注工具开始框结果标到一半发现任务定义错了返工成本极高。奶牛和水牛的视觉任务常见有四类标注方式完全不同任务类型典型应用标注形式单张耗时目标检测牛只计数、个体定位矩形框15-30秒实例分割体况评分、轮廓分析多边形60-120秒关键点姿态、跛行检测关键点坐标40-90秒身份识别牛脸/花纹ID分类标签检测框30-60秒我一般建议新手从目标检测切入因为奶牛和水牛的检测任务边界清晰标注成本最低而且 yolov5训练自己的数据集 或 yolov8 的生态最成熟遇到问题能搜到的解决方案最多。等检测跑通了再往上叠加关键点或分割。奶牛和水牛在标注上有一个关键差异奶牛花纹复杂个体区分度高适合做身份识别水牛毛色深、纹理少个体间外观差异小检测时容易互相遮挡混淆。所以如果你的场景是水牛标注时一定要把遮挡程度作为属性记录下来否则后期分析误检原因时就是一笔糊涂账。2.2 类别体系怎么定才不返工类别设计是数据集的地基。我见过太多人一开始只标一个「牛」类后来想区分奶牛和水牛或者想区分成年牛和犊牛只能全部重标。建议在标注规范里预留层级{ categories: [ {id: 1, name: cattle, supercategory: bovine}, {id: 2, name: buffalo, supercategory: bovine}, {id: 3, name: calf, supercategory: bovine} ], attributes: { occlusion: [none, partial, heavy], posture: [standing, lying, walking] } }这段 JSON 是标注体系的骨架。categories定义类别层级supercategory让奶牛和水牛共享一个上位类后期做粗粒度检测时可以直接合并。attributes是属性字段不参与检测训练但导出时保留在标注文件里方便后续做数据切片分析。比如你想单独看「重度遮挡」样本的召回率没有这个字段就只能靠肉眼翻图。参数说明id从 1 开始0 通常留给背景类name用英文小写避免中文路径在跨平台时的编码问题属性值用枚举不要用自由文本否则后期统计时全是脏数据。2.3 采集端什么角度、什么光照才算合格样本数据集质量七分靠采集。奶牛和水牛的活动场景和实验室数据集完全不同光照变化大、遮挡频繁、牛只不配合。我踩过的坑是在晴天中午采了 2000 张模型在阴天和早晚时段几乎全瞎。采集时至少覆盖这几个维度光照晴天正午、阴天、清晨、傍晚各占一定比例避免单一光照主导角度侧面、斜侧面、正面、俯视无人机或高位摄像头都要有距离近景单头牛占画面 1/3 以上、中景3-5 头、远景群体遮挡无遮挡、部分遮挡、重度遮挡各留样本场景牛舍内、运动场、挤奶通道背景差异越大越好一个实操建议用手机或运动相机先拍视频再抽帧。视频抽帧比逐张拍照效率高得多而且能捕捉到牛只运动的连续姿态。抽帧时注意去重相邻帧差异太小的直接丢弃否则数据集里全是近似重复样本训练时容易过拟合。3. 从原始视频到 YOLO 格式标注、转换与目录组织3.1 抽帧与去重的具体命令假设你拍了一段 10 分钟的牛舍视频第一步是抽帧。用 ffmpeg 按固定间隔抽# 每秒抽1帧输出到 frames 目录 ffmpeg -i cattle_video.mp4 -vf fps1 frames/frame_%04d.jpg # 如果视频很长可以每2秒抽1帧减少冗余 ffmpeg -i cattle_video.mp4 -vf fps0.5 frames/frame_%04d.jpgfps1表示每秒抽一帧10 分钟视频约产出 600 张。fps0.5是每两秒一帧适合牛只移动缓慢的场景。抽完后不要直接标先做去重。去重用感知哈希pHash快速筛import imagehash from PIL import Image import os def deduplicate(folder, threshold5): hashes {} for fname in sorted(os.listdir(folder)): path os.path.join(folder, fname) h imagehash.phash(Image.open(path)) for existing, existing_path in hashes.items(): if abs(h - existing) threshold: os.remove(path) print(f删除重复: {fname}) break else: hashes[h] path deduplicate(frames, threshold5)threshold5是汉明距离阈值越小越严格。牛只场景建议设 5-8太严格会误删不同姿态的样本太宽松则去重效果差。这段代码的逻辑是对每张图算 pHash如果和已保留的某张图哈希距离小于阈值就判定为重复并删除。3.2 标注工具选型与协作规范标注工具我用过 LabelImg、CVAT、Roboflow 三款。单机小规模用 LabelImg 够用多人协作必须上 CVAT 或 Roboflow否则版本混乱会让你崩溃。标注规范要写成文档至少包含框的边界以牛只可见轮廓为准被遮挡部分不脑补最小尺寸小于 20x20 像素的牛只不标避免噪声截断处理画面边缘被截断的牛只如果可见部分超过 50% 就标否则跳过类别判定奶牛和水牛混淆时以耳朵形状和角型为准不确定的标记为「待定」单独存放我一般会先标 100 张做一轮交叉校验三个人标同一批算 IoU 一致性。如果一致性低于 0.85说明规范有歧义先改规范再继续不要硬标。3.3 转成 YOLO 格式脚本与四个边界坑标注完导出通常是 COCO JSON 或 Pascal VOC XMLYOLO 需要的是每张图一个 txt每行class_id x_center y_center width height全部归一化到 0-1。转换脚本import json import os from PIL import Image def coco_to_yolo(coco_json, img_dir, out_dir): with open(coco_json) as f: data json.load(f) # 建立 image_id - 文件信息 的映射 img_map {img[id]: img for img in data[images]} # 建立 category_id - 连续索引 的映射 cat_ids sorted([c[id] for c in data[categories]]) cat_map {cid: idx for idx, cid in enumerate(cat_ids)} for ann in data[annotations]: img_info img_map[ann[image_id]] w, h img_info[width], img_info[height] x, y, bw, bh ann[bbox] # COCO bbox 是左上角宽高YOLO 需要中心点宽高 x_center (x bw / 2) / w y_center (y bh / 2) / h nw, nh bw / w, bh / h # 边界裁剪防止浮点误差导致越界 x_center min(max(x_center, 0), 1) y_center min(max(y_center, 0), 1) nw min(max(nw, 0), 1) nh min(max(nh, 0), 1) line f{cat_map[ann[category_id]]} {x_center:.6f} {y_center:.6f} {nw:.6f} {nh:.6f}\n txt_name os.path.splitext(img_info[file_name])[0] .txt with open(os.path.join(out_dir, txt_name), a) as f: f.write(line) coco_to_yolo(annotations.json, images, labels)四个边界坑必须注意第一COCO 的 bbox 格式是[x_min, y_min, width, height]不是[x_min, y_min, x_max, y_max]搞错会导致框整体偏移。第二归一化前要确认 width/height 是原图尺寸如果标注时用了缩放图这里必须用原图尺寸。第三浮点误差可能让归一化值略微超出 0-1YOLO 训练时会报错必须裁剪。第四一张图有多头牛时要追加写入同一个 txt不能用w模式覆盖。3.4 目录组织与 data.yamlYOLO 训练要求固定的目录结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml内容path: /home/user/dataset train: images/train val: images/val test: images/test nc: 3 names: [cattle, buffalo, calf]nc是类别数必须和 names 长度一致。path用绝对路径相对路径在不同工作目录下容易找不到文件。划分比例我一般用 7:2:1但如果总样本少于 1000 张验证集至少留 150 张否则评估指标波动太大。4. 训练奶牛和水牛检测模型参数、显存与收敛排查4.1 用 yolov8训练自己的数据集最小可跑命令环境装好后最小训练命令yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns/cattle \ nameexp1modelyolov8n.pt是 nano 版本参数量最小适合先跑通流程。imgsz640是输入分辨率牛只目标如果普遍较小可以提到 1280但显存占用会翻倍。batch16在 8GB 显存上跑 640 分辨率基本安全如果 OOM 就降到 8 或 4。device0指定第一块 GPUCPU 训练用devicecpu但速度慢到不实用。4.2 牛只场景的必调参数默认参数在牛只数据集上往往不够这几个必须调参数默认值建议值原因imgsz640960或1280远景牛只目标小batch168-32按显存调整lr00.010.005-0.01小数据集降低学习率patience5020-30早停防过拟合mosaic1.00.5-1.0增强遮挡场景鲁棒性degrees0.05-10牛只姿态角度变化patience20表示 20 轮验证指标不提升就停止小数据集上默认的 50 轮太浪费。mosaic0.5降低马赛克增强强度因为牛只场景本身遮挡就多过强的 mosaic 会让模型学到不真实的拼接模式。4.3 训练不收敛时先看什么训练翻车的典型现象和对策loss 不下降先检查标注文件。最常见的是类别 id 从 1 开始而不是 0YOLO 会直接忽略或报错。用yolo detect train前先跑一遍数据校验yolo detect train datadataset/data.yaml modelyolov8n.pt epochs1 imgsz640一轮就能暴露大部分数据格式问题。如果 loss 震荡剧烈把lr0降到 0.001或者把batch调大稳定梯度。如果验证集 mAP 远低于训练集说明过拟合加数据增强或减模型容量。显存不够时优先降batch其次降imgsz最后才考虑换更小的模型。因为牛只检测对小目标敏感降分辨率对精度伤害最大。5. 数据集质量验证与常见踩坑排查5.1 标注质量的三道检查标完不等于能用。我一般做三道检查第一道可视化抽查。随机抽 50 张把标注框画回原图肉眼过一遍。框偏移、类别错标、漏标这三类问题最容易发现。第二道统计分布。用脚本统计每类样本数、每张图平均目标数、框的宽高分布import os from collections import Counter def stats(label_dir): cls_counter Counter() box_sizes [] for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname)) as f: for line in f: parts line.strip().split() cls_counter[int(parts[0])] 1 box_sizes.append((float(parts[3]), float(parts[4]))) print(类别分布:, dict(cls_counter)) avg_w sum(b[0] for b in box_sizes) / len(box_sizes) avg_h sum(b[1] for b in box_sizes) / len(box_sizes) print(f平均框尺寸: {avg_w:.4f} x {avg_h:.4f}) stats(dataset/labels/train)如果某一类占比超过 80%说明类别不平衡训练时要用cls权重或过采样。如果平均框尺寸小于 0.05说明小目标为主必须提高输入分辨率。第三道交叉验证。用训练好的模型在验证集上跑推理把误检和漏检的图挑出来人工复核是标注问题还是模型问题。这一步能发现很多隐藏的标注错误。5.2 避坑牛只数据集最常见的五个翻车点现象模型在训练集上 mAP 很高一到牧场实际场景就大量漏检。原因训练集光照和角度太单一模型学到了背景捷径而不是牛只特征。 解决按 2.3 节的采集维度重新补采确保每个光照和角度都有样本训练时开启hsv_h、hsv_s、hsv_v颜色增强。现象奶牛和水牛互相误检尤其是深色水牛被检成奶牛。原因两类外观差异在低分辨率下不明显模型区分能力不足。 解决提高imgsz到 960 以上同时在标注时确保两类边界清晰必要时增加一个「深色牛」的中间类做过渡。现象标注文件里出现空 txt 或只有换行的文件。原因转换脚本对无标注图片也生成了文件或者写入时异常中断。 解决训练前扫描并删除空标签文件同时确认对应图片是否真的没有目标如果是漏标要补标。现象训练到一半 loss 突然变成 NaN。原因学习率过高或标注框归一化值越界。 解决检查 3.3 节的边界裁剪是否生效把lr0降到 0.001开启ampFalse排除混合精度问题。现象验证集 mAP 波动很大每次训练结果差很多。原因验证集太小或者划分时没有按场景分层。 解决验证集至少 150 张划分时确保训练集和验证集的光照、场景分布一致不要随机划分。6. 把数据集用出复利增量标注与主动学习闭环数据集不是标完就完了。牧场场景会变牛只会长大摄像头会换位置一次标注的数据集半年后可能就不够用了。我现在的习惯是搭一个增量闭环模型上线后把置信度低和人工复核出错的样本自动回流到待标注队列每周标一批每月重训一次。具体做法是用推理脚本筛出「困难样本」from ultralytics import YOLO import os model YOLO(runs/cattle/exp1/weights/best.pt) def mine_hard_samples(img_dir, out_dir, conf_thres0.3): os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(img_dir): path os.path.join(img_dir, fname) results model(path, confconf_thres, verboseFalse) for r in results: # 有检测结果但置信度都低于阈值说明模型不确定 if len(r.boxes) 0 and r.boxes.conf.max() 0.6: os.system(fcp {path} {out_dir}/) break # 完全没检测到可能是漏检 if len(r.boxes) 0: os.system(fcp {path} {out_dir}/) break mine_hard_samples(new_frames, hard_samples)conf_thres0.3是初筛阈值低于这个值的检测结果直接丢弃。0.6是困难样本判定阈值最高置信度低于 0.6 说明模型对这个场景没把握。这段脚本把「有目标但不确定」和「完全没检出」两类图都捞出来人工过一遍确认是漏检还是真没牛。这个闭环跑起来后每轮增量标注只需要标 100-200 张但带来的精度提升比一次性标 2000 张还明显。因为困难样本的信息密度远高于随机样本。最后说一个我自己的教训早期我总想一次把数据集标完美结果拖了两个月还没开始训练。后来改成「先标 500 张跑通全流程再增量迭代」两周就出了第一版可用模型。数据集是迭代出来的不是一次设计出来的。希望帮到你。本文还有配套的精品资源点击获取
返回列表