
简介一套基于 YOLOv10 的麦穗计数系统项目文档面向熟悉 Python 与深度学习基础、从事农业自动化或计算机视觉研究的科研与工程人员。文档围绕麦穗实时识别与计数场景系统讲解环境搭建、YOLOv10 模型准备、检测代码编写、GUI 界面呈现及结果评估并给出数据集多样性、光照适应性等注意事项帮助读者从零复现完整工作流。整套资料为单个 docx 文档约 46KB结构从项目介绍、实现步骤到完整代码整合循序渐进涵盖环境配置命令、代码整合、数据示例与 ONNX 模型导出命令便于集中查阅与按步骤操作。目前已有 143 人学习下载。通过学习可掌握基于 YOLOv10 的麦穗计数项目设计与优化思路包括迁移学习微调、实时视频流处理、GUI 展示评估指标以及超参数调优和模型轻量化等后续方向适合直接用于农业产量估算相关课题或工程实践。1. 基于 YOLOv10 的麦穗计数系统从数据集到 GUI 的完整实现麦穗计数这件事靠人站在田里数数一天也数不出几亩地而且光照一变、麦穗一重叠误差就大得没法用。这套基于 YOLOv10 的麦穗计数系统就是把目标检测模型和 PyQt5 界面串起来摄像头对着麦田屏幕上实时出数字检测框绿油油地画在每一株麦穗上。它不是玩具 demo包含了完整的程序代码、训练好的模型导出流程和标注数据说明适合有 Python 和深度学习基础、想直接上手农业视觉项目的开发者和科研人员。我拆完这套代码后发现它真正值钱的地方不在模型本身而在「模型怎么落地成 GUI 工具」这一整套衔接逻辑。2. 架构与工作流为什么是 YOLOv10 ONNX PyQt5 这个组合2.1 三个核心组件的分工逻辑这套系统选型很克制每个组件干一件事没有多余的东西。YOLOv10 负责检测这是 You Only Look Once 系列的最新版本在实时性和准确率之间取得了不错的平衡特别适合麦穗这种小目标密集出现的场景。ONNX 负责模型分发把 PyTorch 训练好的模型导出成中间格式脱离原始训练框架运行。PyQt5 负责交互提供开始/停止按钮、计数标签和图像显示区域。为什么不用 TensorFlow 直接推理因为 ONNX Runtime 的推理效率更高部署更干净不需要把整个训练环境带着跑。为什么用 PyQt5 而不是 Flask 做 Web 界面因为农业场景下摄像头通常接在本地机器上桌面应用延迟更低而且 PyQt5 的 QTimer 做视频帧的定时刷新非常顺手。2.2 数据流与核心方法调用链整个系统的工作流可以分为三个阶段。第一阶段是模型准备训练好的 .pt 文件通过torch.onnx.export转换成 ONNX 格式。第二阶段是推理计数摄像头每一帧经过缩放、归一化后送入 ONNX Runtime得到检测框坐标和置信度。第三阶段是结果渲染OpenCV 在原始帧上绘制绿色矩形框PyQt5 把处理后的帧转成 QPixmap 显示在窗口上。关键代码就集中在WheatCountingApp这个类里它的核心方法是update_frame由 QTimer 每 20 毫秒触发一次。这个时间间隔不是随便定的20 毫秒对应 50 FPS 的理论上限给视频帧处理和模型推理留了足够余量。2.3 这套组合的边界在哪里这套架构适合单目摄像头固定视角的麦穗计数——比如架在田边、温室里或者试验田上方。如果你的场景是无人机俯拍大面积麦田或者需要多路视频流并行处理这套代码的架构就需要扩展但作为起步框架它是完全够用的。3. 环境搭建与模型导出先把坑填平再写代码3.1 PyTorch 与 ONNX Runtime 的安装版本匹配环境配置这一步看起来就是几条pip install实际上版本匹配很容易翻车。照着项目里的命令走先装深度学习框架再装图像处理和界面库顺序是 torch → opencv → PyQt5 → onnx 工具链。我一般会建议装完跑一个 10 行的冒烟测试确认 ONNX Runtime 能正常读取模型文件再继续往下写业务代码。pip install torch torchvision torchaudio opencv-python PyQt5 matplotlib onnx onnxruntime这里最容易踩的坑是 PyTorch 版本和 ONNX 导出器不兼容特别是 torch 2.x 之后torch.onnx.export的接口行为有变化。opset_version 建议固定在 11这个版本 ONNX Runtime 兼容性最好太新的话老版本的 onnxruntime 会报 unsupported operator 错误。3.2 从 .pt 到 .onnx导出的三个关键参数模型导出是连接训练和部署的桥梁这一步做不好后面全白搭。项目给出的导出代码用了torch.hub.load加载自定义模型然后构建一个 dummy input 走一遍 forward最后导出。这段代码很标准但有三个参数值得展开讲。import torch # 加载训练好的自定义 YOLOv10 模型 model torch.hub.load(your_channel/yolov10, custom, pathyour_model.pt) model.eval() # 创建与模型输入尺寸匹配的 dummy input dummy_input torch.randn(1, 3, 640, 640) # 导出为 ONNX 格式opset_version 固定为 11 torch.onnx.export(model, dummy_input, yolov10_wheat_counting.onnx, opset_version11)第一个参数是dummy_input的尺寸这里用的是1×3×640×640对应一批一张、RGB 三通道、640×640 分辨率。这个尺寸必须和训练时的输入尺寸一致如果训练时用了 1280这里用 640导出的模型检测精度会明显下降。第二个参数是opset_version它决定了 ONNX 图里算子版本11 的兼容性最好。第三个参数是model.eval()这个必须显式调用否则 BatchNorm 和 Dropout 在导出时还处于训练模式推理结果会是乱的。3.3 导出后必做的验证导出完成后不要急着写 GUI 代码先用一根命令验证 ONNX 模型能正常加载和推理。这里推荐用onnxruntime的 Python API 跑一张纯色图确认输出张量形状符合预期。YOLOv10 的输出通常是一个二维数组每一行是一个检测框包含 x1、y1、x2、y2、conf、cls 六个值。如果你发现输出形状不对或者数值全是 NaN回头检查导出步骤大概率是模型加载路径或者 dummy_input 尺寸的问题。4. 检测系统代码拆解GUI、推理与计数的衔接逻辑4.1 WheatCountingApp 类的初始化流程系统代码的核心是WheatCountingApp这个类它继承自QtWidgets.QWidget。初始化方法里做了三件事创建界面、加载 ONNX 模型、打开摄像头。这个顺序是有讲究的——先界面后模型保证窗口能正常显示模型加载放中间在欢迎界面能看到加载状态摄像头最后打开避免启动时黑屏。class WheatCountingApp(QtWidgets.QWidget): def __init__(self): super().__init__() self.initUI() self.model self.load_model(yolov10_wheat_counting.onnx) self.capture cv2.VideoCapture(0) # 使用默认摄像头这段代码里的cv2.VideoCapture(0)是打开系统默认摄像头。如果接了 USB 摄像头但 index 不是 0画面会黑屏这时候改成 1 或者 2 逐个试。还有一个隐藏问题如果系统里有虚拟摄像头比如 OBS 的虚拟输出也会占用 index导致真实摄像头打不开。4.2 detect_wheat 预处理不是简单的 resizedetect_wheat承担了图像预处理的任务。很多人以为预处理就是cv2.resize实际上 YOLO 系列对输入有自己的要求BGR 到 RGB 的通道转换、归一化到 0-1、CHW 维度排列每一步都不能省。项目代码里这一段处理很规范我原样保留并在注释里标明了每个步骤的用途。def detect_wheat(self, frame): input_size (640, 640) # 缩放图像到模型输入尺寸 blob cv2.resize(frame, input_size) # OpenCV 默认 BGR 通道YOLO 需要 RGB所以必须转换 blob cv2.cvtColor(blob, cv2.COLOR_BGR2RGB) # HWC 转 CHW并归一化到 [0, 1] blob blob.transpose(2, 0, 1).astype(np.float32) / 255.0 # 增加 batch 维度变成 1x3x640x640 blob np.expand_dims(blob, axis0) # 推理 preds self.model.run(None, {images: blob}) return preds[0]这段代码的第一个关键点是COLOR_BGR2RGB。OpenCV 读图像默认是 BGR 排列而 YOLO 模型训练时用的是 RGB不转换的话检测精度会断崖式下降而且很难排查。第二个关键点是归一化/ 255.0把像素值从 0-255 压到 0-1这是模型训练时的标准做法。第三个关键点是np.expand_dims模型需要 batch 维度单张图也要扩成1×3×640×640。这里有一个许多人容易忽略的性能细节cv2.resize是同步阻塞操作对于 640×640 的输入通常在几毫秒内完成但如果摄像头分辨率很高比如 4Kresize 的开销会明显增加。实际部署时可以在摄像头初始化阶段就把分辨率调低比如capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)能省掉大量不必要的缩放计算。4.3 计数逻辑的筛选与更新计数部分用的是列表推导式一行代码完成过滤和统计。置信度阈值 0.5 是一个经验值麦穗检测场景下前景和背景对比度高0.5 就能很好地过滤误检。如果模型在遮挡严重的田块上误检多可以上调到 0.6 或 0.7代价是部分被遮挡的麦穗会被漏掉。def update_count(self, detections): # 过滤置信度大于 0.5 的检测框 unique_wheat len([det for det in detections if det[4] 0.5]) # 更新界面标签 self.countLabel.setText(f当前麦穗计数: {unique_wheat})这段代码的思路很清晰但如果你要处理的是视频流直接对每一帧独立计数会产生严重的问题。同一株麦穗在相邻帧会被重复计数导致数字剧烈跳动。一个简单的优化是维护一个set存储最近 N 帧里已经出现过的检测框位置设定一个重叠阈值只有新位置超过阈值才算新麦穗。另一种思路是每隔 20 帧做一次计数取中间值或平均值牺牲实时性换取稳定性。4.4 实时显示帧率与性能优化QTimer 间隔 20ms 触发一次update_frame这个频率跑在普通台式机上没什么压力。但如果 CPU 较弱或者摄像头分辨率过高会出现画面卡顿和计数延迟。这时优先检查两个地方一是摄像头分辨率是否过高二是convert_to_pixmap里QImage的构造是否高效。def convert_to_pixmap(self, frame): # BGR 转 RGB frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch frame.shape bytes_per_line ch * w # 用 QImage 包装 numpy 数组再转 QPixmap qImg QtGui.QImage(frame.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888) return QtGui.QPixmap.fromImage(qImg)这里用QImage(frame.data, ...)直接包装 numpy 数组的内存避免了数据拷贝。但需要注意frame.data指向的是 OpenCV 内部缓冲区如果后续对frame做原地修改显示中的图像也会跟着变所以这段代码要在所有绘制操作完成后再调用。5. 避坑与常见问题麦穗计数系统开发中的五个典型踩坑记录5.1 摄像头黑屏但程序没有报错现象程序能启动点击开始计数后窗口显示全黑终端没有任何异常输出。原因cv2.VideoCapture(0)使用的摄像头 index 不对或者是笔记本自带摄像头但系统装了多个摄像头驱动默认打开的并不是你想要的那个。解决在代码里加一段枚举逻辑或者先在命令行跑python -c import cv2; cap cv2.VideoCapture(0); ret, frame cap.read(); print(ret, frame.shape)如果返回 False把 index 改成 1、2 逐个试。还有一种情况是摄像头被其他程序占用关掉占用程序再跑。5.2 检测框坐标画在错误位置现象麦穗能被检测出来但绿色框的位置和实际目标错位比如框偏左上角。原因YOLO 输出的是相对坐标0-1 之间的比例值而cv2.rectangle需要的是绝对像素坐标。如果直接拿相对坐标画框画出来的位置自然不对。但项目代码里直接用了int(x1)这种转换实际上这个转换在部分 YOLO 输出格式下会出问题。解决确认模型的输出格式。如果输出是归一化的 xywh需要乘以图像宽高还原成绝对坐标。YOLOv10 的不同导出方式输出格式有差异建议在拿到preds[0]后先打印形状和前几行数值确认是 xyxy 还是 xywh再做对应转换。5.3 计数数字剧烈跳动不稳定现象摄像头固定不动麦穗也没有移动但计数结果每隔几帧就变一次像随机数。原因每一帧独立推理同一株麦穗在不同的帧里被检测的位置和置信度略有差异前一帧置信度 0.52 通过阈值后一帧 0.48 被过滤掉数值就会上下波动。解决在update_count里加入滑窗逻辑取最近 5 帧计数结果的中位数作为显示值。另一个办法是记录每个检测框的中心点维护一个已计数列表新检测框中心点与列表里已有位置的距离小于某个阈值就认为还是同一个麦穗不重复计数。5.4 ONNX Runtime 报错 Exit Code 1现象运行代码时终端输出onnxruntime.capi.onnxruntime_pybind11_state.InvalidProtobuf或类似错误进程直接退出。原因ONNX 模型文件损坏或者导出的 ONNX 模型与当前 onnxruntime 版本不兼容常见于 opset_version 过高。解决重新导出模型把opset_version11固定下来。如果重新导出还不行检查torch.onnx.export里dummy_input的数据类型必须是torch.float32如果是 float64导出的模型在部分 runtime 上会报类型错误。5.5 数据集过少导致误检严重现象在测试图像上表现尚可一到真实田间视频就频繁误检把杂草叶子当成麦穗。原因训练数据太少或者数据只在单一光照条件下采集模型没有见过逆光、黄昏、阴雨等场景。解决扩充数据集至少包含晴天、阴天、逆光三个维度。标注时注意麦穗和其他禾本科植物的区别。如果不想从零标注可以先下载公开的麦穗数据集做预训练再用自己采集的数据微调。6. 从基础版到生产级的进阶优化评估可视化与模型微调6.1 把评估函数真正接入系统项目里有一个evaluate_model函数但注释标明它「未集成计数逻辑」。这个函数的价值在于展示训练过程中 precision 和 recall 随 epoch 的变化趋势用来判断模型是否过拟合。实际使用时你可以在训练完成后加载 metrics 数据调用这个函数把这个 numpy 数组替换成你自己的训练日志数据。def evaluate_model(metrics): plt.figure(figsize(10, 5)) # 绘制精确率曲线 plt.plot(metrics[epochs], metrics[precision], labelPrecision, colorblue) # 绘制召回率曲线 plt.plot(metrics[epochs], metrics[recall], labelRecall, colorgreen) plt.title(模型评估) plt.xlabel(Epochs) plt.ylabel(Scores) plt.legend() plt.show()这段代码有两个可以改进的点。第一个是添加 mAP0.5 曲线和 mAP0.5:0.95 曲线这两个指标比单纯 precision 和 recall 更能反映检测模型的综合能力。第二个是把plt.show()换成plt.savefig(evaluation.png)这样在无显示环境下也能保存评估图也方便归档到实验记录。6.2 模型参数微调与超参数调整方向项目指出的未来改进方向里超参数优化排第一这一点在麦穗检测场景下确实值得优先做。YOLOv10 训练时最影响结果的三个超参数是imgsz输入分辨率、batch批次大小和epochs训练轮数。麦穗是密集小目标训练输入分辨率建议不低于 640如果显存充足可以上 1280对小目标的检测提升非常明显。另外可以试验conf_thres和iou_thres两个后处理参数。项目代码里置信度阈值写死为 0.5如果你的麦穗图像遮挡严重0.5 会导致大量漏检可以降低到 0.3 观察效果。IOU 阈值影响 NMS 的去重力度麦穗密集的情况下两个框重叠度很高阈值太高会把相邻麦穗合并成一个这个也需要根据实际数据反复调。6.3 视频流计数的三个稳定性技巧第一个技巧是固定摄像头曝光参数。麦田环境光照变化剧烈自动曝光会导致相邻帧亮度差异大检测结果不稳定。建议在初始化时关闭自动曝光手动设定一个适中的曝光值。第二个技巧是 ROI 区域裁剪。如果监控画面里存在干扰物体比如人走动、飞鸟只统计画面中间区域的检测框可以显著降低误检。第三个技巧是在update_frame里加一个简单的帧率统计用cv2.getTickCount计算每帧处理耗时如果超过 100 毫秒就需要考虑优化模型或者降低输入分辨率。6.4 从单摄像头到多视角的扩展思路这套系统目前是单摄像头架构。如果要扩展成三点定位或者多视角计数核心改动在于两点一个是在__init__里管理一个摄像头列表而不是单个capture另一个是修改update_frame用线程池并行推理多个摄像头的画面每个线程处理一个摄像头把结果汇总后统一更新 GUI。这种扩展涉及线程安全和 Qt 界面更新规则setPixmap和setText必须在主线程调用否则会出现间歇性崩溃。6.5 一个值得固化的调试习惯我从这套麦穗计数系统里学到的最实用的一件事是在模型输出和 GUI 渲染之间加一个简单的日志开关。开发的时候打开开关把每一帧的检测框数量、坐标和置信度打印到终端观察数值分布联调完成后再关掉避免日志拖慢实时性。从那以后我每次搭建视觉检测系统都会强制在推理层和显示层之间留一个 debug 开关先跑通数值再调界面这套麦穗计数系统的代码结构很适合作为这个习惯的起点希望帮到你。本文还有配套的精品资源点击获取