ARTICLE DETAIL

建站实战干货

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

基于YOLOv5和PyQt的水下垃圾检测系统设计与实现

2026/9/8 8:56:47 拓冰建站 浏览量
基于YOLOv5和PyQt的水下垃圾检测系统设计与实现 简介面向水下垃圾检测与目标识别学习者这份资源围绕YOLOv5提供了从数据准备、模型训练到结果可视化的完整链路。内容包含训练好的水下垃圾检测权重以及PR曲线、loss曲线等训练过程记录可用于观察模型收敛情况与精度变化。配套的VOC格式数据集包含数千张真实海洋场景图片均使用LabelImg逐张标注并提供VOC与YOLO两种标签目录类别覆盖金属、木材、塑料、橡胶、布料等多种水下垃圾场景多样可直接用于继续训练、迁移学习或算法测评也为模型微调提供了可靠基础。压缩包共2000个文件其中1994个为txt标注文件另有3个Python脚本和3个PDF文档整体约278.55MBPDF包含YOLOv3至YOLOv8环境配置教程及PyQt5使用说明Python脚本可辅助完成检测流程调用。目前已有339人学习适合水下视觉方向的学生、科研人员和算法工程师参考借助PyQt可视化界面可直观呈现检测框与类别信息大幅缩短数据标注和调试周期。 做水下垃圾检测这个选题一开始就是冲着“能落地”去的。水下环境复杂光线衰减严重悬浮颗粒多目标尺度小和常规的地面目标检测完全是两套打法。用YOLOv5做检测主干配合PyQt做可视化操作界面再加一套标注好的数据集和训练好的权重整个项目跑下来既能作为本科毕设的完整交付也能作为研究工作的前期验证平台。这套组合最典型的意义在于它把“算法能跑”和“用户能用”之间的空隙填上了。下面我按照从数据到训练再到界面落地的顺序把这个项目的完整思路、踩坑记录和关键实现拆开讲。1. 水下垃圾检测任务拆解为什么是YOLOv5加PyQt的组合先说我为什么选这套技术栈。水下垃圾检测本质上是目标检测任务的一个细分场景任务的核心是解决“在浑水、弱光、低对比度的图像里把塑料、金属、玻璃、渔网等水下废弃物找出来并框住”。在工程上这个任务会拆成三个环节图像输入处理、模型推理、结果展示与交互。YOLOv5在中间这个环节承担推理任务是目前工业界和学术界都用得最顺手的目标检测模型之一。它有几个很实际的优势网络结构不复杂训练资源要求不高单张2080Ti就能跑得动支持导出多种部署格式PyTorch权重可以转成TorchScript、ONNX等后续要用的形式推理速度快在一块入门级显卡上也能跑到几十FPS满足实时检测的需求。对于一个需要演示和交互的项目来说这些特性非常关键。PyQt则负责最外层的可视化界面。选它不是因为框架多先进而是因为它在桌面应用开发上实在太成熟跨平台、控件丰富、和OpenCV的图像格式容易互转。QStackedWidget可以做多页面切换QGraphicsView可以承载实时视频流和标注框的绘制QThread可以解决“界面卡死”这个所有视觉应用都绕不开的问题。数据这块是另外的重点。训练一个可用的水下垃圾检测模型没有真实水下垃圾图像是行不通的。我手上的数据集包含了水下拍摄的常见垃圾类别比如塑料瓶、金属罐、玻璃瓶、渔网和织物类每张图都用LabelImg完成了矩形框标注格式为YOLO的txt文本格式。标注好的数据集和训练好的模型放在一起意味着拿到项目的人不需要从零开始跑标注流程可以直接把精力放在调优和二次开发上。三个环节的配合逻辑是OpenCV接摄像头或读取视频/图像文件把帧交给YOLOv5进行推理再把检测结果类别、置信度、坐标绘制回画面最终全部呈现在PyQt界面里。用户只需要点击开始检测就能看到实时的识别结果也可以通过界面按钮切换模型权重或调整置信度阈值。这个组合看起来常规但每个部分都有自己的讲究下面拆开细讲。2. 数据集才是项目的真正地基采集、标注与质量检查很多人拿到一个检测项目第一反应是上模型调参实际上决定模型上限的是数据集。水下垃圾检测比常规目标检测更依赖数据质量原因很简单水下图像的噪声模式非常特殊颜色通道会漂移目标边缘会被悬浮物干扰如果训练数据不能覆盖这些情况模型在测试时几乎必然现原形。2.1 类别设计不是分得越细越好这份数据集的类别设定为常见的几类水下废弃物包括塑料、金属、玻璃、渔网、橡胶等。类别数量控制在个位数看起来不复杂但这个设定本身就是权衡过的结果。水下图像中很多垃圾在外观上高度相似比如白色塑料袋和白色织物如果训练集里没有足够的样本来区分这两类模型就会陷入混淆。类别越细对标注一致性的要求就越高。同一个塑料瓶有人标成“塑料”有人标成“瓶子”模型学到的特征就会混乱这种不一致对检测精度的影响远超类别数量的多少。所以我建议如果你的数据集是自己建的类别设计阶段一定要写一份标注规范文档定义好每个类别的判定标准尤其是边界情况怎么处理。比如“被水草部分遮挡的塑料瓶还能不能算塑料瓶”“碎裂的玻璃瓶是否标为玻璃”等这类问题在标注过程中一定会反复出现。提前定义清楚后期返工成本可以降低一大半。2.2 标注格式与工具LabelImg足够数据集使用LabelImg标注输出YOLO格式的txt标签文件。YOLO格式的核心是每行一个目标包含类别id、归一化后的中心点x坐标、中心点y坐标、宽度w、高度h。归一化是指所有坐标都除以图像宽高取值在0到1之间。举例来说一张1920x1080的图片里一个目标的左上角坐标是(480, 270)宽高是(240, 270)那么对应的YOLO标签就是class_id 0.375 0.375 0.125 0.25计算过程中心点x(480240/2)/19200.375中心点y(270270/2)/10800.375宽240/19200.125高270/10800.25。LabelImg标注的时候要特别注意一个细节软件默认保存的是Pascal VOC格式的XML文件需要在保存前切换到YOLO格式否则后期还要写脚本转格式。另外标注框应该尽量紧贴目标边缘框线离目标太远或者太近都会影响mAP评估结果。2.3 数据增强让模型见过更多的水水下场景特殊性决定了数据增强策略不能照搬陆地上的通用方案。常规的随机翻转、随机裁剪、色彩抖动都可以用但针对水下图像有几项增强很重要亮度扰动模拟不同深度的光照衰减。对比度扰动模拟浑浊水域的雾化效果。色偏模拟水下图像普遍偏蓝绿色可以在这个方向上做扰动让模型不依赖颜色作为唯一判别依据。Mosaic增强YOLOv5内置的Mosaic策略在训练时会随机取四张图拼接对小目标检测帮助很大。水下垃圾中的塑料碎片、渔网碎片都偏小这个增强基本是刚需。我的实际做法是基础数据集约含5000张图像标注目标约1.2万个经过Mosaic、随机仿射变换、HSV扰动后每次epoch实际看到的数据形态远超原始数量模型对光线变化的鲁棒性明显提高。2.4 一个容易被忽略的步骤数据集划分训练集、验证集、测试集的比例建议控制在8:1:1左右但比比例更重要的是划分方式。如果同一个场景下连续拍摄的多张相似图片被同时分到训练集和测试集模型相当于提前“见过”了测试数据评估结果会虚高这个现象叫数据泄漏。更稳妥的做法是按照采集场景或者视频片段来划分同一次下潜采集的所有图片放同一个集合里而不是随机打乱之后按比例切。这块工作不复杂却很影响结论可信度评审老师或读者只要问到“你的测试集有没有泄漏”答不上来就很尴尬。3. 模型训练从环境配置到收敛的全过程记录3.1 环境配置版本配对是最容易翻车的地方YOLOv5的代码库对PyTorch版本有隐式要求版配不上轻则警告重则训练中途直接内存报错或loss变成NaN。我自己实测比较稳的组合是Python 3.8以上PyTorch 1.12以上、2.0以下如果你不计划用新版导出功能2.0也可以用但注意有些扩展模块的兼容性CUDA 11.3以上建议直接用CUDA 11.7ultralytics的YOLOv5 v6.0或v7.0分支v5.0和v6.0之间的anchors定义逻辑有差别不要混用不同版本的预训练权重一个建议不要在Windows的cmd里裸装环境用Anaconda创建独立虚拟环境不然你会在各种依赖冲突上浪费大量时间。3.2 配置训练参数在yaml文件里做文章数据集准备好之后需要在YOLOv5的data目录下新建一个数据集yaml配置文件指向你的图片目录和标签目录。核心内容包括train: /path/to/dataset/images/train val: /path/to/dataset/images/val nc: 6 names: [plastic, metal, glass, fishing_net, rubber, others]其中nc是类别数必须和标注时的class_id范围对应上否则训练过程中会报索引越界的错误。训练超参数的设置上我个人认为首训尽量用默认参数跑一版搞清楚baseline之后再考虑调参。重点观察两个东西训练集的box_loss、cls_loss有没有稳定下降以及验证集的mAP0.5有没有在合理地往上走。水下垃圾不同于通用物体部分类别之间相似度高第一版mAP0.5能到75%以上就可以接受了。一个常见的调参误区是盲目加大batch size。YOLOv5的默认batch size是16但如果你显存只有8G建议降到8同时适当增加epoch数来补偿。另一个参数是img size训练分辨率默认640x640如果数据集中小目标占比高可以试试在训练时用640推理时用更大分辨率如960或1280做TTA提升效果常常比改模型结构更明显。3.3 训练过程的实际观测记录我在一次完整训练中的关键参数是训练集4800张验证集600张测试集600张img size640batch size8epoch120优化器SGD初始lr0.01。前40个epoch损失下降比较快mAP0.5从0直接升到0.6左右40到80个epoch进入缓升期mAP从0.6涨到0.7180到120个epoch基本是0.71到0.74之间的微小波动说明模型已经近似收敛。整个训练过程耗时约9小时主要取决于你的GPU算力和数据增强的运算开销。如果时间有限可以用YOLOv5s作为主干速度比YOLOv5l快很多精度损失在可接受范围内。对我来说在水下垃圾这个任务上YOLOv5s和YOLOv5l的最终精度差距只有2到3个百分点但推理速度差了一倍。3.4 模型的导出不止是留个pt文件训练完成后YOLOv5会生成best.pt和last.pt。best.pt是验证集上表现最好的一版last.pt是最后一个epoch的结果。正式使用必须加载best.pt不要问为什么问就是last.pt大概率过拟合或者欠拟合。如果项目需要落地成桌面应用建议把模型导出为TorchScript格式这样不依赖原始代码结构加载速度更快。导出命令python export.py --weights runs/train/exp/weights/best.pt --include torchscript --img 640 --batch 14. PyQt可视化界面算法落地的最后一公里模型再准如果只能通过命令行跑对普通用户来说就完全没有可用性。PyQt界面的作用就是把“检测能力”包装成“检测产品”。4.1 界面整体结构设计我希望界面能做到“打开就会用”所以整体分成了几个区域顶部功能按钮区包括打开图片、打开视频、打开摄像头、暂停、退出。中间主显示区基于QLabel或QGraphicsView用于实时显示检测图像。右侧参数设置区包括置信度阈值滑动条、IOU阈值滑动条、模型选择下拉框。底部日志输出区实时显示检测帧数、FPS、检测出的目标列表和数量统计。为了让界面在检测视频流时不卡顿必须把推理过程放到子线程里。QThread是PyQt提供的线程类拥有独立的事件循环我们可以把检测循环写在一个继承QThread的类中用signal向主线程传递检测结果然后由主线程刷新UI。4.2 核心代码检测线程与界面通信线程内部逻辑的核心部分如下class DetectThread(QThread): change_pixmap pyqtSignal(QImage) update_fps pyqtSignal(float) update_result pyqtSignal(dict) def __init__(self, model_path, conf_thres, iou_thres): super().__init__() self.model torch.hub.load(ultralytics/yolov5, custom, pathmodel_path, force_reloadTrue) self.conf_thres conf_thres self.iou_thres iou_thres self.running True def run(self): cap cv2.VideoCapture(self.video_source) while self.running: ret, frame cap.read() if not ret: break t0 time.time() results self.model(frame) plotted results.render()[0] # 返回numpy数组 t1 time.time() fps 1 / (t1 - t0) self.update_fps.emit(fps) self.change_pixmap.emit(self.numpy_to_qimage(plotted)) cap.release()这段代码的核心是用torch.hub加载自定义权重它内部会自动匹配YOLOv5仓库的预处理逻辑省去你自己写letterbox等操作的麻烦。results.render()的作用是把检测框直接画到图像上这是开发调试阶段的效率神器。需要注意的一点torch.hub.load在第一次加载时会连接网络拉取仓库代码如果加载不了可以在本地clone一份YOLOv5仓库然后通过path参数指向本地路径。我在后面的避坑部分会展开讲。4.3 numpy图像转QImage的细节OpenCV读入的图像格式是BGR可是PyQt的QImage默认按RGB解析如果直接用cv2的图像数据转QImage显示出来的画面会蓝红互换。正确做法是先把BGR转成RGBdef numpy_to_qimage(self, img): rgb_image cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w return QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888).copy()最后一个.copy()也很重要。如果不加复制原始的numpy数组在帧循环中被释放后QImage对象会指向无效内存表现是界面偶尔花屏或者闪退排查起来很隐蔽。4.4 阈值滑条和下拉框的交互逻辑置信度阈值滑条的作用是过滤掉置信度低的框。水下图像噪声多模型容易产生假阳性预测调高阈值可以减少误检但也会漏检。界面上把这个作为实时可调参数用户可以根据场景动态调整。滑条的事件绑定逻辑self.slider_conf.valueChanged.connect(self.update_conf_label) def update_conf_label(self, value): self.conf_thres value / 100.0 self.label_conf.setText(f置信度阈值: {self.conf_thres:.2f})模型下拉框则可以绑定多个已训练好的权重文件路径比如不同类别集合的模型、或不同backbone大小的模型。这里建议给用户一个“目录选择后再刷新下拉框”的操作避免把路径写死在代码里。5. 实测效果、推理速度与部署避坑指南5.1 实际检测效果我拿一段约3分钟的水下视频做了测试视频分辨率1920x1080帧率30FPS。界面上的检测表现如下模型YOLOv5s训练120个epochimg size 640平台i7-12700 NVIDIA RTX 3060 12G控制台实测推理耗时约18毫秒每帧去掉视频解码时间后FPS约45大尺寸目标塑料瓶、金属罐、渔网团块的检测稳定连续多帧的类别判断基本一致小目标碎片塑料、小鱼缠绕的细网偶有漏检在调低置信度阈值到0.15之后召回率明显上升但同时出现少量误检从体验上说把置信度阈值默认设置为0.25比较合理——既能抑制一部分水下微小颗粒造成的噪声误检又不至于将大半碎片漏掉。5.2 踩坑1torch.hub加载模型失败这个问题出现频繁。torch.hub.load在加载ultralytics/yolov5时需要访问GitHub拉取代码如果你运行环境的网络无法访问GitHub或者YOLOv5仓库版本被更新可能直接抛错。解决思路是绕过hub。先本地准备好YOLOv5仓库目录然后直接用仓库里的detect模块加载权重import sys sys.path.insert(0, /本地路径/yolov5) from models.experimental import attempt_load model attempt_load(/本地路径/best.pt, map_locationcuda) model.eval()这个方式的好处是加载路径完全本地化不依赖任何在线操作部署到别的机器时只要把权重文件和仓库代码一起拷过去就行。坏处是你要自己处理输入图像的letterbox缩放和NMS后处理代码量会多几行。但相比在线加载的不确定性这个本地化方案更可靠。5.3 踩坑2PyQt界面直接调用模型推理导致卡死在初版界面里我直接把torch的推理写在了主线程中打开摄像头以后画面一卡一卡的滑动阈值条要过一两秒才有反应鼠标在窗口上移动都延迟。原因很明确PyQt的主线程既要处理UI事件循环又要执行GPU推理而推理过程中会阻塞事件循环。解决方案就是把推理放进QThread子线程。def start_detection(self): self.thread DetectThread(self.model_path, self.conf_thres, self.iou_thres) self.thread.change_pixmap.connect(self.update_image) self.thread.update_fps.connect(self.update_fps_label) self.thread.start() def stop_detection(self): self.thread.running False self.thread.wait()注意关闭程序时一定要调用wait()等待线程退出否则程序退出时会报“QThread: Destroyed while thread is still running”的错误。5.4 踩坑3不同分辨率图像下的检测尺度漂移水下拍摄设备的帧率、分辨率差异很大有的录制视频是4K有的摄像头输出只有640x480。YOLOv5训练时的分辨率是640如果你直接把4K的整帧送到模型推理不仅慢还可能因目标相对尺寸过小而漏检。更合理的做法是对帧做等比例缩放将长边缩放到640或768而不是直接resize到正方形。YOLOv5内部已经有letterbox的预处理逻辑使用torch.hub加载或attempt_load后你需要手动把待检测图像先缩放到合适尺寸建议参考YOLOv5官方detect.py中的letterbox实现img letterbox(img, new_shape640, stride32, autoTrue)[0] img img.transpose((2, 0, 1))[::-1] img np.ascontiguousarray(img)经过letterbox处理后推理出的boxes坐标需要按原图信息还原即把letterbox的padding偏移减回去再除以缩放比。这个步骤做错会导致检测框位置全部偏移标的框和实际物体对不上。如果你用results.render()自动绘制它内部会处理坐标映射但如果你要自己绘制或者输出坐标用于后续机械控制就必须手动处理。5.5 标注数据集的复用价值最后说一下这套标注数据集的价值。很多水下垃圾相关的公开数据集存在类别不均衡、场景单一的问题。你手上这份已经标注好的数据集类别覆盖常见的水下废弃物标签格式统一无论在学术论文里做实验对比还是在工程上做模型微调都很有价值。扩展方向上你可以在现有数据基础上加入自己采集的图像用已有模型做预标注即模型先给出粗糙的框人工再修正通过这种方式把新数据集的标注时间压缩一半以上。这是一个很省力的增量标注策略值得用起来。6. 进一步优化方向从“能演示”到“真能用”如果你不想让项目停在演示阶段以下三个方向值得考虑。6.1 部署到边缘设备水下机器人或检测装置的实际工作环境往往没有GPU服务器这时候需要把模型部署到边缘设备比如Jetson Nano、树莓派或基于STM32的嵌入式平台。虽然STM32本身算力极低但可以借助外接NPU模块或协处理器来跑轻量级模型。YOLOv5可以导出为TensorRT引擎在Jetson上或通过ONNX Runtime进行推理精度基本没有损失推理速度在Jetson Nano上使用TensorRT FP16可以达到10FPS左右。这个性能对定点投放的垃圾检测场景基本够用。如果你的目标平台是树莓派5这类设备建议选择YOLOv5n或者YOLOv5s并且把推理分辨率降到320或416这样CPU推理可以接近实时。控制板则通过串口或局域网接收检测结果实现“看到垃圾—上报坐标—触发机械臂或定位标记”的闭环。6.2 增加目标跟踪模块单纯检测只能给出当前帧的目标位置实际清理作业需要知道每个目标的运动轨迹。可以接入ByteTrack或DeepSORT把检测框关联成轨迹。跟踪的结果可以用来统计目标出现时长、运动方向也可以过滤掉因视频抖动造成的闪烁误检。对水下运载器来说跟踪还有一个重要的意义当目标短暂离开视野时能够通过轨迹预测它的位置。ByteTrack比DeepSORT更适合这个场景因为它不依赖ReID特征对类别相似、外观变化大的水下垃圾更鲁棒速度也更快。把检测结果输入ByteTrack只需维护一个卡尔曼滤波的状态列表代码量不大但功能增益很明显。6.3 加入水下图像增强预处理水下图像的通病是偏色和低对比度。可以在界面中增加一个“图像增强”开关在推理前先做灰度世界白平衡或CLAHE对比度增强。实测下来CLAHE对改善低对比度画面中的目标边缘有明显帮助在浑浊水体数据上mAP能提升3到5个点。代价是每帧会增加约几毫秒的CPU时间对整体实时性影响很小。一个更高级的做法是训练一个水下图像复原网络如UWCNN做前置处理但这类网络本身就需要大量的水下成对数据训练对算力的消耗也不小现阶段性价比不如传统增强方法高。工程项目的原则是用最小的成本拿到大多数收益而不是追求指标的极致。7. 项目交付与后续维护建议一个完整交付的项目除了模型权重、PyQt源码和标注好的数据集之外还应该带上这几样东西清晰的项目目录说明、每个脚本的启动方式、模型文件的大小和适用分辨率说明、以及一份简单的训练日志或评估报告。目录结构可以参考这样组织project/ │ ├── weights/ │ └── best.pt ├── datasets/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ ├── ui/ │ ├── main_window.py │ ├── detect_thread.py │ └── utils.py ├── requirements.txt └── README.md模型文件尽量附注训练信息比如训练多少轮、平均精度多少、在什么分辨率上测试的这样接手的人在复用时可以快速判断这个权重适不适合自己场景。数据集目录里建议保留一份类别名字典和标注规范说明防止时间久了连自己都忘了class_id对应关系。最后再分享一个经验这套项目真正花时间的部分不是模型训练而是数据清洗和界面交互细节的打磨。模型训练一个晚上就能出结果但标注规范不一致导致的精度问题、界面在特定分辨率下显示错乱的问题、摄像头从USB3.0换到USB2.0后帧率骤降的问题——这些才是拖慢进度的坑。我建议所有做同类项目的朋友在动手训练之前先花一倍以上的时间把数据质量管理和界面框架理顺后面会顺畅很多。本文还有配套的精品资源点击获取