ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于YOLOv8的西红柿成熟度检测系统:从数据集到PyQt5部署全流程

2026/10/2 2:43:32 拓冰建站 浏览量
基于YOLOv8的西红柿成熟度检测系统:从数据集到PyQt5部署全流程 简介本资源是一套基于YOLOv8深度学习与PyQt5图形界面构建的西红柿成熟度检测系统面向计算机、人工智能、自动化等专业的在校学生、教师及企业开发者也适合作为毕业设计、课程设计或实战演示项目。系统可对西红柿进行成熟与未成熟两类识别兼顾高准确率与开箱即用的使用体验。压缩包共678个文件约193.42MB包含296张jpeg与14张jpg图像、290个txt标注文件、31张png图表、15个Python源码、4个pt权重文件以及yaml配置、ui界面文件、csv评估指标、mp4与avi演示视频等覆盖数据集、模型、界面与文档全流程。目前已有186人学习下载。资源提供训练好的模型、288张标注数据集、评估指标曲线与演示素材并附安装使用教程读者可快速复现检测效果也可在此基础上修改类别或功能用于二次开发与项目展示。1. 西红柿分拣线上的视觉方案这套 YOLOv8 成熟度检测系统到底能干什么做过农产品分拣设备的人都知道西红柿按成熟度分级这件事人工干一天下来眼睛基本就废了。青果、半熟、全红三个档位靠人眼在传送带上盯效率低不说标准还不统一——张三觉得这个算半熟李四觉得已经全红了。我去年帮一个做果蔬分选设备的朋友看产线他们当时用的是传统 HSV 阈值分割光照一变就翻车下午的识别结果和上午能差出两成。这套基于 YOLOv8 的西红柿成熟度检测系统核心思路就是用深度学习目标检测替代手工特征。它把成熟度判断拆成检测问题模型直接输出每个西红柿的边界框和类别成熟度等级而不是先分割再分类。项目自带 PyQt5 图形界面、288 张标注好的数据集、训练好的权重文件、评估指标曲线和演示素材拿到手就能跑推理不用从零配环境。适合两类人一是做农业视觉检测的工程师想快速验证 YOLOv8 在果蔬分级上的效果二是深度学习入门者需要一个完整闭环的项目练手——从数据集格式、训练脚本到 GUI 部署链路是通的。2. YOLOv8 检测成熟度的技术选型为什么不用分类网络2.1 检测 vs 分类成熟度判断的任务本质很多人第一反应是成熟度分级不就是分类吗把西红柿抠出来送进 ResNet 或 EfficientNet 不就行了。这个思路在单果、背景干净的场景下确实能用但实际产线上有三个问题绕不开。第一图像里往往有多个西红柿大小不一、相互遮挡。分类网络要求输入是单个目标裁剪图你得先做检测或分割把每个果子抠出来等于多了一步误差还会累积。第二成熟度不是非黑即白的同一颗果子上可能一半红一半青分类网络给的是整图一个标签检测网络可以框出局部区域分别判断。第三产线要的是「哪个位置、什么等级」检测框直接给了空间信息后续机械臂抓取或气吹分拣不用再做坐标映射。YOLOv8 在这类任务上的优势是单阶段检测推理速度快n/s/m/l/x 五个尺度可选。西红柿分拣线一般要求 30 FPS 以上YOLOv8n 在普通 GPU 上跑 640×640 输入轻松过百帧CPU 上也能到十几帧够用。2.2 数据集标注格式YOLO txt 与 LabelImg 的配合项目自带的 288 张数据集是 YOLO 格式标注每张图对应一个同名 txt 文件内容形如0 0.523 0.412 0.156 0.203 1 0.721 0.635 0.142 0.188每行五个值类别索引、归一化中心 x、归一化中心 y、归一化宽、归一化高。类别索引对应你的成熟度等级比如 0青果、1半熟、2全红。这里有个容易搞混的点YOLO 的坐标是相对于图像宽高的归一化值不是像素值。如果你用 LabelImg 标注导出时选 YOLO 格式它会自动转换如果自己写脚本转记得除以图像宽高。标注时我一般建议框尽量贴紧果实边缘但不要把果蒂、枝叶框进去太多否则模型会学到无关特征。遮挡超过 50% 的果子建议标成困难样本或直接跳过不然训练时 loss 震荡得厉害。2.3 环境搭建Python、PyTorch、Ultralytics 的版本匹配这套系统依赖 Python PyTorch Ultralytics PyQt5。版本不匹配是新手翻车最多的地方。我实测比较稳的组合是组件推荐版本说明Python3.8 ~ 3.103.11 以上部分 PyQt5 轮子不全PyTorch1.13 ~ 2.1跟 CUDA 版本走CPU 版也行Ultralytics8.0.xYOLOv8 官方库别装太老的PyQt55.15.x界面依赖pip 直接装OpenCV4.5图像读写和预处理安装命令按顺序来# 创建虚拟环境避免污染全局 python -m venv venv # Windows 激活 venv\Scripts\activate # Linux/Mac 激活 source venv/bin/activate # 装 PyTorchCPU 版示例有 GPU 去官网选对应 CUDA 命令 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 装 Ultralytics 和界面依赖 pip install ultralytics pyqt5 opencv-python参数说明--index-url指定 PyTorch 官方源国内下载慢可以换清华镜像但注意镜像同步有延迟版本对不上时优先用官方源。ultralytics会自动拉取 numpy、matplotlib 等依赖不用单独装。装完验证一下import torch from ultralytics import YOLO print(torch.__version__) print(torch.cuda.is_available()) # 有 GPU 返回 True model YOLO(yolov8n.pt) # 会自动下载预训练权重 print(model.names)如果torch.cuda.is_available()返回 False 但你确实有 NVIDIA 显卡大概率是 CUDA 版本和 PyTorch 不匹配去 PyTorch 官网重新选命令装。3. 从标注数据到训练权重YOLOv8 训练流程拆解3.1 数据集目录结构与 data.yaml 配置YOLOv8 训练要求固定的目录结构dataset/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 训练集标注 txt │ └── val/ # 验证集标注 txt └── data.yaml # 数据集配置文件288 张图按 8:2 划分训练集约 230 张验证集约 58 张。数据量偏小所以训练时要开数据增强。data.yaml内容path: ./dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 nc: 3 # 类别数按你的成熟度等级改 names: # 类别名顺序要和标注索引一致 0: green 1: half_ripe 2: ripe这里最容易错的是names的顺序。如果你标注时 0 是青果配置里 0 就必须是 green反了的话模型学出来的类别全是错的评估指标看着还行但实际推理结果驴唇不对马嘴。3.2 训练脚本与关键超参设置训练入口就几行from ultralytics import YOLO # 加载预训练模型n 是最小尺度速度快适合产线 model YOLO(yolov8n.pt) # 开始训练 results model.train( datadataset/data.yaml, # 数据集配置 epochs100, # 训练轮数小数据集可以多跑 imgsz640, # 输入尺寸产线一般 640 batch16, # 批大小显存不够就调小 device0, # 0 表示第一块 GPUCPU 写 cpu workers4, # 数据加载线程数 patience20, # 20 轮没提升就早停 lr00.01, # 初始学习率 augmentTrue, # 开启数据增强 mosaic1.0, # mosaic 增强概率 mixup0.1, # mixup 增强概率 projectruns/train, # 输出目录 nametomato # 实验名 )参数逐个说epochs设 100 是因为 288 张图属于小样本模型需要多轮才能收敛但配合patience20早停实际可能 60~80 轮就停了。batch16在 8G 显存上跑 640 输入基本够爆显存就降到 8。mosaic1.0是 YOLOv8 默认开的把四张图拼成一张对小数据集提升明显但训练后期建议关掉不然边界框会有点飘。mixup别开太高0.1 到 0.2 就行太高了图像语义混乱成熟度这种细粒度分类反而学不好。训练过程中看runs/train/tomato/下的results.csv重点关注metrics/mAP50-95和train/box_loss。mAP50-95 能到 0.7 以上说明模型基本可用0.85 以上算不错。如果 loss 一直不降先检查标注文件和图片是否一一对应再检查data.yaml路径。3.3 评估指标曲线怎么读项目自带的评估曲线一般包括results.pngloss 和 mAP 随 epoch 变化、confusion_matrix.png混淆矩阵、PR_curve.png精确率-召回率曲线。重点看两个混淆矩阵里如果 green 和 half_ripe 之间误判多说明这两个等级的特征区分度不够可能是标注时边界模糊或者光照导致颜色偏移。解决办法是补充这两个等级之间的过渡样本或者在标注规范里把半熟的定义写死比如红色面积占比 30%~70%。PR 曲线看的是不同置信度阈值下的表现。产线上如果要求高召回宁可错杀不可放过就把置信度阈值调低比如 0.25如果要求高精确减少误分拣就调到 0.5 以上。这个阈值在推理时通过conf参数控制。4. PyQt5 界面集成把模型塞进桌面应用4.1 界面布局与功能模块划分PyQt5 界面一般分三块左侧是图像/视频显示区右侧是控制面板选择文件、开始检测、置信度滑块、结果显示底部是状态栏。用 Qt Designer 拖控件最快生成.ui文件后用pyuic5转成.pypyuic5 mainwindow.ui -o mainwindow.py转换后的mainwindow.py是界面定义不要直接改逻辑写在单独的app.py里继承它。这样界面调整重新生成不会覆盖业务代码。4.2 推理线程与界面刷新别让主线程卡死新手最容易犯的错是把推理直接写在按钮点击槽函数里。模型加载和推理是耗时操作放主线程界面直接假死用户以为程序崩了。正确做法是开 QThreadfrom PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO import cv2 class DetectThread(QThread): # 信号传回检测后的图像和统计结果 frame_ready pyqtSignal(object, dict) def __init__(self, source, conf0.25): super().__init__() self.source source # 图片路径或视频路径或摄像头索引 self.conf conf self.running True self.model YOLO(runs/train/tomato/weights/best.pt) def run(self): cap cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ret, frame cap.read() if not ret: break # 推理 results self.model(frame, confself.conf, verboseFalse) annotated results[0].plot() # 画框和标签 # 统计各类别数量 counts {} for box in results[0].boxes: cls_name self.model.names[int(box.cls)] counts[cls_name] counts.get(cls_name, 0) 1 self.frame_ready.emit(annotated, counts) cap.release() def stop(self): self.running False self.wait()逻辑说明QThread子类里跑推理循环每处理完一帧通过pyqtSignal把图像和统计结果发回主线程。主线程收到信号后更新 QLabel 显示图像、更新统计标签。self.running标志位用于安全停止线程直接 kill 线程会导致资源泄漏。results[0].plot()是 Ultralytics 自带的绘图方法返回带框和标签的 numpy 数组直接转 QImage 显示。参数说明conf是置信度阈值界面上的滑块可以绑定它实时调整。verboseFalse关掉控制台每帧输出不然刷屏。如果是视频文件循环里加个cv2.waitKey控制播放速度如果是摄像头source传 0。4.3 模型加载与推理加速的几个实操点模型加载放在线程__init__里只加载一次不要每帧都YOLO()。如果界面启动时加载慢可以做个 splash 或者状态提示。推理加速方面imgsz设 640 是精度和速度的平衡点产线如果只检测近景大果可以降到 416 甚至 320速度翻倍但小果漏检率上升。halfTrue开启 FP16 推理GPU 上能快 20%~30%CPU 不支持。device指定 GPU 索引多卡机器别默认用 0 卡可能被其他任务占满。5. 避坑与排查这套系统跑不起来时先看这几条5.1 现象训练 loss 不降或震荡剧烈原因最常见的是标注文件和图片不匹配——图片有 288 张txt 只有 280 个或者文件名对不上比如图片是IMG_001.jpg标注是img_001.txt大小写敏感。其次是data.yaml里nc和names数量不一致或者类别索引越界。还有一种情况是学习率设太大lr00.01对小数据集偏大可以降到 0.001。解决写个脚本遍历 images 和 labels 目录检查每个图片是否有同名 txt打印缺失列表。确认data.yaml里nc等于names的键数量。学习率先用默认的 0.01 跑 10 轮看 loss 趋势不降就降一个数量级。5.2 现象PyQt5 界面报「could not find or load the Qt platform plugin windows」原因PyQt5 和 Python 版本不兼容或者环境里混装了多个 Qt 版本比如同时装了 PyQt5 和 PySide2。Windows 上还可能是缺少 VC 运行库。解决先pip uninstall pyqt5 pyqt5-tools再重装指定版本pip install pyqt55.15.9。检查pip list | grep -i qt看有没有冲突包。Windows 装一下 VC 2015-2022 运行库。如果还不行在代码最前面加import os; os.environ[QT_QPA_PLATFORM_PLUGIN_PATH] 你的pyqt5路径/Qt5/plugins/platforms。5.3 现象推理结果类别全乱或置信度极低原因data.yaml里names顺序和训练时不一致或者推理时加载的权重不是训练好的best.pt而是预训练的yolov8n.pt。还有一种可能是图像预处理不一致——训练时用了归一化推理时没做。解决确认推理脚本里model.names打印出来和训练时一致。权重路径写绝对路径或确认相对路径正确。Ultralytics 的model()接口会自动做预处理不用手动归一化但如果你自己写了预处理再送进去就会双重归一化导致输入分布偏移。5.4 现象视频检测卡顿、界面延迟高原因每帧都调用results[0].plot()绘图这个操作在 CPU 上比较耗时。或者imgsz设太大或者线程里做了多余的图像拷贝。解决绘图可以隔帧做或者用更轻量的画框方式直接cv2.rectangle和cv2.putText。imgsz降到 416。线程里frame直接送模型不要先copy()再送。如果还卡检查是不是在run()里每帧都创建了新的YOLO对象——这是血泪经验有人真这么干过。5.5 现象换自己的数据集后 mAP 很低原因新数据集的类别定义和原项目不一致或者图像分辨率、光照条件和原数据集差异大。288 张原数据集可能覆盖不了你的场景。解决先拿原数据集跑通确认流程没问题再逐步替换。新数据集至少每类 100 张以上且要包含不同光照、不同遮挡程度的样本。如果类别定义变了nc和names必须同步改标注文件里的类别索引也要改。可以用labelme重新标注导出 YOLO 格式。6. 进阶技巧用 TTA 和置信度分层把成熟度误判压下去模型训完只是起点产线上真正头疼的是边界样本——半熟偏青、半熟偏红的果子模型置信度往往在 0.4~0.6 之间晃今天判青明天判红。我一般会加两个后处理TTA测试时增强和置信度分层。TTA 的思路是推理时对同一张图做多种变换水平翻转、多尺度把结果融合。Ultralytics 支持augmentTrue参数results model(frame, conf0.25, augmentTrue, verboseFalse)开启后推理速度会慢 2~3 倍但 mAP 通常能涨 1~3 个点。产线如果帧率有余量建议开如果卡在 30 FPS 边缘就别开用下面的置信度分层。置信度分层的逻辑是高置信度0.7直接判类低置信度0.3~0.7的果子标记为「待定」送人工复核或二次检测。代码大概这样def classify_with_confidence(results, high_th0.7, low_th0.3): final [] for box in results[0].boxes: conf float(box.conf) cls_name results[0].names[int(box.cls)] if conf high_th: final.append((cls_name, auto, conf)) elif conf low_th: final.append((cls_name, review, conf)) else: final.append((unknown, reject, conf)) return final这样产线上自动分拣只处理auto的review的走旁路人工确认reject的直接剔除。实测能把误分拣率从 8% 压到 2% 以内代价是人工复核量增加约 15%。这个 trade-off 看产线怎么定如果人工成本低就阈值设严一点如果追求全自动就放宽。还有一个技巧是类别权重。如果青果和全红样本多、半熟样本少训练时给半熟类更高的cls权重Ultralytics 里可以通过自定义 loss 或者过采样实现。我一般直接在数据集层面做把半熟样本复制一份文件名加后缀让每个 epoch 都能多见到几次。简单粗暴但有效。从那以后我每次训完模型都会先拿一批边界样本跑一遍看置信度分布再决定阈值怎么设。这套系统给的是起点真正落地还得根据自己的产线数据微调。希望帮到你。本文还有配套的精品资源点击获取