ARTICLE DETAIL

资讯详情

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

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现 这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测PyQt5 负责提供可视化的桌面交互界面二者通过 Python 生态整合最终产出的是一个可以独立运行的桌面应用程序。相比单纯跑一段检测脚本这种带图形界面的系统更适合农业科研场景中的样本统计、田间调查记录也非常适合作为毕业设计、课程项目或者工业视觉的参考原型。这篇文章会重点覆盖这几个部分YOLOv8 模型如何准备和训练、PyQt5 桌面界面如何搭建、检测结果如何在界面上实时展示、摄像头/图片/视频三种输入模式怎么切换、界面与推理线程怎么解耦避免卡顿以及最终如何打包成可分发的一键运行程序。如果你正在做类似的检测识别系统这篇文章可以直接参考整体架构和关键代码。1. 核心能力速览能力项说明项目类型桌面端目标检测识别系统检测算法YOLOv8Ultralytics YOLOv8界面框架PyQt5Python 5检测目标麦穗、稻穗可通过数据集扩展输入方式本地图片、视频文件、摄像头实时画面输出内容检测框、类别标签、置信度、目标数量统计启动方式Python 脚本启动 / PyInstaller 打包为 exeGPU 支持支持 CUDA 加速也支持 CPU 推理显存需求需按模型版本和输入分辨率测试YOLOv8n 在 CPU 上也能运行API 接口非本项目核心但 YOLOv8 检测逻辑可独立封装批量任务可扩展界面逻辑和推理逻辑分离后可接入批量目录处理适合场景农业科研统计、田间调查、教学演示、毕业设计、算法原型验证从项目定位来看这个系统并不追求在算法层面做出创新而是把 YOLOv8 的检测能力和 PyQt5 的桌面交互能力打通。它解决的核心问题是让没有命令行操作习惯的使用者也能通过鼠标点击完成目标检测和结果统计。2. 适用场景与使用边界2.1 适合谁用农业科研人员、植保相关专业的学生、需要做作物生长情况统计的研究团队是这个系统最直接的受众。比如在田间拍摄一批麦穗图片过去靠人工数穗数费时费力现在可以通过模型自动标注每个麦穗的位置并输出数量统计。对于计算机视觉方向的学生来说这个项目的工程价值更明显。它演示了如何把 YOLOv8 模型封装成可交互的桌面应用这套架构可以迁移到其他检测任务中比如果实计数、工厂零件检测、安防目标识别等。2.2 能解决什么问题检测识别系统要解决的并不是“识别得有多准”这一个问题而是“从拿到图像到得到结果”这个完整链路的问题。具体包括图片预览和检测结果叠加显示检测框坐标、类别、置信度的读取和保存单张图片中目标数量的自动统计视频流和摄像头画面中的连续目标检测检测结果的导出方便后续数据分析2.3 不适合什么场景实时性要求特别高的场景不适合直接用 PyQt5 的简单方案界面渲染本身会占用一定资源嵌入式设备部署也不是这个项目的强项更适合用 YOLOv8 导出 ONNX 或 TensorRT 后在 RK3588、Jetson 等边缘设备上单独部署大规模并行推理服务场景则建议把检测逻辑独立成 API 服务而不是依赖桌面界面。2.4 版权与授权边界使用 YOLOv8 开源框架时需要注意 Ultralytics 的许可证约定。训练数据如果是自己采集的田间图片需要确保拍摄对象不涉及他人隐私或保密区域。如果使用公开数据集则要遵循数据集的授权协议。系统如果涉及商业交付需要确认模型权重、数据集和代码部分的许可兼容性。3. 环境准备与前置条件3.1 硬件环境YOLOv8 官方提供多种尺寸的模型n、s、m、l、x。其中 YOLOv8n 最轻量CPU 也能推理YOLOv8s 和 YOLOv8m 在普通消费级显卡上有比较好的精度和速度平衡。如果手头只有核显或者无独显设备优先选 YOLOv8n。如果使用 NVIDIA 显卡并希望通过 CUDA 加速需要提前装好显卡驱动和 CUDA 工具包。从常见部署经验看YOLOv8n 在 CPU 上处理单张 640x640 图片通常需要几百毫秒到 1 秒左右GPU 上可以到几十毫秒甚至更快。显存方面轻量模型在推理时占用量不大但实际数字需要结合模型版本、批量大小和输入分辨率测试确认。3.2 软件环境建议使用 Python 3.8 到 3.11 之间的版本PyQt5 和 Ultralytics 在这几个版本下兼容性比较好。核心依赖包括ultralytics PyQt5 opencv-python torch torchvision numpy Pillow其中torch和torchvision的安装方式取决于是否使用 GPU。CPU 版本可以直接安装默认的 PyPI 包GPU 版本需要到 PyTorch 官网根据 CUDA 版本选择对应安装命令。3.3 环境检查依赖安装完成后建议先跑一个简单的 YOLOv8 推理验证环境是否正常from ultralytics import YOLO model YOLO(yolov8n.pt) results model(test.jpg) results[0].show()如果界面弹出了检测结果窗口说明核心依赖已经可用。接下来就可以进入项目代码编写阶段。4. YOLOv8 麦穗稻穗检测模型准备4.1 数据集标注与目录结构训练自定义检测模型的第一步是准备数据集。麦穗稻穗检测属于单类别或双类别目标检测任务建议每类目标准备 1000 张以上的标注图片。图片可以从田间多角度、多光照条件拍摄也可以从公开农业数据集补充。推荐使用 LabelImg 或 X-AnyLabeling 进行标注标注格式选择 YOLO 格式即每张图片对应一个同名的.txt文件文件中每行内容为类别id x_center y_center width height其中坐标值均为归一化后的数值。数据集目录结构如下dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容示例path: dataset train: images/train val: images/val nc: 2 names: [wheat_head, rice_head]4.2 模型训练在项目根目录执行训练命令。以 YOLOv8n 为例yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16batch 大小需要根据显存调整。显存较小的设备可以设置batch8或batch4同时开启cacheTrue可以加快数据加载。训练完成后最佳权重保存在runs/detect/train/weights/best.pt。如果是 GTX 1660 Ti 这类 6G 显存的显卡YOLOv8n 或者 YOLOv8s 配合 640 输入分辨率是可以正常训练的YOLOv8m 以上需要把 batch 调小或者降低输入尺寸。如果训练时遇到CUDA out of memory优先减 batch 而不是降分辨率因为分辨率对模型精度影响更大。4.3 推理测试训练完成后可以用 best.pt 做一次推理测试from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model(test_image.jpg, conf0.25) results[0].show()确认模型在验证集图片上能够正确框出麦穗稻穗保存该模型文件路径后面会用到 PyQt5 界面中。5. PyQt5 界面整体设计5.1 功能模块拆分PyQt5 界面部分建议拆成几个独立的模块避免把所有逻辑都堆在main.py里主窗口模块负责整体布局、菜单栏、按钮事件绑定推理线程模块负责调用 YOLOv8 模型进行检测避免阻塞 UI绘图模块负责在 QLabel 或 QGraphicsView 上绘制检测框工具模块负责文件选择、结果保存、数量统计5.2 主界面布局推荐使用左侧控制区 右侧显示区的布局结构。左侧放置图片/视频/摄像头切换按钮、置信度阈值滑块、检测按钮和结果统计表格右侧使用 QStackedWidget 切换静态图片显示和视频流显示。from PyQt5.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QSlider, QTableWidget, QFileDialog ) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(麦穗稻穗检测识别系统 - YOLOv8 PyQt5) self.resize(1200, 800) self.init_ui() def init_ui(self): central_widget QWidget() self.setCentralWidget(central_widget) main_layout QHBoxLayout(central_widget) # 左侧控制面板 control_panel QWidget() control_layout QVBoxLayout(control_panel) self.btn_image QPushButton(选择图片) self.btn_video QPushButton(选择视频) self.btn_camera QPushButton(打开摄像头) self.btn_detect QPushButton(执行检测) self.btn_save QPushButton(保存结果) control_layout.addWidget(self.btn_image) control_layout.addWidget(self.btn_video) control_layout.addWidget(self.btn_camera) control_layout.addWidget(self.btn_detect) control_layout.addWidget(self.btn_save) # 右侧显示区域 display_panel QWidget() display_layout QVBoxLayout(display_panel) self.image_label QLabel(图像显示区域) self.image_label.setMinimumSize(800, 600) display_layout.addWidget(self.image_label) main_layout.addWidget(control_panel) main_layout.addWidget(display_panel)5.3 推理线程实现PyQt5 的界面线程必须保持响应耗时的目标检测不能直接写在按钮的槽函数里否则界面会卡死。推荐使用QThread或QRunnable QThreadPool来做异步推理。import cv2 import numpy as np from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO class DetectThread(QThread): result_ready pyqtSignal(np.ndarray, list, int) def __init__(self, model_path, image_path, conf_thres0.25): super().__init__() self.model YOLO(model_path) self.image_path image_path self.conf_thres conf_thres def run(self): image cv2.imread(self.image_path) if image is None: return results self.model(image, confself.conf_thres)[0] boxes results.boxes.xyxy.cpu().numpy() classes results.boxes.cls.cpu().numpy() confs results.boxes.conf.cpu().numpy() names results.names detections [] for box, cls, conf in zip(boxes, classes, confs): detections.append({ bbox: [int(v) for v in box], class: names[int(cls)], confidence: float(conf) }) annotated_frame results.plot() annotated_frame cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB) self.result_ready.emit(annotated_frame, detections, len(detections))关键点是results.plot()已经帮我们把检测框、类别标签和置信度画到了图像上省去了手动用 OpenCV 绘制的步骤。如果希望统一界面风格也可以自己遍历 detections 并用cv2.rectangle绘制。5.4 主窗口与线程交互选择图片后将该路径传给 DetectThread并在result_ready信号中接收检测结果def on_detect_clicked(self): if not hasattr(self, current_image_path): return self.detect_thread DetectThread( model_pathruns/detect/train/weights/best.pt, image_pathself.current_image_path, conf_thresself.conf_slider.value() / 100.0 ) self.detect_thread.result_ready.connect(self.on_result) self.detect_thread.start() def on_result(self, annotated_frame, detections, count): height, width, channel annotated_frame.shape bytes_per_line channel * width q_image QImage(annotated_frame.data, width, height, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(q_image) self.image_label.setPixmap(pixmap.scaled( self.image_label.size(), Qt.KeepAspectRatio)) self.update_table(detections) self.label_count.setText(f目标数量: {count})通过 QThread 做异步推理后点击检测按钮会立即触发线程界面不卡顿。图片较大时QImage 转换后要记得调用setPixmap前先释放之前的 pixmap 引用避免内存增长。6. 三种输入模式与实时检测实现6.1 图片检测模式图片检测是最简单也最稳定的模式直接读取本地图片文件推理结束后把结果展示在界面上。建议在检测结果下方同时展示原始图片和标注图片的切换按钮方便对比。6.2 视频文件检测模式视频检测的处理逻辑与图片不同需要逐帧读取、逐帧检测。为了保持流畅度不需要每帧都送入模型可以设置跳帧策略比如每 2 帧检测一次。视频检测放到独立的 QThread 中通过 OpenCV 的VideoCapture读取视频帧推理后将结果转换为 QImage 发送到界面线程。class VideoDetectThread(QThread): frame_ready pyqtSignal(np.ndarray) def __init__(self, model_path, video_path, conf_thres0.25): super().__init__() self.model YOLO(model_path) self.video_path video_path self.conf_thres conf_thres self.is_running True def run(self): cap cv2.VideoCapture(self.video_path) frame_count 0 while self.is_running and cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 if frame_count % 2 ! 0: continue results self.model(frame, confself.conf_thres)[0] annotated_frame results.plot() self.frame_ready.emit(annotated_frame) cap.release()视频检测中的核心问题是线程退出机制必须在窗口关闭事件中设置is_running False并调用wait()等待线程结束否则会出现程序无法退出或崩溃的问题。6.3 摄像头实时检测模式摄像头模式实现逻辑与视频文件相似只需要把VideoCapture的参数从文件路径改为摄像头索引cap cv2.VideoCapture(0) # 0 表示默认摄像头摄像头采集的帧率一般是 30fps 左右但 YOLOv8 的推理速度通常达不到每秒 30 帧因此必须做跳帧处理。另外在低成本设备上建议把摄像头输入分辨率调低例cap.set(cv2.CAP_PROP_FRAME_WIDTH, 960) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 540)降低输入分辨率可以显著减少 YOLOv8 的推理耗时。做农忙季节田间移动端部署时这比换大模型更实用。7. 检测结果统计与保存7.1 结果统计表PyQt5 中可以使用QTableWidget展示检测结果的详细数据def update_table(self, detections): self.table.setRowCount(len(detections)) for row, det in enumerate(detections): bbox det[bbox] self.table.setItem(row, 0, QTableWidgetItem(det[class])) self.table.setItem(row, 1, QTableWidgetItem(f{det[confidence]:.2f})) self.table.setItem(row, 2, QTableWidgetItem(f({bbox[0]}, {bbox[1]}))) self.table.setItem(row, 3, QTableWidgetItem(f({bbox[2]}, {bbox[3]})))结果表可以实时记录每个检测框的坐标和置信度。对于麦穗计数场景直接看表格中的行数即可。如果希望进一步做统计分析可以将表格数据导出为 CSVdef save_results(self): path, _ QFileDialog.getSaveFileName( self, 保存检测结果, , CSV Files (*.csv)) if not path: return with open(path, w, encodingutf-8) as f: f.write(class,confidence,x1,y1,x2,y2\n) for row in range(self.table.rowCount()): items [ self.table.item(row, col).text() for col in range(self.table.columnCount()) ] f.write(,.join(items) \n)CSV 导出是农业科研场景里很实用的功能后续可以直接导入 Excel 或 SPSS 做产量统计、密度分析。8. 性能优化与资源占用观察8.1 CPU vs GPU 推理差异YOLOv8 在 CPU 上做推理完全可行但速度受模型尺寸影响很大。YOLOv8n 在 CPU 上速度尚可YOLOv8s 开始就比较吃力了YOLOv8m 及以上基本建议走 GPU。如果只有 CPU 平台可以尝试将模型导出为 OpenVINO 格式来加速yolo export modelbest.pt formatopenvinoOpenVINO 格式的模型在英特尔 CPU 上通常能获得 1.5 到 3 倍的加速效果。8.2 显存占用观察方法训练时用nvidia-smi命令实时查看显存占用nvidia-smi -l 1推理阶段显存占用通常远低于训练阶段。常见 6G 显卡在 YOLOv8n 推理时可能只占 1G 到 2G 左右但这因输入分辨率和批次大小而异具体数字需要在本机实测。8.3 降低资源占用的有效手段输入分辨率从 1280 降到 640显存和耗时都会明显下降使用halfTrue开启 FP16 推理减小批量大小使用模型导出后的 int8 量化版本关闭不用的后台进程视频检测跳帧策略也是降低整体资源消耗的有效手段田间监控视频场景下每 5 帧检测一次也可以接受。9. 完整项目结构参考wheat_rice_detector/ ├── main.py # 程序入口 ├── ui/ │ ├── main_window.py # 主窗口 │ └── styles.py # 样式表 ├── core/ │ ├── detector.py # YOLOv8 封装 │ └── threads.py # 推理线程 ├── models/ │ └── best.pt # 训练好的模型权重 ├── dataset/ │ ├── images/ │ ├── labels/ │ └── data.yaml ├── outputs/ # 检测结果输出目录 └── requirements.txt # 依赖列表detector.py中把 YOLOv8 的加载和推理逻辑单独封装from ultralytics import YOLO class Detector: def __init__(self, model_pathyolov8n.pt): self.model YOLO(model_path) def predict(self, image, conf_thres0.25): results self.model(image, confconf_thres)[0] return results这样主界面代码只需要创建 Detector 实例并调用predict方法不需要关心 YOLO 内部细节。线程、界面和模型逻辑解耦后后续如果要接入新的检测算法如 YOLOv9、YOLO11只需要修改Detector类的内部实现即可。10. PyInstaller 打包发布如果要把系统交付给非开发人员需要打包成 exe 文件。PyInstaller 打包 PyQt5 Ultralytics 项目有几个注意事项。10.1 安装 PyInstallerpip install pyinstaller10.2 生成 spec 文件PyInstaller 打包时需要把模型文件和资源文件作为附加数据加入pyinstaller --name WheatRiceDetector --onefile --windowed ^ --add-data models/best.pt;models ^ --hidden-import ultralytics ^ main.py如果是在 Linux 或 macOS 环境路径分隔符要换成冒号pyinstaller --name WheatRiceDetector --onefile --windowed \ --add-data models/best.pt:models \ --hidden-import ultralytics \ main.py10.3 打包后常见问题使用--onefile模式打包的 exe 启动时会先解压到临时目录启动速度会变慢而且容易被杀毒软件误报。如果对体积和启动速度有要求可以使用--onedir模式也能避免一些误报问题。--windowed会隐藏控制台窗口但检测过程中的 print 信息也就看不到了。如果需要在程序中打印日志建议用 logging 同时输出到文件和弹窗不要依赖控制台。打包后 exe 运行在另一台电脑上如果对方电脑没有显卡驱动或者没有安装 CUDAtorch 在导入时可能报错。稳妥的方案是打包纯 CPU 版本的 torch虽然推理速度慢一些但兼容性最好。11. 常见问题与排查方法问题现象可能原因排查方式解决方案界面启动后立即闪退PyQt5 依赖缺失在 IDE 中运行 main.py 查看报错安装 PyQt5 及对应缺少的依赖模型文件加载失败best.pt 路径错误或文件损坏检查路径是否存在尝试重新加载使用绝对路径或修正路径重新训练/下载权重检测按钮点击无反应线程内异常或模型推理出错在 DetectThread.run 中加 try-except 打印异常检查图像是否为中文路径确认模型与图像尺寸是否兼容CUDA out of memory显存不足查看nvidia-smi确认显存状态减小 batch、降低 imgsz、换轻量模型视频检测画面卡顿推理耗时超过帧间隔打印每帧推理耗时增加跳帧数、降低输入分辨率、使用更小的模型摄像头检测黑屏摄像头索引错误或设备被占用换cv2.VideoCapture(1)测试关闭占用摄像头的其他程序检查设备管理器打包后 exe 缺少模型--add-data 未正确配置检查打包时配置重新打包确保模型路径已加入 data 目录PyQt5 下拉框闪退信号连接方式或对象生命周期问题检查槽函数中对象引用为控件设置父对象避免 Python 垃圾回收提前释放对象中文图片路径下视频读不出来OpenCV 无法直接读取中文路径用os.path.exists检查路径使用cv2.imdecode或先重命名文件为英文路径用 QThread 做目标检测时一个非常隐蔽的坑是在按钮的槽函数中创建了线程对象但槽函数执行结束后线程对象被 Python 垃圾回收结果就是线程还没结束对象先没了信号发不出来。解决方法是把线程对象保存为 self 的成员变量并在窗口关闭事件中主动等线程结束。12. 最佳实践与工程化建议12.1 从训练到部署的完整链路先小规模训练验证数据标注质量跑通流程后再扩数据集。第一次训练建议只跑 50 轮观察 loss 曲线是否下降如果 loss 不降优先检查数据标注是否有大量错标漏标。12.2 模型选择策略麦穗稻穗目标在田间图片中往往密集排列且存在遮挡这属于小目标检测场景。如果 YOLOv8n 精度不够不要急着上 YOLOv8x先尝试以下手段提高训练输入分辨率到 960开启数据增强参数如hsv_h、hsv_s、degrees使用 YOLOv8m 并用更大的 batch 训练在颈部引入小目标检测头尝试 YOLO11 或 YOLOv5u 等替代模型12.3 目录与命名规范数据集、模型权重、输出结果分开存放。输出目录建议按日期建子目录避免历史检测结果被覆盖。模型的训练权重和推理权重分开保存防止误用中途保存的 checkpoint。outputs/ ├── 20250101/ │ ├── image1_annotated.jpg │ ├── image1_results.csv │ └── video1_detected.mp4 └── 20250102/12.4 线程安全与异常处理推理线程内部涉及模型调用、OpenCV 图像处理、numpy 数组操作这些代码在异常时会导致线程静默退出。建议在线程运行入口处加全局异常捕获。def run(self): try: # 省略原有逻辑 pass except Exception as e: self.error_occurred.emit(str(e))界面端在收到错误信号后弹窗提示用户而不是等线程无声消亡后让用户面对一个“无响应”的按钮。13. 总结与下一步这个项目的核心链路其实很清晰YOLOv8 提供检测能力PyQt5 包装出桌面界面两者独立维护、通过信号槽机制通信。相比单独跑 YOLOv8 命令行推理这套系统让非技术人员也能上手操作这是它最大的价值。如果你正在做类似的系统建议先复现图片检测这一条链路跑通后再加入视频和摄像头模式。训练模型用官方yolo命令即可重点精力花在 PyQt5 界面和线程设计上。最容易踩的两个坑一个是 QThread 对象的生命周期一个是打包时模型文件的挂载这两点解决掉系统基本就能稳定运行。下一步可以考虑的方向包括接入 RTSP 网络摄像头实现田间远程监测把检测结果同步到 Excel 或数据库在界面中加入数据分布直方图实时展示不同地块的麦穗密度对比或者把模型封装成本地 API 服务通过 HTTP 接口对接到 Web 端或小程序端。架构层面这套 YOLOv8 PyQt5 的组合并不局限于麦穗稻穗检测替换训练数据集后完全可以扩展到其他农作物计数、果园果实检测或者工业零件识别场景。
返回列表