拓冰建站拓冰建站
首页 / 资讯中心 / 正文

YOLOv8鸡蛋目标检测:从数据集标注到PyQt界面部署全流程

简介面向目标检测初学者与工程师提供一套基于YOLOv8的鸡蛋检测完整方案包含已划分train/val/test的鸡蛋数据集和data.yaml配置可直接用于yolov5、yolov7、yolov8等算法训练并附带PyQt图形界面方便实时预览检测结果。压缩包共1023个文件、84.18MB涵盖jpg图像、xml标注、Python脚本、yaml配置文件、pt模型权重、ui界面及qrc资源等目录结构清晰便于按需取用。此外内附训练日志、results.csv曲线、验证预测图等过程文件可帮助理解训练状态、对比检测效果并快速复现实验。该资源已有186人学习下载适合需要从零搭建鸡蛋检测项目、或希望复用现成数据集与界面模板的开发者。1. 鸡蛋目标检测从数据到界面的一次完整落地鸡蛋目标检测这个标题听起来像是一个顺手做的练手工程实际上它把深度学习落地的三件套——模型、数据、界面——全部串在了一起。你在鸡舍传送带前架一个摄像头yolov8 负责在画面里框出每一枚鸡蛋标注好的数据集让模型分得清蛋托上的、散落的、被遮挡的鸡蛋PyQt 界面则把推理结果变成人看得懂的计数、框选和视频流。这套组合常见于蛋品分拣、智慧养殖和农业信息化演示也常被拿来当深度学习课程设计。适合两类人一类是已经能跑通 yolov8 官方训练流程、但在“怎么把模型接进桌面应用”上卡壳的人另一类是手头有真实鸡舍或蛋托物料、想快速验证视觉方案可靠性的工程人员。整个链路拆开看并不复杂数据标注一天、训练一小时、界面半天难的只是每个环节里那些不写在官方文档里的参数和边界条件。2. 数据集怎么造鸡蛋标注、格式转换与增强2.1 数据从哪来自采是首选公开数据质量难控鸡蛋检测目前没有像 COCO 那样统一的大规模公开数据集网络上下载到的所谓“鸡蛋目标检测数据集”要么是论文附带的私有小样本要么是其他农畜产品的错误归类用之前还得逐个核对类别和标注格式时间成本不低。实际项目里绝大多数可用样本来自现场摄像头或手机拍摄再按帧截取。我一般会建议按场景分类拍摄蛋托俯拍、传送带侧拍、地面散落拍每类至少收集 300 张。拍摄时故意覆盖三种难样本——光照偏暗的、鸡蛋互相遮挡的、蛋托表面强反光的。这样训练出来的模型切到真实环境时才不会因为拍照设备和光线条件不同出现严重的域偏差。如果实在没有现场素材退而求其次是去图像搜索引擎按“egg tray top view”之类关键词找无版权图片但整理和清洗成本几乎等于自己拍一遍。2.2 用 Labelimg 完成 YOLO 格式标注标注工具最常见的是 Labelimg轻量、免配置支持直接导出 YOLO 格式。安装和启动只有两条命令pip install labelimg labelimg打开后先把左上角格式切换到 YOLO类别名只写一行egg。标注时有两个高频错误要避开一是把蛋壳外阴影也框进去阴影在灰度上和蛋壳接近会让模型学到错误轮廓二是把两颗紧挨的鸡蛋框进同一个框这会直接污染回归分支的训练目标。保存后每个图片会生成一个同名 txt 文件每行内容格式为类别id 中心点x 中心点y 宽 高四个坐标均为 0 到 1 的归一化值。2.3 训练集与验证集划分脚本YOLO 的目录结构是约定死的训练前必须把图片和标签按下列方式排好egg_datasets/ images/ train/ val/ labels/ train/ val/划分脚本可以直接抄重点在固定随机种子和保留原始素材import os, random, shutil src rE:/egg_raw # 原始图片和同名txt的存放目录 train_img rE:/egg_datasets/images/train train_lab rE:/egg_datasets/labels/train val_img rE:/egg_datasets/images/val val_lab rE:/egg_datasets/labels/val imgs [f for f in os.listdir(src) if f.endswith((.jpg, .png))] random.seed(42) random.shuffle(imgs) split int(len(imgs) * 0.8) for f in imgs[:split]: shutil.copy(os.path.join(src, f), os.path.join(train_img, f)) shutil.copy(os.path.join(src, f[:-4] .txt), os.path.join(train_lab, f[:-4] .txt)) for f in imgs[split:]: shutil.copy(os.path.join(src, f), os.path.join(val_img, f)) shutil.copy(os.path.join(src, f[:-4] .txt), os.path.join(val_lab, f[:-4] .txt))脚本按 8:2 划分训练集和验证集random.seed(42)保证每次执行结果一致标签文件与图片文件名必须保持相同前缀。这里用copy而不是move原素材要留底标注发现错误时能快速回炉。2.4 数据增强参数坐标框交给模型不用手写鸡蛋检测场景里单独的增强代码其实不必要ultralytics 训练时内置的增强已经覆盖了主要扰动。真正影响鸡蛋这类圆形小目标的参数集中在颜色扰动和几何翻转上下面是我在蛋品项目里常用的一组配置写在训练命令里即可参数值作用hsv_h0.015色调轻微扰动应对不同批次蛋壳色差hsv_s0.7饱和度扰动适应冷白灯和暖光灯hsv_v0.4亮度扰动模拟遮光和过曝degrees15小角度旋转传送带上的蛋姿态变化fliplr0.5水平翻转默认开启即可mosaic1.0四图拼接提升小目标检测能力mixup0.2图像混合降低对背景纹理的过拟合这些参数不需要手工写增强逻辑ultralytics 在训练时自动读取。要注意的是像 mosaic 和 mixup 这类增强会让画面中出现拼接边界如果鸡蛋目标本身很小建议把 mosaic 关掉或降为 0.5否则模型可能学到拼接缝。3. yolov8 环境、模型结构与训练参数一锅端3.1 先看 yolov8 在做什么C2f 与 anchor-freeyolov8 的 backbone 用 C2f 结构替代了 yolov5 里的 C3。C2f 把梯度流拆分得更细让浅层网络也能拿到足够的梯度信息训练更稳定推理速度几乎没有损失。检测头则彻底去掉了 anchor改成 anchor-free直接在特征图上预测目标中心点和框的上下左右距离。对鸡蛋这种尺寸相对固定、形状接近圆形的目标anchor-free 省去了聚类 anchor 的步骤训练流程更省事。网络结构图在官方仓库里就有训练时真正要关心的只有两个点输入分辨率imgsz和最终输出的置信度阈值。前者决定显存占用后者决定界面里漏检和误检的平衡点。3.2 环境配置与 GPU 需求判断环境安装很简单CPU 版本这样就行pip install ultralyticsyolov8 训练需要用到 GPU 吗这个问题要看数据量和模型规格。鸡蛋数据集如果只有两三百张yolov8n 在 CPU 上也能跑但一个 epoch 可能要几分钟。只要数据集上千张或者想训练 yolov8m 以上规格就必须上 GPU。GTX 1660 Ti 这类 6G 显存的卡跑 yolov8n 和 yolov8s 没问题batch 开到 16 就能吃满想上 yolov8m 就得把 batch 降到 8否则会直接 OOM。yolo detect train dataegg.yaml modelyolov8n.pt epochs200 imgsz640 batch16 device0这条命令里data指向数据集配置文件model用官方预训练权重做迁移学习初始点epochs200对于千张级别的数据是安全范围batch16在 6G 显存下刚好够用device0指定第一块 GPU。训练过程中每完成一个 epochultralytics 会自动评估验证集保留 mAP 最高的权重为best.pt。3.3 egg.yaml 配置文件写法egg.yaml是训练入口的核心配置文件内容如下path: E:/egg_datasets # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 1 # 类别数量 names: [egg] # 类别名称路径这里有个坑path字段建议写绝对路径相对路径在换机器训练时极易因为当前工作目录不一致报dataset not found。nc和names必须与标注时保持一致如果 Labelimg 里导出的是egg这里写成Egg也会导致标签索引对不上训练出的模型几乎无法收敛。3.4 训练日志与损失函数曲线训练完成后结果保存在runs/detect/train/目录下里面有两个关键文件weights/best.pt和weights/last.pt还有一张results.png。results.png是三行两列的曲线图按行分别是box_loss、cls_loss、dfl_loss的 train 和 val 曲线这就是 yolov8 画损失函数曲线图的方式。看这张图只做两个判断一val 曲线是否在 150 epoch 后仍在明显下降如果是说明欠拟合把 epoch 加到 300二train 和 val 的 box_loss 是否拉开明显距离如果 val 掉头向上说明过拟合优先减少 batch 或加weight_decay。对鸡蛋检测来说cls_loss通常很低因为只有一个类别分类任务本身太简单真正要盯的是box_loss。参数推荐值说明epochs200千张级别数据够用batch166G 显存上限imgsz640鸡蛋小目标默认 640可试 480 提速patience30val 指标连续 30 epoch 不提升就早停lr00.01迁移学习时保持默认optimizerSGD数据量小时比 Adam 更稳4. 训练完怎么算数评估指标与调优方向4.1 从 mAP50 和 loss 曲线判断模型状态验证集评估命令yolo detect val dataegg.yaml modelruns/detect/train/weights/best.pt终端会输出Precision、Recall、mAP50、mAP50-95四项核心指标。鸡蛋检测场景里mAP50比mAP50-95更有参考意义因为鸡蛋的检测算的是“框住就算对”不需要像素级精确。一个合格的鸡蛋模型mAP50 应该到 0.95 以上如果只有 0.8 左右说明有约两成鸡蛋漏检或误检直接上界面毫无意义。runs/detect/val/下的confusion_matrix.png也要看一眼。单类别情况下矩阵只有两行重点关注对角线上真正例的比例。如果假阴性偏高说明漏检集中在某个特定角度或光照条件。4.2 漏检与误检的四个调整方向egg 目标检测调优方向比参数重要。漏检多优先动数据而不是动网络。把验证集里预测失败的样本导出人工看是哪类场景再去补拍 50 到 100 张该场景图片加入训练集重训这是成本最低、收益最高的一步。如果误检多也就是把蛋托边缘、地面阴影识别成鸡蛋处理方向是加“负样本”。在数据集中加入不含鸡蛋的纯背景图片标注文件留空即可模型会学到“这里没有目标”的负反馈。第三种方向是调置信度阈值。训练结束后默认阈值是 0.25在生产界面里我一般会调到 0.5 以上。鸡蛋的形状太规则模型很容易给出高置信度阈值提高后误检会明显下降漏检增加量很小。最后才是动网络结构。mAP50 卡在 0.9 左右上不去时再考虑往 C2f 后面加注意力模块这类 yolov8 改进技巧。这个方向耗时最长效果还依赖调参属于其他手段用尽后的备选项。4.3 n/s/m/l/x 选型权衡yolov8 有五个规格鸡蛋场景的选型策略很明确项目演示用yolov8n追求精度用yolov8s极少数要求极致精度的离线检测才上yolov8m。m 以上的规格在 6G 显存上训练困难推理帧率也会掉到 15 FPS 以下对于传送带上的实时检测并不划算。规格体积推荐帧率适用场景yolov8n约 6 MB60 FPS 以上实时演示、嵌入式部署yolov8s约 22 MB30 FPS 左右精度优先的实时检测yolov8m约 50 MB15 FPS 左右离线图片批处理5. 用 PyQt 把 yolov8 模型包成桌面检测工具5.1 界面组件与交互逻辑PyQt 界面不负责训练只负责两件事喂图给模型、展示结果。搞清楚这个边界后界面结构就很简单了。主窗口放一个 QLabel 显示检测后的画面一个 QTextBrowser 显示鸡蛋计数两个 QPushButton 分别触发“打开图片”和“打开摄像头”。控件与功能的对应关系控件功能QLabel实时显示检测结果帧QTextBrowser显示当前画面鸡蛋数量QPushButton(打开图片)弹出文件选择框读取本地图片QPushButton(打开摄像头)开启 USB 摄像头采集依赖安装pip install PyQt5 opencv-python5.2 QThread 里做推理别卡住主线程PyQt 最经典的坑是把model.predict()直接写进按钮的槽函数里。predict 在 CPU 上一次推理可能要几百毫秒这会阻塞 Qt 的事件循环界面会直接“假死”。正确做法是单独开一个 QThread 线程主线程只负责接收结果信号。import sys import cv2 from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget, QTextBrowser, QFileDialog from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import Qt, QThread, pyqtSignal from ultralytics import YOLO class DetectThread(QThread): result_ready pyqtSignal(object, int) def __init__(self): super().__init__() self.frame None self.model None self._running True def run(self): while self._running: if self.frame is None: self.msleep(30) continue results self.model.predict(self.frame, imgsz640, conf0.5) annotated results[0].plot() count len(results[0].boxes) self.result_ready.emit(annotated, count) self.frame None class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(鸡蛋目标检测) self.label QLabel() self.label.setAlignment(Qt.AlignCenter) self.count_browser QTextBrowser() self.btn_img QPushButton(打开图片) self.btn_cam QPushButton(打开摄像头) layout QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.count_browser) layout.addWidget(self.btn_img) layout.addWidget(self.btn_cam) container QWidget() container.setLayout(layout) self.setCentralWidget(container) self.model YOLO(best.pt) self.thread DetectThread() self.thread.model self.model self.thread.result_ready.connect(self.update_result) self.thread.start() self.btn_img.clicked.connect(self.open_image) self.btn_cam.clicked.connect(self.open_camera) def update_result(self, frame, count): rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, c rgb.shape img QImage(rgb.data, w, h, c * w, QImage.Format_RGB888).copy() self.label.setPixmap(QPixmap.fromImage(img)) self.count_browser.setText(f当前画面鸡蛋数{count}) def open_image(self): path, _ QFileDialog.getOpenFileName(self, 选择图片, , 图片 (*.jpg *.png)) if path: self.thread.frame cv2.imread(path) def open_camera(self): self.cap cv2.VideoCapture(0) self.timer QTimer() self.timer.timeout.connect(self.read_camera) self.timer.start(33) def read_camera(self): ret, frame self.cap.read() if ret: self.thread.frame frame app QApplication(sys.argv) win MainWindow() win.show() sys.exit(app.exec_())这段代码里最关键的设计是DetectThread主线程把待检测帧写入self.frame推理线程取走后处理完通过result_ready信号把结果帧和数量回传。QImage转换时一定要加.copy()否则rgb.data在帧被回收后会出现悬垂指针界面显示乱码。5.3 计数不要直接累加每帧结果摄像头模式下鸡蛋在画面里会停留好几帧如果每帧都计数并累加数字会虚高。运动鸡蛋经过摄像头只识别一次、完成一次计数正确做法是在界面层维护一个去重逻辑记录上一帧所有检测框的中心点当前帧检测框中心点与上一帧距离小于设定阈值比如 30 像素的视为同一颗蛋不重复计数。这个逻辑放在update_result里做不需要改模型。6. 导出 ONNX 轻量化部署与两个容易踩的细节6.1 用 ONNX Runtime 替代 ultralytics 推理训练好的best.pt直接放进 PyQt 工程虽然能用但每次启动都要加载全套 ultralytics 依赖内存占用高启动慢。常见做法是把模型导出为 ONNX 格式再用onnxruntime推理帧率能提升 20% 以上。yolo export modelbest.pt formatonnx opset12导出后用 onnxruntime 接进界面代码量反而更少import onnxruntime as ort import cv2 import numpy as np session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name推理时把图像预处理为 640x640 的归一化张量模型输出解析出坐标和置信度后直接用 OpenCV 画框。6.2 两个容易被忽略的细节第一个是光照预处理。鸡舍和蛋托场景里塑料蛋托的反光会让蛋壳表面出现高光区域模型对高光区域的置信度会明显下降。推理前对亮度通道做一次 CLAHE 自适应直方图均衡能显著改善这类漏检。第二个是重叠遮挡下的阈值调整。鸡蛋密集重叠时模型输出框的 IoU 往往很高默认 NMS 阈值 0.45 会把部分真实鸡蛋合并掉。界面推理时把conf0.5和iou0.5两个参数拆开调优先提高conf到 0.6而不是动iou。这两个参数直接写在model.predict()里改完不用重新训练。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门