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

资讯详情

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

从COCO JSON标注到YOLOv8训练:猪行为识别完整实践

从COCO JSON标注到YOLOv8训练:猪行为识别完整实践 简介针对猪圈场景的猪行为识别数据集覆盖喝、吃、睡觉、站立等常见行为类别平均正确识别率达92.6%适用于农业智能化、动物行为分析以及计算机视觉目标检测等方向的开发者和研究人员开展模型训练与效果验证。资源共1275个文件主体为1272张JPG图片另有3个JSON标注文件压缩包整体约85.57MB。图片文件名保留了原始视频来源与帧信息可看出数据来自猪舍监控视频抽帧方便按场景回溯与筛选标注文件采用COCO JSON格式可直接接入MMDetection、Detectron2等主流框架省去数据格式转换步骤。目前已有213人学习下载适合用于构建贴近真实养殖环境的猪行为识别模型也可作为目标检测与行为分类任务的练习数据集。1. 猪圈里的猪行为识别数据集从1272张图片到92.6%准确率的关键在哪养猪场的监控摄像头越来越多但视频流只是被存下来真正需要识别的行为——喝、吃、睡觉、站立——仍然依赖人工回看。猪的行为是健康状况的晴雨表不吃不喝、长时间卧着不动都可能是异常信号。这个数据集的价值在于它用1272张图片覆盖了最基本的行为类别并以coco json格式给出了标注让做算法验证的人不用从零开始采集和清洗数据。适用人群很明确想快速跑通一个行为识别demo的学生、刚接触数据标注的算法工程师以及要给猪场做软硬件方案的集成商。92.6%的平均正确识别率不是拍脑袋的数字它背后是合理的行为定义、规范的标注格式和可复现的模型训练流程。接下来我会从标注格式、数据转换到训练评估把这条链路完整拆开。2. 从行为定义到coco json标注猪行为数据集的构建要点2.1 猪行为类别定义喝、吃、睡觉、站立的可判定边界行为识别和普通目标检测最大的区别在于“类别”是动态的。一张图上猪还是那头猪但它正在做的动作决定了标签。标注前必须把行为边界定死否则标注员之间会产生大量不一致。常见的定义如下喝猪的嘴部接触饮水器或者水嘴同时头部朝下或侧向饮水器。吃猪的头部伸入食槽并有咀嚼或吞咽动作。如果只是站在食槽旁不算吃。睡觉猪侧躺、趴卧且眼睛闭合或没有明显的肢体活动。站立但是闭眼不算睡。站立四条腿支撑身体头部抬起没有进食或饮水行为。这四条规则看似简单但实际标注时经常遇到重叠场景猪站着头伸进槽里但没咬到食物算吃还是站我的建议是“优先行为原则”——只要嘴部接触食槽或饮水器就归为吃或喝否则即使身体姿势接近也归为站立。标注手册里必须写清楚这类优先级数据质量才有保障。2.2 使用cvat标注工具产出coco json格式的实操步骤cvat是当前做计算机视觉标注的常用工具开源、支持团队协作也支持导出coco json格式。用cvat标注猪行为数据的流程如下创建项目添加标签drinking、eating、sleeping、standing。上传1272张猪圈图片按8:2或9:1拆为训练集和验证集分任务。在每张图上使用矩形框bounding box框住单头猪然后分配行为标签。一个框只标一个行为。如果一头猪同时有两个动作以嘴部状态为准。标注完成后导出为coco json格式注意在导出选项里勾选“include annotations”和“images with annotations”。cvat导出的coco json和标准coco格式大体一致但有时会出现图片路径、license等字段缺失需要自己补。另外cvat默认把类别name保存成数字id导出后要检查categories字段里的name和id映射是否符合预期。2.3 coco json结构里与行为识别相关的关键字段一个标准的coco json数据集包含images、annotations、categories三个顶层字段。行为识别数据集在categories里定义行为类别在annotations里通过category_id指定每头猪的行为。下面是一个简化示例{ images: [ { id: 1, file_name: 001.jpg, width: 1920, height: 1080 } ], annotations: [ { id: 1, image_id: 1, category_id: 3, bbox: [420.1, 315.2, 186.7, 242.5], area: 45216.5, iscrowd: 0 } ], categories: [ {id: 1, name: drinking}, {id: 2, name: eating}, {id: 3, name: sleeping}, {id: 4, name: standing} ] }这段json里bbox的四个数字分别是左上角x、左上角y、宽、高单位是像素。area可以由w*h算出来但cvat导出时会自己填。iscrowd在猪行为标注里一般设为0因为我们要检测的是个体不是密集人群。验证json格式是否正确可以写一个简单的解析脚本import json with open(annotations.json, r) as f: coco json.load(f) cat_map {c[id]: c[name] for c in coco[categories]} print(类别映射:, cat_map) ann coco[annotations] img coco[images] print(标注数量:, len(ann), 图片数量:, len(img)) # 检查每张图的标注框是否越界 for a in ann: img_info next(i for i in img if i[id] a[image_id]) x, y, w, h a[bbox] if x w img_info[width] or y h img_info[height]: print(f图片 {a[image_id]} 的框越界: {a[bbox]})这个脚本先建立类别id和名称的映射再统计标注数量最后检查bbox是否超出图片边界。越界的框通常是标注时误拖鼠标导致的轻则影响训练重则让模型在推理时抛出异常所以数据清洗这一步不能跳过。注意area这个字段在coco检测任务里是必填项但yolo格式转换时用不到你可以保留也可以忽略。3. 1272张图片的样本分布与模型选型为什么不做视频序列而做图像识别3.1 小样本下行为识别的两种路线图像分类与目标检测面对1272张图片直接上视频行为识别网络如SlowFast、I3D并不现实因为视频模型需要连续帧序列作为输入数据量至少十几万帧起步。更可行的路线有两种路线A整图分类。把每张图片按主要行为分成四类用ResNet或EfficientNet训练。优点是标注成本低缺点是猪圈里通常多头猪共存整图分类只能给出画面主行为无法定位哪头猪在喝、哪头在睡。路线B目标检测行为分类。先用检测框框出每头猪再对框内图像做行为分类。这就回到了coco json的原始设计每个bbox的类别就是行为。这样不仅知道猪在哪还知道它在干什么。考虑到标题里明确写了coco json格式标注且平均正确识别率达到92.6%说明这个数据集走的是路线B甚至可能直接训练了yolov8或类似检测器。用检测网络做行为识别的好处是端到端输入图片直接输出行为类别和位置推理链路短部署方便。3.2 从coco json转换成yolo格式的脚本与参数yolov8等主流检测框架默认使用yolo格式的txt标注而不是coco json。转换时要把coco的[x, y, w, h]转成归一化的[center_x, center_y, w, h]同时保持类别id连续。下面是一个可直接运行的转换脚本import json import os def coco_to_yolo(coco_path, img_base, out_dir): with open(coco_path, r) as f: coco json.load(f) # 建立image_id到文件名、宽高的映射 img_map {} for img in coco[images]: img_map[img[id]] { file: os.path.join(img_base, img[file_name]), w: img[width], h: img[height] } # 建立category_id到连续id的映射按名称排序保证稳定性 cats sorted(coco[categories], keylambda x: x[name]) cat_id_map {} for new_id, cat in enumerate(cats): cat_id_map[cat[id]] new_id os.makedirs(out_dir, exist_okTrue) # 对每张图片生成一个txt文件 for img_id, img_info in img_map.items(): txt_path os.path.join(out_dir, os.path.basename(img_info[file]).replace(.jpg, .txt)) with open(txt_path, w) as f: for ann in coco[annotations]: if ann[image_id] ! img_id: continue cat_id cat_id_map[ann[category_id]] x, y, w, h ann[bbox] # 归一化到[0,1] center_x (x w / 2) / img_info[w] center_y (y h / 2) / img_info[h] norm_w w / img_info[w] norm_h h / img_info[h] # 约束范围防止越界 center_x min(max(center_x, 0), 1) center_y min(max(center_y, 0), 1) norm_w min(max(norm_w, 0), 1) norm_h min(max(norm_h, 0), 1) f.write(f{cat_id} {center_x:.6f} {center_y:.6f} {norm_w:.6f} {norm_h:.6f}\n) if __name__ __main__: coco_to_yolo(annotations.json, images, yolo_labels)转换逻辑里做了三件关键事一是重新映射类别id保证从0开始连续二是把绝对坐标转成归一化坐标这对应yolo模型对输入尺寸不敏感的特性三是用min/max把坐标限制在0到1之间避免浮点误差导致的部分框超出边界。执行脚本后每个images目录下的.jpg文件都能在yolo_labels里找到对应的同名txt文件里面每行代表一个猪目标第一个数字是行为类别后面四个数字是归一化坐标。3.3 样本分布检查与数据增强策略1272张图片不算多尤其对检测任务来说每张图可能只包含一两头猪数据量很容易偏少。训练前必须检查每个类别的对象数量分布防止某个行为占比过低导致模型偏向多数类。可以用下面的统计脚本from collections import Counter import json with open(annotations.json, r) as f: coco json.load(f) cat_map {c[id]: c[name] for c in coco[categories]} counts Counter(cat_map[a[category_id]] for a in coco[annotations]) for k, v in counts.items(): print(f{k}: {v})如果发现某个类别比如喝只有100多个目标而睡觉有400多个就需要在训练时针对少样本类别加权重或者做离线增强。常用的增强手段包括水平翻转、亮度抖动、高斯噪声、小角度旋转。注意猪行为识别中垂直翻转要慎用因为摄像头固定在高处翻转后会得到不真实的地面视角。对于“喝”这类行为嘴部区域细节很重要不能用太强的模糊或裁剪否则会破坏关键特征。4. 用yolov8训练猪行为识别模型配置、训练与92.6%复现路径4.1 数据集划分与配置文件写法yolov8需要一份数据集yaml文件指明训练集和验证集的图片路径以及类别名称。按照8:2划分1272张图片验证集约254张。yaml配置如下path: /path/to/pig_behaviors train: images/train val: images/val names: 0: drinking 1: eating 2: sleeping 3: standing这里path是数据集根目录train和val是相对根目录的图片文件夹路径。yolov8会自动读取每个图片同名的txt标注文件所以必须保证图片文件夹和标注文件夹的basename一致只是后缀不同。names列表的顺序必须和3.2节转换脚本里cat_id_map的顺序严格一致否则类别就错位了。我一般会在训练前先跑一次debug命令打印一张图片上的实际标注确认无误再开正式训练。4.2 训练命令与关键超参数说明在yolov8环境下训练命令如下yolo detect train datapig_data.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0 patience20各参数含义如下data数据集yaml路径。model预训练权重。用小模型yolov8s.pt起步1272张图不至于过拟合太快。epochs训练轮数。100轮在小数据集上通常是足够的配合patience早停可以防止后期震荡。imgsz输入尺寸。640是速度和精度的折中。猪的行为细节不算微小不需要特别大的分辨率。batch受显存限制。16对于8GB显存来说比较稳显存小就降到8。patience验证集指标连续20轮不提升就停止。缓解过拟合。训练结束后模型权重保存在runs/detect/train/weights/里。注意yolov8默认使用best.pt和last.pt两个文件验证指标最好时保存的是best.pt后续推理应该加载它而不是last.pt。4.3 平均正确识别率是怎么算的从mAP到准确率标题里写“平均正确识别率92.6%”这个数字不能直接等同于mAP更常见的是测试集上的行为分类准确率。需要先明确评估口径否则复现时没法对齐。我一般会做两步验证在验证集上跑检测评估看每个类别的Precision、Recall和mAP0.5。把所有检测框过滤后按类别统计预测正确的比例即正确识别率 预测正确的框数 / 总框数。计算命令yolo detect val datapig_data.yaml modelruns/detect/train/weights/best.pt运行结束后终端会输出每个类别的p、r和mAP50。如果想复现92.6%这个值就要重点看验证集上的mAP50或者按框计算的accuracy。如果只关心四类行为的平均正确率可以写个简单脚本统计confusion matrix看哪些类别互相混淆。5. 部署时的行为判定陷阱时间平滑、置信度阈值与边缘案例处理模型训练完只完成一半部署到猪场实时视频流才是真正的考验。单帧检测的结果是抖动的猪低头喝水时偶尔被前腿挡住模型可能某一帧判成站立下一帧又判回喝水。直接输出单帧结果监控画面会频繁闪变失去实用价值。常见做法是加时间平滑对同一头猪的行为预测结果做滑动窗口投票。下面是一个轻量级的后处理逻辑假设检测器每500毫秒输出一帧结果from collections import deque class BehaviorSmoother: def __init__(self, window_size5): self.window deque(maxlenwindow_size) def update(self, behavior_id): self.window.append(behavior_id) # 投票选出现次数最多的行为 from collections import Counter counter Counter(self.window) most_common counter.most_common(1)[0] # 只有当最高票超过50%时才更新结果 if most_common[1] / len(self.window) 0.5: return most_common[0] return None # 不稳定保持上一状态 smoother BehaviorSmoother(window_size5) # 模拟10帧的检测结果 for frame, pred in enumerate([1, 1, 2, 2, 2, 2, 2, 2, 2, 2]): result smoother.update(pred) if result is not None: print(f帧 {frame}: 行为切换为 {result})这里的window_size决定平滑力度5帧意味着最多抑制两次孤立误判。低于50%支持率的预测直接丢弃让输出保持上一状态这样画面不会来回跳变。部署时还要根据实际场景调整置信度阈值yolo detect predict默认0.25但猪圈里目标多且有遮挡可以提高到0.3到0.4减少误报。如果某个特定行为比如“喝”频繁漏检可以考虑对该类别单独降低阈值yolov8支持在推理时传conf参数但更精细的办法是使用predict接口逐类别设置。边缘案例是另一大坑。猪群叠在一起时后面那头猪的框可能只有半个身子这时检测器很容易把睡着的猪框成站立。我的建议是在输入端就过滤极小面积的框比如面积小于图像尺寸1%的目标直接忽略因为它们对行为统计没有意义。另一个技巧是统计每头猪的行为持续时间喝水少于2秒的通常是误检或者只是舔了一口不能算真正喝水睡觉持续10分钟以上才值得记录。把单帧识别升级为时间窗口内行为事件才能让猪场管理者拿到真正有用的报警。本文还有配套的精品资源点击获取
返回列表