简介面向养殖场智能化管理场景这套资源围绕YOLOv5生猪行为状态检测提供成套方案内含训练权重、1000余条标注数据以及PyQt界面脚本。数据集已按train/val/test划分好附带data.yaml和txt格式标签覆盖进食、站立、躺卧、攻击四类行为模型配置与目录结构可直接被YOLOv5、YOLOv7、YOLOv8、YOLOv9等版本调用免去自行标注、整理目录和编写配置的繁琐流程。压缩包共2000个文件以1210个txt标签、706个jpg图片为主体另有py脚本、yaml配置、xml标注、说明文档等整体约274MB既能用于模型训练也便于基于PyQt做界面化检测演示。当前已有447人学习浏览适合农业AI开发者、算法学习者和养殖监控方案设计者使用可直接用于猪只行为识别、异常攻击预警等方向的快速验证与二次开发。1. 养殖场猪只行为检测为什么是yolov5pyqt这套组合而不是现成盒子进到一间现代化猪舍最不缺的就是摄像头最缺的是盯着屏幕看的人。配种员一天要巡好几遍栏看猪有没有压伤、有没有互咬、饮水和采食是否正常时间一长眼睛先受不了漏报是常态。标题里这条路线给出的是一套自建方案用yolov5做检测骨架拿1000张猪只图像训练一份能区分站立、卧姿、采食、饮水、争斗等行为状态的权重再套一个PyQt写的桌面程序让场内人员点开就能看图看结果。它解决的不是“猪脸识别”那种锦上添花的事而是最基础的栏舍巡检和异常预警。适合三类人养殖场想自建软件的技术员、接畜牧智能化项目的开发者、以及用行为数据做课题的学生。三拨人读到的落地路径是同一份区别只在最终交付形态。2. 先得有像样的数据猪只行为数据集的类别定义、采集与标注转换这一章看起来是在讲数据实际上决定了后面权重能用到什么程度。我见过太多人拿到1000张图就往yolov5里塞训练也跑完了一测才发现要么类别分得太粗没法用要么验证集和训练集里有同一头猪指标虚高到自欺欺人。2.1 行为类别定义先想清楚要分几类再决定采集策略养殖场猪只检测最常见的坑是把“行为状态”和“个体识别”混在一起。标题要求的是行为状态检测不是追踪每一头猪是谁所以类别定义要落在动作本身。我一般这样分standing站立、lying卧姿、eating采食、drinking饮水、fighting争斗、lame跛行/异常姿态。6类是一个很稳的起点再多比如分“侧卧”“趴卧”“半卧”标注成本翻倍模型区分度却上不去再少比如只分“正常”和“异常”争斗和跛行混在一类里兽医拿到的信息几乎没用。这个定义和后面的标签文件、类别顺序强相关。classes.txt里第几行写什么训练时data.yaml里的nc和names就必须对应同一个顺序。建议先在Excel里列一张类别表标好中文名、英文名、典型姿态描述、常见误判场景再开始采集和标注。别小看这一步后面改类别顺序要重新生成全部标签真的是血泪经验。2.2 从监控录像里攒出1000张图采集、初筛与去重1000张听起来不多但对一只固定机位的栏舍监控来说完全够用前提是分布合理。我一般从至少3天的监控视频里抽帧覆盖早中晚不同光照、饲喂前后不同时段、清扫时人等干扰画面。抽帧间隔要大于10秒否则连续帧几乎相同等于无效样本。抽完先做一轮人工初筛剔除模糊、遮挡超过70%、目标小于20×20像素的帧。初筛后的去重容易被忽略。同一个栏舍同一个时间段里猪的姿势变化其实不快很多帧看起来不同、但目标框位置高度重合。可以用简单的感知哈希或直接按文件名间隔抽样来粗去重。真实场景里还有一类样本必须主动补拍争斗和跛行自然发生频率低1000张图里可能只有七八十张含争斗这会导致类别严重不均衡。常见做法是单独把这类片段挑出来连帧抽把争斗样本扩到150张以上再把总数拉到1200到1500张。训练时按比例随机采样少样本类别才不会被淹没。2.3 用LabelImg标注并转成yolo格式目录结构与转换脚本标注工具用LabelImg就行输出Pascal VOC的xml格式再用脚本转成yolov5要的txt。目录结构按yolov5的习惯组织pig_dataset/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── ... │ └── val/ │ ├── 101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 001.txt │ │ └── ... │ └── val/ │ ├── 101.txt │ └── ... ├── classes.txt └── data.yaml标签文件每行是一类目标格式为“类别id x_center y_center width height”全部归一化到0到1。写转换脚本时最需要注意的是坐标归一化别除错尺寸xml里是像素坐标而yolov5要的是相对坐标import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) out_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) x_min float(box.find(xmin).text) y_min float(box.find(ymin).text) x_max float(box.find(xmax).text) y_max float(box.find(ymax).text) # 归一化到[0,1]防止坐标出边界 x_center ((x_min x_max) / 2) / img_w y_center ((y_min y_max) / 2) / img_h box_w (x_max - x_min) / img_w box_h (y_max - y_min) / img_h out_lines.append(f{cls_id} {x_center:.4f} {y_center:.4f} {box_w:.4f} {box_h:.4f}) file_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, file_name), w) as f: f.write(\n.join(out_lines)) class_names [standing, lying, eating, drinking, fighting, lame] os.makedirs(labels/train, exist_okTrue) os.makedirs(labels/val, exist_okTrue) for split in [train, val]: xml_dir fxmls/{split} out_dir flabels/{split} for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(xml_dir, xml_file), out_dir, class_names)这段脚本会把xmls目录下按train/val分好的标注统一转成txt并写入labels。坐标全部除以真实图片宽高做归一化这样不管训练时img-size设640还是1280标签都不用改。如果你的标注xml里存在目标超出图像边界的框脚本里最好加一句判断x_min、x_max任何值小于0或大于img_w时做clip裁剪否则训练时容易报“invalid box”警告甚至把loss算成nan。2.4 扩充到能训练的规模数据增强与train/val划分1000张原始图直接训练yolov5s理论上跑得动但val集小、指标波动大一个epoch的随机性就能让map差两三个点。常见做法是让yolov5自己的增强管线来扩不需要离线用imgaug做一堆增强训练时默认开mosaic、hsv扰动、随机缩放平移相当于每张图每轮都被重画过。要调整的是训练超参里的mosaic概率和hsv色域幅度这放到下一章讲。数据划分要单独说。千万别对整个数据集做random.shuffle猪是有个体记忆的同一头猪在不同帧里外观高度相似随机划分会导致训练集和验证集里出现“同一头猪”验证指标虚高到现场换一批猪就翻车。正确的做法是按录像时间段切前两天的帧全部进train后一天的帧全部进val宁可val样本少几十张也不要混时段。切分脚本一句话就能完成import os import random random.seed(42) img_files os.listdir(images/train) # 按文件名前缀的日期时间分组同一时间段整体划分 day_groups {} for f in img_files: date_part f.split(_)[0] # 例如 20250410_083000.jpg - 20250410 day_groups.setdefault(date_part, []).append(f) train_files [] val_files [] for day, files in sorted(day_groups.items()): if day.endswith((09, 10)): # 前两天进训练 train_files.extend(files) else: # 最后一天进验证 val_files.extend(files) print(len(train_files), len(val_files))切分逻辑是按日期后缀把不同天归组前两天做训练、最后一天做验证保证同一头猪不会同时出现在两个集合里。验证集一般占总量的15%到20%如果某一天图像特别少就把验证集比例压到10%到12%同时用mAP和F1两个指标一起看不要只盯着一个数。3. 训练自己的猪只检测权重从命令到超参数的落地路径数据就位后进入正题怎么把1000张猪只图像变成一份可用的best.pt。很多教程只给一条train.py命令实际上yolov5训练自己的数据集的关键在于几个前置选择和三个超参数的调整这一章按我自己的实操顺序来写。3.1 模型尺寸选择yolov5s还是yolov5m养殖场这个场景有个特点机位固定、算力环境差、对单帧延迟要求不高但对漏检容忍度低。栏舍里白天和夜间的光照差别大猪的纹理又被铁栏杆分成碎片并不是简单的检测任务。如果后备母猪栏里的机器是几年前的老工控机显卡都没有只能用CPU推理那yolov5n或yolov5s是唯一选择如果有一张1080以上的卡训练推理时还能用GPUyolov5m的收益就很明显尤其在躺卧和争斗这类小目标上m的召回比s稳定不少。我的建议是拿yolov5s起步先把数据和流程跑通再在同样的数据上训练一版yolov5m做对比。不要一开始就上yolov5l1000张的数据量喂不饱大模型训练时间翻倍最后map反而可能比s低。这个“数据集规模决定模型上限”的道理在目标检测里很常见模型越大不代表越好只是拟合能力更强数据不够就变成硬背训练集。3.2 训练命令逐项拆解从目录结构到启动训练先拉代码和装依赖。yolov5的官方仓库ultralytics维护 clone 下来后用pip安装requirements.txt里的依赖。注意PyQt界面需要的是pyqt5和opencv-python训练环境里这两个不是必需但建议在同一个conda环境里装好避免后面部署时环境冲突。git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt然后准备data.yaml确认类别数和类别名和上一章保持一致# pig_dataset/data.yaml train: ../pig_dataset/images/train val: ../pig_dataset/images/val nc: 6 names: [standing, lying, eating, drinking, fighting, lame]路径这里有个细节data.yaml里的路径是相对于你执行train.py的目录也就是yolov5目录来解析的我习惯用相对路径从yolov5指向pig_dataset。如果写成绝对路径换台机器就得改反而麻烦。接着启动训练python train.py \ --data ../pig_dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --hyp data/hyps/hyp.scratch-low.yaml \ --device 0逐项说参数--img是输入分辨率640在检测精度和速度之间最平衡如果你的猪栏是远景镜头、猪目标普遍偏小可以试1280但训练时间直接翻倍--batch受显存限制在6G卡上16比较稳8G可以到32--epochs设100配合早停机制一般60到80轮就收敛--hyp指定超参文件首次训练用scratch-low最稳它各项增强幅度都偏保守适合数据量一般的情况。首次运行会自动下载coco预训练权重yolov5s.pt到仓库根目录看到下载输出不用慌中断了重新执行会断点续传。3.3 优先调哪几个超参数lr0、mosaic与锚框策略训练跑通之后真正影响结果的是超参数这也是“yolov5超参数怎么调”最常见的提问点。我不建议一上来就改一堆三个优先学习率lr0、数据增强mosaic概率、锚框自动计算开关。yolov5在data/hyps/hyp.scratch-low.yaml里默认lr0是0.01mosaic默认1.0。如果你的loss曲线在前10个epoch就震荡很厉害、val_map来回跳把lr0降到0.005多数情况下立竿见影。mosaic增强是把4张图拼成一张训练对1000张这种小数据集帮助很大但它会把猪切碎刚开始几个epoch模型容易懵。我的习惯是训练前期保留mosaic1.0如果发现前20轮loss下不去就把hyp文件里的mosaic改为0.5再续训。第三个是锚框yolov5默认按coco数据集的框大小设计了锚但猪的行为框和coco里的物体差异很大。训练时加上--noautoanchor参数先跑一轮用训练日志里“Autoanchor: Analyzing anchors...”打印的建议值再手动填回模型yaml里。也可以不加参数让它自动算但自动算用的聚类样本是当前训练集通常更贴合。python train.py --data ../pig_dataset/data.yaml --weights yolov5s.pt \ --img 640 --batch 16 --epochs 100 --noautoanchor --device 0用--noautoanchor跑完后去runs/train/exp/里看日志输出的anchor建议值打开models/yolov5s.yaml把里面的anchors替换成建议值再正常训练一次。这个操作看着折腾实际对猪只这类长宽比变化大的行为框站立框高瘦、卧姿框矮宽收益明显值得多花一轮的时间。3.4 训练产物分析best.pt与last.pt怎么选指标怎么读训练结束后runs/train/exp/weights目录下有两个文件best.pt和last.pt。best.pt是验证集map最高的那一轮权重last.pt是最后一轮。部署PyQt界面时几乎一定选best.pt除非你训练尾段发生了严重过拟合best和last指标差得离谱那时才要考虑last。判断过拟合看两个指标val_map不再涨但train_loss还在降或者confusion_matrix里某一类几乎全被预测成另一类。训练输出的图表里优先看confusion_matrix.png和results.png。results.png里的val_map曲线如果最后10轮还在缓慢上升建议取消早停再训30轮val_map从0.5到0.65说明数据够但某类行为没学好常见的是fighting和lame样本太少。这时间不要急着加数据先打开val_batch0_prediction.jpg看预测框把误检严重的类别挑出来补样本比盲目加图有效。4. 用PyQt把检测权重变成养殖场内可点的桌面工具权重训练好只完成一半猪场里真正操作的人不会开终端敲python他们要的是一个双击打开的窗口。PyQt在这个方案里的角色就是把yolov5的推理过程包成一个带按钮、带显示区、带结果表格的工具。标题里既然写了pyqt界面这一章就按界面设计、推理线程、绘制渲染、打包分发四步来拆。4.1 界面按什么流程设计模型加载、输入源、结果展示三大块界面不需要花哨但流程必须符合场内人员的操作习惯。我一般把主窗口分成三块顶部是一排按钮和下拉框包括“加载模型权重”“选择图片/视频”“开始检测”“停止”中间是图像显示区用QLabel放QPixmap底部是结果表格实时刷新当前检测到的猪只数量和各类行为的计数。from PyQt5.QtWidgets import QMainWindow, QLabel, QPushButton, QFileDialog, QComboBox, QGridLayout, QWidget class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(猪只行为状态检测) self.model None self.thread None self._build_ui() def _build_ui(self): self.source_combo QComboBox() self.source_combo.addItems([图片, 视频, 摄像头]) self.open_btn QPushButton(打开文件) self.open_btn.clicked.connect(self.open_file) self.run_btn QPushButton(开始检测) self.run_btn.clicked.connect(self.start_detect) self.image_label QLabel(未加载模型) self.image_label.setMinimumSize(800, 600) # 布局略QGridLayout 把控件按行放好代码里没有把布局写全因为不同屏幕分辨率下尺寸策略不一致这个可以根据实际屏幕调。要点是加载模型按钮和开始检测按钮要分开因为模型加载耗时几秒甚至十几秒如果做成一个按钮用户会以为程序卡死。按钮状态也要做联动未加载模型时“开始检测”置灰检测中时“打开文件”置灰。4.2 用QThread跑推理把yolov5的检测过程丢出主线程最常见的PyQt翻车现场是在按钮的槽函数里直接调model(img)图片推理一次还好视频流一帧帧跑界面立刻转圈无响应。yolov5的推理本身有耗时显卡上几十毫秒CPU上几百毫秒这些时间如果阻塞在GUI主线程里窗口就“假死”。解决办法是把推理放进QThread通过信号把结果传回主线程。用torch.hub加载自定义权重最省事import os import sys import cv2 import torch from PyQt5.QtCore import QThread, pyqtSignal class DetectThread(QThread): frame_ready pyqtSignal(dict) def __init__(self, model, source_type, source_path, parentNone): super().__init__(parent) self.model model self.source_type source_type self.source_path source_path self._running True def stop(self): self._running False def run(self): if self.source_type 图片: img cv2.imread(self.source_path) self._process(img) elif self.source_type 视频: cap cv2.VideoCapture(self.source_path) while self._running and cap.isOpened(): ret, frame cap.read() if not ret: break self._process(frame) cv2.waitKey(1) cap.release() elif self.source_type 摄像头: cap cv2.VideoCapture(0) # 0 为默认摄像头 while self._running and cap.isOpened(): ret, frame cap.read() if not ret: break self._process(frame) cap.release() def _process(self, img): results self.model(img, size640) det results.xyxy[0].cpu().numpy() self.frame_ready.emit({image: img, boxes: det})DetectThread在run里循环读帧把每帧图像交给模型推理再把结果通过frame_ready信号发出去。主线程里连接这个信号做界面刷新这样无论推理多慢界面都能保持响应。“停止检测”按钮调用thread.stop()循环会在下一帧退出。模型统一在加载按钮的槽函数里初始化不要在DetectThread里重复加载否则每开一次视频就重新读一次权重。4.3 在label上绘制检测结果与猪只状态统计拿到boxes后要在图像上画框、写类别名再把图转成QPixmap显示到QLabel。yolov5返回的det数组每行是x1、y1、x2、y2、置信度、类别id直接cv2.rectangle画上即可。绘制要在主线程里做因为QPainter操作必须在GUI线程。from PyQt5.QtGui import QImage, QPixmap import numpy as np def update_frame(self, result): img result[image] boxes result[boxes] class_names self.model.names # 统计每种行为的数量 state_counter {} for box in boxes: conf float(box[4]) if conf 0.4: continue cls_id int(box[5]) label class_names[cls_id] state_counter[label] state_counter.get(label, 0) 1 x1, y1, x2, y2 [int(v) for v in box[:4]] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f{label} {conf:.2f}, (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) # OpenCV 的 BGR 转 QImage 再转 QPixmap rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, ch rgb_img.shape bytes_per_line ch * w qimg QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888) self.image_label.setPixmap(QPixmap.fromImage(qimg).scaled( self.image_label.size(), aspectRatioMode1)) self.state_table.setText(f站立:{state_counter.get(standing, 0)} f卧姿:{state_counter.get(lying, 0)} f争斗:{state_counter.get(fighting, 0)})这里做了两个关键操作置信度阈值过滤和类别计数。阈值0.4是我在猪只场景里常用的中位数如果误检多就提0.5漏检多就降0.3现场调试时把阈值暴露成一个可以改的成员变量比每次改代码重新打包方便太多。OpenCV读进来的是BGRQImage显示要转成RGB和一个通道对齐的bytes_per_line这两处错了画面会发蓝或者出现条纹属于最常见的显示争议问题。4.4 打包成exe常用的路径坑与规避场内机器通常没有Python环境最后一步是用PyInstaller打包。打包本身不复杂麻烦的是路径。PyInstaller打包后程序运行在临时解压目录所有相对路径都会失效尤其是模型权重和yolov5仓库里的依赖文件。import sys import os def resource_path(relative_path): if getattr(sys, frozen, False): base_path os.path.dirname(sys.executable) else: base_path os.path.dirname(__file__) return os.path.join(base_path, relative_path) weights_path resource_path(weights/best.pt)frozen是PyInstaller打包后注入的标识用它区分开发态和打包态权重文件放在exe同级的weights目录下。torch.hub.load在打包场景下几乎必挂因为它要按仓库结构加载模块。稳妥做法是放弃hub模式改用yolov5源码里的attempt_load。把yolov5仓库整个目录放在程序同级打包时用--add-data把仓库和权重一起带进去代码里用sys.path.insert把yolov5目录加进模块搜索路径再from models.experimental import attempt_load加载。这一步解决了exe才能在没有Python、没有外网的猪场办公电脑上双击运行。5. 猪只行为检测落地排查手册权重失效的5个常见坑到了现场你会发现训练时看指标一片大好部署后问题一个个往外冒。以下5个坑我基本都踩过每条按“现象-原因-解决”的顺序写照着排查能少走很多弯路。5.1 现象白天F1接近0.9夜视画面一头猪都框不出来这是养殖场最常见的翻车。猪舍晚上开的是红外补光灯监控画面是黑白带噪点的灰度图而训练集全是白天可见光的彩色图。模型在彩图上学会了“棕色皮肤粉色耳朵”这类颜色特征一到红外图颜色特征全部失效检测框就像消失了一样。原因本质是训练集和部署场景的域不一致。解决思路是少量补充红外样本不用多从夜视录像里抽100到150帧按白天的标注流程重新标注混进训练集一起训练。还有一种做法是在数据增强里把图像随机转灰度、调低亮度和对比度模拟夜视效果但效果不如真实红外样本直接。5.2 现象单头猪检测框又准又稳一换到十几头的大栏全漏检有的项目初期图方便标注的全是单体特写或两三头猪的近距离画面训练出来的模型对大视野栏舍图完全不适配。大栏里目标只有三四十像素高跟训练样本里几百像素的猪相差一个数量级小目标检测能力自然归零。原因是训练集尺度分布过于单一。解决分两步一是采集时主动加入中远景画面让目标尺度覆盖30到300像素的区间二是训练时开启多尺度训练命令里加--multi-scale每个batch随机缩放输入尺寸相当于告诉模型“猪可大可小”。如果这两步做完还不行把输入分辨率从640提到960或1280小目标特征会更明显。5.3 现象点“开始检测”后界面卡死拖动窗口都拖不动视频流检测时DetectThread每推理完一帧就emit一次信号主线程槽函数里要画框、统计、更新表格。当推理速度超过界面刷新能力时信号队列越积越多主线程忙不过来表现就是程序像死了一样。原因有两个一是推理没在子线程跑二是信号合并没做。解决确认模型加载和推理都在DetectThread内主线程只做绘制在信号连接时用Qt.QueuedConnection并在emit前判断上一次槽函数是否已处理完没处理完就丢弃当前帧。视频流做检测不需要每帧都显示掉几帧完全感觉不到但界面卡死会让人直接关掉程序。5.4 现象同一份best.pt在开发机正常换到现场工控机上结果差很多这里要分清是环境差异还是代码差异。开发机上有NVIDIA显卡推理走CUDA现场工控机多半只有核显或纯CPU。yolov5的模型在FP16半精度下跑GPU速度很快CPU不支持FP16自动降级到FP32结果本身不应该差。真正的原因通常是代码里写死了device0现场机器没有显卡attempt_load传入devicecuda:0会直接报错或者反过来用了devicecpu的权重在GPU上跑。解决是在加载权重时动态判断torch.cuda.is_available()为真才用cuda:0否则用cpu。另外现场机器如果内存只有8G推理时不要一次把整个视频帧做成batch一次只处理一帧避免内存峰值把进程杀掉。5.5 现象争斗和跛行类别的F1明显低于其他类别confusion_matrix里互相混这个坑基本可以预判1000张数据里争斗可能只有80张跛行更少可能30张。yolov5训练时按图片数量加权少样本类别学不到足够稳定的特征表现就是两个类别在特征空间里重叠。解决有两个方向。一是数据层面单独为少样本类别补拍或从历史监控里找回放片段把争斗扩到150张以上跛行扩到80张以上宁可总数变成1300张也值得。二是损失函数层面在hyp文件里加大少样本类的类别权重比较麻烦更简单的做法是给cls_loss的系数调高一些让分类损失对focal难样本更敏感。这里提醒一句先把数据补齐再谈调参数据不到位的调参都是玄学指标涨了也解释不清。6. 进阶技巧导出ONNX跑实时视频流按行为状态自动生成报表训练和桌面界面都稳定之后下一个需求往往是“能不能不用开电脑直接对接监控流每天给我一张报表”。onnx导出和状态统计就是这最后一公里的两个技巧。先把best.pt导出成onnx。yolov5官方仓库自带export.pypython export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --opset 12 \ --dynamic加--dynamic是为了让输入尺寸可变方便处理不同分辨率的摄像头画面。如果你的现场只有固定分辨率不加dynamic导出的模型推理速度更快、更稳。导出后用onnxruntime加载几乎不需要改推理逻辑import onnxruntime as ort import cv2 import numpy as np session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name img cv2.imread(sample.jpg) # 用训练时相同的letterbox预处理尺寸640 # 这里直接复用第2章思路resizepad到640再归一化到0-1 blob np.transpose(img.astype(np.float32) / 255.0, (2, 0, 1))[None] outputs session.run(None, {input_name: blob})onnx模型推理出的原始输出还要做NMS解码yolov5的onnx输出已经包含了坐标和置信度但要做非极大值抑制。建议直接复用yolov5源码里的非极大值抑制函数别再自己写浪费半天还不一定对。导出onnx的意义在于它脱离开了PyTorch环境现场机器只要装个onnxruntime就能跑CPU速度还比原版PyTorch推理快一些如果想下沉到rk3568这类边缘盒子上跑量化模型onnx这一步也是必经之路。状态统计报表是养殖场真正愿意为软件付费的功能。在视频流里对每一帧的检测结果按类别计数每小时或者每天汇总一次输出csv时间段、站立分钟数、卧姿分钟数、采食次数、争斗告警次数。这个功能不需要追踪每一头猪的身份用整栏的宏观统计就能发现异常比如某栏半天采食次数为0说明这批猪可能生病或饲料适口性出了问题。report {} def update_report(cls_id, label): key time.strftime(%Y-%m-%d %H) if key not in report: report[key] {standing: 0, eating: 0, fighting: 0} report[key][label] 1 # 每小时结束把report写入csv并清空当前小时计数报表按小时累加画框线程只负责检测统计线程单独维护字典避免锁竞争。csv字段要和前面classes.txt的类别名保持一字不差否则后面做数据分析时对不上。我自己在这个项目上最深的教训是第一次做数据切分时随手random.shuffle验证集里混进了同一头猪mAP高达0.92现场一测只有0.7后来按日期整段重切才把真实水平测出来。数据划分、类别顺序、路径读取这些基础环节宁可多花一小时确认也别赌运气。希望帮到你。本文还有配套的精品资源点击获取