YOLOv11花卉图像分割实战:从数据集划分到PyQt摄像头实时识别
简介本资源面向深度学习与计算机视觉方向的开发者、学生及研究人员提供一套基于Python与PyTorch的花卉图像分割识别完整项目核心采用YOLOv11算法实现目标检测与图像分割并集成PyQt界面支持摄像头实时识别。压缩包共726个文件以jpg花卉图像、txt标注与说明、json配置、pt权重、yaml模型参数及py脚本为主整体约42.15MB目录结构清晰便于按数据集、训练与界面模块检索。项目按01划分数据集、02模型训练、03PyQt界面识别三个脚本组织覆盖从数据预处理、模型训练到实时推理的完整流程并附requirement.txt环境依赖清单。已有208人学习适合希望掌握YOLOv11分割实战、理解深度学习项目落地流程的读者参考也可作为课程设计或科研原型的基础模板。1. 花卉图像分割识别从数据集到 PyQt 界面的完整落地路径花卉识别这件事听起来像是植物学爱好者的玩具但真正做过的人都知道它是个典型的「小目标 类间差异小 背景干扰大」的视觉难题。同一朵花在不同光照下颜色能差出两个色号不同品种的花瓣纹理又极其相似单纯靠分类网络很容易翻车。这个资源包给出的方案是用 YOLOv11 做图像分割把每一朵花从背景里抠出来再分类同时配了一个 PyQt 界面接摄像头做实时识别。整套流程拆成了三个脚本划分数据集、训练、界面推理环境用 PyTorch pip 管理。适合谁如果你已经跑通过 YOLO 的检测任务想往分割方向走一步或者需要一个带界面的演示项目来验证摄像头场景下的分割效果这份代码能省掉你搭框架的时间。但如果你连 PyTorch 环境都没装过建议先把环境配置那关过了再往下看。2. 环境搭建与数据集划分01划分数据集.py 的实操细节2.1 依赖安装与目录结构确认拿到压缩包之后第一件事不是急着跑代码而是把目录结构看清楚。资源包里除了三个核心脚本还有requirement.txt、训练过程产生的results.csv、events.out.tfevents日志文件以及一批可视化输出图比如val_batch0_pred.jpg、val_batch0_labels.jpg、labels.jpg、labels_correlogram.jpg、train_batch0.jpg、train_batch1.jpg。这些图不是装饰它们是你判断数据集质量和训练是否正常的依据。环境安装走 pip 就行requirement.txt里列了 PyTorch、Ultralytics、OpenCV、PyQt5 这些核心依赖。我一般会先建一个干净的虚拟环境避免和系统里的其他包打架# 创建虚拟环境Python 版本建议 3.9 以上 python -m venv flower_seg_env # 激活环境Windows flower_seg_env\Scripts\activate # 激活环境Linux/macOS source flower_seg_env/bin/activate # 安装依赖建议加国内镜像源加速 pip install -r requirement.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里有个参数值得注意requirement.txt里如果写的是torch2.0.0这种宽松版本约束实际装的时候可能会拉到最新版而 YOLOv11 对 PyTorch 版本有一定要求。稳妥做法是先装torch和torchvision的指定版本再装ultralytics。如果安装过程中报CUDA out of memory或者torch not compiled with CUDA enabled说明你装的是 CPU 版或者显卡驱动不匹配需要去 PyTorch 官网查对应 CUDA 版本的安装命令。2.2 数据集划分脚本的逻辑与参数调整01划分数据集.py这个脚本干的事情不复杂把原始花卉图片和对应的标注文件按比例分成训练集、验证集有时候还有测试集。但这里有几个坑我逐个说。先看脚本里常见的划分逻辑import os import random import shutil # 原始数据集路径 raw_images_dir datasets/raw/images raw_labels_dir datasets/raw/labels # 划分后的输出路径 output_base datasets/flower_seg train_ratio 0.8 # 训练集比例 val_ratio 0.2 # 验证集比例 # 获取所有图片文件名不含扩展名 image_files [f for f in os.listdir(raw_images_dir) if f.endswith((.jpg, .png))] random.shuffle(image_files) # 随机打乱避免按顺序划分导致分布偏差 # 计算划分点 split_idx int(len(image_files) * train_ratio) train_files image_files[:split_idx] val_files image_files[split_idx:] # 创建目录并复制文件 for phase, files in [(train, train_files), (val, val_files)]: img_out os.path.join(output_base, images, phase) lbl_out os.path.join(output_base, labels, phase) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for f in files: # 复制图片 shutil.copy(os.path.join(raw_images_dir, f), os.path.join(img_out, f)) # 复制对应的标注文件注意扩展名要换成 .txt label_name os.path.splitext(f)[0] .txt label_src os.path.join(raw_labels_dir, label_name) if os.path.exists(label_src): shutil.copy(label_src, os.path.join(lbl_out, label_name)) else: print(f警告{f} 没有对应的标注文件)这段代码的逻辑很直白但参数上有几个地方需要根据你的实际情况改。train_ratio和val_ratio加起来必须等于 1如果你还想分测试集就改成 0.7/0.15/0.15 这种。random.shuffle之前最好固定随机种子比如random.seed(42)这样每次划分结果一致方便复现实验。另外标注文件的扩展名要和图片严格对应YOLO 格式的标注是.txt每行是类别 x_center y_center width height坐标是归一化后的值。如果标注文件缺失脚本会打印警告这时候你要么补标注要么把对应的图片删掉否则训练时 Ultralytics 会直接报错。还有一个容易被忽略的点花卉数据集的类别数。如果你用的是公开花卉数据集类别可能从几种到上百种不等。划分完之后建议手动检查一下每个类别在训练集和验证集里的分布是否均衡。如果某个类别只在验证集里出现训练集里一张都没有那这个类别在验证时必然全错。常见做法是分层抽样但01划分数据集.py这种简单随机划分不一定能保证所以划分完最好用脚本统计一下各类别数量。提示划分数据集之前先把原始图片和标注文件备份一份。我见过有人跑完划分脚本发现原始文件被覆盖了后悔药都没得吃。3. YOLOv11 分割模型训练02train.py 的参数配置与训练监控3.1 模型选型与训练脚本核心参数YOLOv11 的分割模型有多个尺寸版本从yolo11n-seg到yolo11x-seg参数量和精度依次递增。02train.py里通常会指定一个预训练权重比如yolo11n-seg.pt或yolo11s-seg.pt。选哪个如果你的显卡显存小于 8GB老老实实从n或s开始显存 12GB 以上可以尝试m或l。花卉分割这个任务类别数不多的话s版本通常够用训练速度也快。训练脚本的核心代码大概长这样from ultralytics import YOLO # 加载预训练的分割模型 model YOLO(yolo11s-seg.pt) # 开始训练 results model.train( datadatasets/flower_seg/data.yaml, # 数据集配置文件 epochs100, # 训练轮数 imgsz640, # 输入图像尺寸 batch16, # 批次大小 device0, # 使用 GPU 0CPU 则填 cpu workers4, # 数据加载线程数 optimizerAdamW, # 优化器 lr00.001, # 初始学习率 patience20, # 早停耐心值 saveTrue, # 保存检查点 projectruns/flower_seg, # 输出目录 nameexp1 # 实验名称 )data.yaml是数据集配置文件里面要写清楚train、val的路径nc是类别数names是类别名称列表。这个文件如果路径写错训练直接启动不了。imgsz640是 YOLO 系列的默认输入尺寸如果你的花卉图片分辨率很高可以调到 1280但显存占用会翻倍。batch16也要根据显存调整显存不够就降到 8 或 4同时把workers也降一降不然数据加载进程会卡死。patience20这个参数值得说一下。它的意思是如果验证集指标连续 20 轮没有提升就自动停止训练。花卉数据集如果标注质量一般模型可能在前几十轮就达到瓶颈早停能帮你省时间。但如果你发现模型还在缓慢提升就被停了可以把patience调大比如 50。3.2 训练过程监控与结果文件解读训练启动之后runs/flower_seg/exp1目录下会生成一堆文件。results.csv记录了每一轮的损失值和指标你可以用 pandas 读出来画曲线import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/flower_seg/exp1/results.csv) df.columns df.columns.str.strip() # 列名可能有空格先清理 # 画训练和验证的 box loss plt.figure(figsize(10, 5)) plt.plot(df[epoch], df[train/box_loss], labeltrain box loss) plt.plot(df[epoch], df[val/box_loss], labelval box loss) plt.xlabel(Epoch) plt.ylabel(Loss) plt.legend() plt.savefig(loss_curve.png)events.out.tfevents文件是 TensorBoard 的日志如果你装了 TensorBoard可以用tensorboard --logdir runs/flower_seg启动可视化面板看损失曲线和指标变化更直观。训练过程中还会生成一批可视化图片比如train_batch0.jpg、train_batch1.jpg展示的是训练时的数据增强效果val_batch0_pred.jpg和val_batch0_labels.jpg分别是验证集的预测结果和真实标注对比。我一般会重点看val_batch0_pred.jpg如果预测的掩码和真实标注偏差很大说明模型还没学好要么继续训练要么检查标注质量。labels.jpg和labels_correlogram.jpg是数据集标签的分布图前者展示每个类别的边界框分布后者展示类别之间的相关性。如果某个类别的样本数特别少labels.jpg里会很明显这时候要考虑数据增强或者补充样本。注意训练结束后最好的模型权重保存在runs/flower_seg/exp1/weights/best.pt最后一个 epoch 的权重是last.pt。推理和界面调用都用best.pt别用错了。4. PyQt 界面与摄像头实时识别03pyqt.py 的集成与调试4.1 界面布局与摄像头线程03pyqt.py这个脚本把训练好的模型和 PyQt 界面串起来核心功能是打开摄像头、实时显示画面、对每一帧做分割推理、把结果叠加显示。PyQt 的界面布局通常包含一个QLabel用来显示视频帧几个按钮控制摄像头开关和推理启停可能还有一个文本框显示识别结果。摄像头读取和推理不能放在主线程里否则界面会卡死。常见做法是用QThread开一个子线程专门处理视频帧from PyQt5.QtCore import QThread, pyqtSignal import cv2 from ultralytics import YOLO class CameraThread(QThread): frame_ready pyqtSignal(object) # 信号把处理后的帧传给主线程 def __init__(self, model_path): super().__init__() self.model YOLO(model_path) self.running True def run(self): cap cv2.VideoCapture(0) # 0 表示默认摄像头 while self.running: ret, frame cap.read() if not ret: continue # 推理conf 是置信度阈值iou 是 NMS 的 IoU 阈值 results self.model(frame, conf0.5, iou0.45, verboseFalse) # 把分割结果画在帧上 annotated_frame results[0].plot() self.frame_ready.emit(annotated_frame) cap.release() def stop(self): self.running False self.wait()conf0.5是置信度阈值低于这个值的检测框会被过滤掉。花卉识别场景下如果发现漏检比较多可以降到 0.3如果误检太多就提到 0.6 或 0.7。iou0.45是 NMS 的阈值控制重叠框的合并程度一般不用改。verboseFalse是为了不在控制台刷推理日志不然帧率一高终端会被刷屏。4.2 界面与推理的联调要点主线程里接收frame_ready信号把帧转成QImage再显示到QLabel上。这里有个格式转换的坑OpenCV 读出来的是 BGR 格式PyQt 显示需要 RGB所以要用cv2.cvtColor转一下。另外帧的尺寸如果和QLabel大小不匹配要么缩放帧要么缩放QLabel不然显示会变形。from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QLabel, QVBoxLayout, QWidget, QPushButton class MainWindow(QWidget): def __init__(self): super().__init__() self.label QLabel(摄像头未开启) self.btn_start QPushButton(开启摄像头) self.btn_stop QPushButton(停止) layout QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.btn_start) layout.addWidget(self.btn_stop) self.setLayout(layout) self.thread None self.btn_start.clicked.connect(self.start_camera) self.btn_stop.clicked.connect(self.stop_camera) def start_camera(self): self.thread CameraThread(runs/flower_seg/exp1/weights/best.pt) self.thread.frame_ready.connect(self.update_frame) self.thread.start() def update_frame(self, frame): # BGR 转 RGB rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w q_img QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(q_img).scaled( self.label.width(), self.label.height(), aspectRatioMode1 # 保持宽高比 )) def stop_camera(self): if self.thread: self.thread.stop() self.thread None联调的时候如果界面能打开但视频画面是黑的先检查摄像头索引对不对有些笔记本内置摄像头是 0外接 USB 摄像头可能是 1 或 2。如果画面卡顿严重把推理的imgsz降下来比如从 640 降到 320或者跳帧推理每两帧处理一次。如果识别结果框的位置偏移检查一下results[0].plot()返回的帧和原始帧尺寸是否一致。5. 避坑与常见问题排查5.1 训练 loss 不下降或震荡严重现象results.csv里train/box_loss和val/box_loss在前几十轮几乎不变或者上下剧烈震荡。原因最常见的是学习率设大了。lr00.001对 AdamW 来说不算大但如果你的数据集很小比如几百张图这个学习率可能还是偏高。另一个原因是标注文件格式不对比如坐标没有归一化或者类别索引从 1 开始而不是 0。YOLO 的类别索引必须从 0 开始从 1 开始会导致所有标签错位。解决先把lr0降到 0.0001 试试同时加cos_lrTrue让学习率按余弦退火衰减。然后抽查几个标注文件确认每行的第一个数字是 0 到nc-1之间的整数后面四个数是 0 到 1 之间的小数。如果标注有问题用脚本批量修正。5.2 验证集掩码预测全黑或全白现象val_batch0_pred.jpg里预测的掩码要么全是黑色要么全是白色没有分割出花朵轮廓。原因全黑通常是模型还没学到东西或者data.yaml里的nc和实际类别数不一致。全白可能是conf阈值太低把所有像素都当成目标了。另外如果验证集的图片和训练集分布差异太大比如训练集是室内拍摄验证集是室外自然光模型也会表现很差。解决先确认data.yaml的nc和names与标注文件一致。然后把conf从默认的 0.25 提到 0.5 再试。如果还是不行检查验证集图片是否和训练集来自同一批数据分布差异大的话需要重新划分数据集保证训练集和验证集的拍摄条件接近。5.3 PyQt 界面启动报错缺少 DLL 或插件现象运行03pyqt.py时报ImportError: DLL load failed或者qt.qpa.plugin: Could not load the Qt platform plugin。原因PyQt5 和 OpenCV 的版本不兼容或者系统缺少 Visual C 运行库。Windows 上这个问题尤其常见因为 OpenCV 的某些版本会自带 Qt 插件和 PyQt5 的插件冲突。解决先卸载现有的opencv-python换成opencv-python-headless这个版本不带 GUI 组件不会和 PyQt5 冲突。然后确认requirement.txt里 PyQt5 的版本如果是 5.15 以上一般没问题。如果还报错安装 Visual C Redistributable 最新版。pip uninstall opencv-python pip install opencv-python-headless5.4 摄像头推理帧率过低现象界面能显示画面但帧率只有几帧操作延迟明显。原因模型推理在 CPU 上跑或者imgsz设得太大或者每一帧都在做完整的预处理和后处理。解决确认device0是否生效在CameraThread里打印一下self.model.device如果是cpu说明 GPU 没调用起来。然后把推理的imgsz降到 320或者改用yolo11n-seg.pt这种更小的模型。如果还是慢可以跳帧推理每三帧处理一次中间帧直接显示原始画面。5.5 训练完成后推理结果与训练时指标差距大现象训练时val/seg_loss很低mAP 看起来也不错但用best.pt在摄像头画面上推理效果差很多。原因训练时的验证集和实际摄像头场景差异大。验证集可能是清晰的花卉特写摄像头拍的是带背景的实时画面光照、角度、遮挡都不一样。另外训练时的数据增强可能过强导致模型对真实场景的泛化能力下降。解决在摄像头场景下收集一些图片手动标注后加入训练集做一轮微调。微调时把epochs设小一点比如 20 到 30学习率也调低避免破坏预训练权重。数据增强方面把mosaic和mixup的概率降下来这两个增强对分割任务有时会起反作用。6. 进阶技巧用 TensorBoard 盯训练曲线与模型导出训练跑起来之后光看results.csv的数字不够直观我习惯开 TensorBoard 盯着曲线。events.out.tfevents文件就是干这个用的在训练输出目录的上一级执行tensorboard --logdir runs/flower_seg --port 6006浏览器打开http://localhost:6006能看到损失曲线、学习率变化、mAP 指标。重点看val/seg_loss和metrics/mAP50-95(B)这两条线。如果val/seg_loss开始上升而train/seg_loss还在下降说明过拟合了要么加数据要么加正则化要么早停。训练完之后如果想把模型部署到其他平台可以用 Ultralytics 的导出功能转成 ONNX 或 TensorRTfrom ultralytics import YOLO model YOLO(runs/flower_seg/exp1/weights/best.pt) model.export(formatonnx, imgsz640, halfTrue) # halfTrue 启用 FP16 量化halfTrue会把模型权重转成 FP16推理速度能提升不少精度损失通常在可接受范围内。导出 ONNX 之后可以用onnxruntime在 Python 里加载推理也可以集成到 C 或其他框架里。还有一个我踩过的坑导出 ONNX 时如果imgsz和训练时不一致推理结果会偏移。比如训练用 640导出用 320检测框的位置就对不上。所以导出时的imgsz必须和训练时保持一致或者至少在推理时做相应的缩放。从那以后我每次训练完都强制走一遍导出验证用best.pt和导出的 ONNX 模型分别推理同一张图对比掩码的 IoU低于 0.95 就回去检查导出参数。这个习惯帮我省了好几次返工。希望帮到你。本文还有配套的精品资源点击获取