ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLO水下生物检测实战:从数据划分到PyQt界面部署

YOLO水下生物检测实战:从数据划分到PyQt界面部署 简介采用 YOLO 系列深度学习模型的水下生物目标检测资源面向计算机视觉初学者以及水下生态监测相关开发者提供从数据准备、模型训练到可视化检测的完整实现方案。压缩包内包含一千八百三十个文件总体积约一百一十二点一兆其中有九百一十张 JPG 原始水下图像、四百四十八个 XML 标注文件和四百五十三个 TXT 标签文件共同构建出可直接投入 YOLO 训练的规范数据集同时提供 Python 训练脚本、YAML 配置文件、训练完成的权重以及训练日志和验证结果图表便于用户核对模型效果。资源还设计了基于 PyQt 的图形化检测界面加载图片后即可快速完成水下生物识别整体代码结构清晰并配有必要的环境依赖说明适合希望获得可运行项目并快速上手的实践者。目前已有 111 人学习下载尤其适合需要完整目标检测流程参考的初学者。1. 水下生物检测为什么YOLO在浑浊环境里依然能打水下机器人拍回的图像比想象中难处理得多光线被水吸收后画面偏蓝偏绿远处鱼群的边缘和背景几乎融在一起再加上浮游生物造成的“海洋雪”噪声传统形态学方法一碰就碎。YOLO这类单阶段检测器把定位和分类合成一个回归问题网络直接输出目标框和类别反而在这种低对比度场景下更容易保住召回率。这份资源把完整链路拆成了三个脚本——01划分数据集.py负责把原始图片和标注转成YOLO格式02train.py执行训练03pyqt.py提供一个能点击图片的图形界面。包含自带的标注数据和训练好的权重适合手里有水下图像但不想从零搭建数据管线的算法工程师也适合那些需要快速验证YOLO效果、又不想被标注格式细节卡住的研究者。从环境配置到界面调通离线就能走完整条流程。2. 数据预处理从原图到YOLO格式的txt2.1 水下数据集的“脏”与“稀缺”水下生物数据集有三个绕不开的问题颜色通道衰减、目标尺度严重不均、标注格式五花八门。公开的DeepFish、ROV数据集通常只覆盖晴好水质换到自己采集的视频上分布立刻失效。这份代码自带的图像和标注文件好处是可以立刻复现训练但前提是先把标注统一成YOLO能吃的txt。YOLO的txt每行代表一个目标格式是class x_center y_center width height其中x、y、w、h都除以了图片的宽高做了归一化。这样训练时无论输入分辨率怎么变框的相对位置都不会飘。2.2 01划分数据集.py的核心逻辑解压后目录里通常有images和labels两个文件夹图片和同名txt标签一一对应。01划分数据集.py做的事情是从所有图片中按比例切出训练集和验证集并把图片路径列表写入train.txt和val.txt。下面是我在这个场景下最常用的划分写法# 01划分数据集.py核心要点 import os import random from pathlib import Path img_dir images ratios {train: 0.8, val: 0.2} all_imgs list(Path(img_dir).rglob(*.jpg)) list(Path(img_dir).rglob(*.png)) random.shuffle(all_imgs) n_train int(len(all_imgs) * ratios[train]) def write_txt(paths, filepath): with open(filepath, w) as f: for p in paths: f.write(str(p) \n) write_txt(all_imgs[:n_train], train.txt) write_txt(all_imgs[n_train:], val.txt)这段代码用Path.rglob递归扫描jpeg和png图片随机打乱后按8:2切分。train.txt保存的图片路径会被后续训练脚本逐行读取并根据图片路径自动寻找同名的.txt标签。实际使用时只需要修改img_dir为你自己的图片目录以及调整ratios字典里的比例。需要注意写入的路径如果是相对路径之后运行02train.py时必须在相同的当前目录下执行否则找不到文件。2.3 data.yaml的配置陷阱01划分数据集.py还会生成data.yaml它是训练时的核心配置# data.yaml path: . # 工程根目录建议用相对路径 train: train.txt # 训练图片路径列表 val: val.txt # 验证图片路径列表 nc: 4 # 类别数量 names: [鱼, 蟹, 海星, 虾] # 类别名称与标注id对应这里最常见的坑是path字段。YOLO会把path与train直接拼接如果写成绝对路径换电脑后必须改写成.最稳妥前提是train.txt和val.txt都在项目根目录。另一个坑是nc与names列表长度不一致这样训练能跑但验证结果里的类别名会错位导致绘制PR曲线时无法读。2.4 快速验证划分结果不要急着进入训练先用命令行检查一下切分是否合理wc -l train.txt val.txt head -5 train.txt cat data.yamlwc -l分别统计两个txt的行数两者之和应等于图片总数。如果少了说明有些图片没有对应的标签被过滤掉或者扫描时漏了扩展名。head查看前几行确认路径中不能有空格或中文否则在Windows上打开文件时会因编码问题报FileNotFoundError。这一步能提前消掉训练时一半以上的路径错误。3. 模型训练02train.py背后的损失与回调3.1 启动训练的方式环境配置完成后训练只需要一行命令python 02train.py脚本会自动读取根目录下的data.yaml加载预训练权重开始迭代。YOLO系列通常在ImageNet预训练的backbone基础上微调训练初期冻结浅层让模型先学习目标框的回归随着epoch增加逐渐解冻深层。这样既能沿用通用特征又能适配水下生物的颜色和纹理。如果你是在自己的数据上做全新训练我通常会先跑一个100 epochs的快速实验看收敛趋势再决定是否加到150 epochs。3.2 超参数释疑02train.py里的训练参数以全局变量或argparse形式存在修改前先看下表参数建议值说明epochs100-150太少欠拟合太多过拟合batch-size16/326GB显存建议1612GB可试32img-size640x640水下小目标可试1280显存占用会翻倍workers4-8数据加载线程数Windows上超过8有时会卡死不要盲目把batch-size调大。水下生物类别往往不平衡比如“鱼”出现几千次而“海星”只有几十次更大的batch会让稀有类别在反向传播时被频繁出现的类别稀释掉导致mAP迟迟上不去。3.3 训练过程会留下哪些痕迹训练结束后目录下会出现results.csv、val_batch1_pred.jpg、val_batch1_labels.jpg、labels.jpg以及events.out.tfevents.*和labels.cache。results.csv是每个epoch的指标快照列包含train/box_loss、train/obj_loss、train/cls_loss、metrics/precision、metrics/recall、metrics/mAP_0.5等。val_batch*_labels.jpg画的是验证集上的人工标注框val_batch*_pred.jpg是模型预测出的框左右对比能直观看出漏检和误检。labels.cache是标签索引缓存第二次训练时加载数据集的速度会明显变快。3.4 用pandas快速画损失曲线训练日志刷屏很难看趋势读取results.csv更直接import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(results.csv) df.columns [c.strip() for c in df.columns] fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(df[epoch], df[train/box_loss], labelbox_loss) ax[1].plot(df[epoch], df[metrics/mAP_0.5], labelmAP0.5) plt.savefig(train_curve.png)这段代码先用pandas读取csv并去掉列名首尾可能存在的空格再分别绘制box_loss和mAP曲线。判断训练是否收敛的标准是loss降到平台期且不发生反弹同时mAP0.5趋于稳定。如果box_loss持续下降但mAP一直很低优先检查标签框是否有错位比如类别id和names顺序对不上。3.5 中断恢复与断点续训训练到一半机房断电是常事02train.py如果支持--resume参数就可以续训python 02train.py --resume runs/train/exp/weights/last.ptlast.pt保存的是最近一个epoch的权重和优化器状态续训后学习率不会重回初始值收敛速度远快于重新开始。如果脚本本身没写resume逻辑那就只能手动把last.pt当预训练权重加载但这样学习率调度会重置效果会打折扣。因此拿到代码后建议先检查一下02train.py里是否带有--resume分支。4. PyQt5识别界面把模型封装成能点按钮的工具4.1 为什么要给YOLO套一个GUI水下生物检测的最终用户往往不是开发者而是海洋生物学家或渔业资源调查员。让他们打开终端敲python 03pyqt.py已经算门槛低但更好的是提供一个只有“加载图片”和“检测”两个按钮的窗口。点击图片界面直接显示检测框和类别名这种交互对业务场景最友好。03pyqt.py做的事情就是把训练好的best.pt权重封装进PyQt窗口。4.2 先封装推理类在写界面前先把模型推理逻辑单独拆出来避免界面控件的回调函数里塞满模型代码# detector.py供03pyqt.py调用 import torch import cv2 class Detector: def __init__(self, weightsbest.pt): self.model torch.hub.load(yolov5, custom, pathweights, force_reloadFalse) self.model.conf 0.25 # 置信度阈值 self.model.iou 0.45 # NMS IoU阈值 def detect(self, img_path): img cv2.imread(img_path) results self.model(img[:, :, ::-1]) # BGR转RGB boxes results.xyxy[0].cpu().numpy() # [x1,y1,x2,y2,conf,cls] return img, boxes这里用torch.hub.load加载本地YOLO仓库第一个参数yolov5指向仓库根目录custom表示加载自定义权重。conf和iou是关键参数conf越高漏检越多越低误检越多水下目标对比度低建议把conf放在0.15到0.25之间否则很多半遮挡的鱼会被滤掉。detect返回原始BGR图像和一个[x1, y1, x2, y2, confidence, class_id]的numpy数组。4.3 PyQt5主窗口骨架# 03pyqt.py裁剪版 from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QFileDialog from PyQt5.QtGui import QPixmap, QImage import cv2 from detector import Detector class MainWindow(QMainWindow): def __init__(self): super().__init__() self.det Detector(runs/train/exp/weights/best.pt) self.btn QPushButton(加载图片, self) self.btn.clicked.connect(self.load_image) self.label QLabel(self) self.label.setFixedSize(800, 600) def load_image(self): path, _ QFileDialog.getOpenFileName(self, 选择图片, , Images (*.jpg *.png)) if not path: return img, boxes self.det.detect(path) for x1, y1, x2, y2, conf, cls in boxes: cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, _ rgb.shape qimg QImage(rgb.data, w, h, 3*w, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(qimg)) app QApplication([]) win MainWindow() win.show() app.exec_()这段代码展示了全部交互逻辑点击按钮后打开文件对话框拿到图片路径交给Detector.detect再把画好绿色框的图片转成QImage显示。QImage构造里的bytesPerLine参数必须填3*w即一行的字节数否则图像边缘会错位。真正的03pyqt.py里还会加入类别名称显示和识别耗时统计但核心结构就是这样一个类。4.4 界面以外的坑模型路径写错是最常见的界面启动失败原因。runs/train/exp/weights/best.pt在Windows下反斜杠和字符串转义容易冲突建议在Detector.__init__里用os.path.abspath(weights)转成绝对路径。另一个坑是PyQt5在某些Linux发行版上缺少xcb运行环境启动时报Qt platform plugin xcb错误需要安装libxcb-xinerama0而不是重装PyQt5。5. 进阶水下图像增强与部署排错5.1 在检测前加一个白平衡水下图像偏蓝绿直接送进模型会让颜色特征失效。一个低成本的做法是在推理前做灰度世界白平衡import numpy as np def gray_world(img): b, g, r cv2.split(img) mean (b.mean() g.mean() r.mean()) / 3 b np.clip(b * mean / b.mean(), 0, 255) g np.clip(g * mean / g.mean(), 0, 255) r np.clip(r * mean / r.mean(), 0, 255) return cv2.merge([b.astype(uint8), g.astype(uint8), r.astype(uint8)])这个函数假设场景平均色接近灰色将三个通道的平均值拉齐减弱水下偏色。在Detector.detect里把img先经过gray_world再送入模型。近岸浑浊水质采集的数据做增强后mAP通常能提升2-5个点但单一色调的深海水域反而会引入不存在的偏色需要先做A/B测试。5.2 显存不足的应急方案训练时遇到CUDA out of memory优先把batch-size减半然后把img-size从640降到512。分辨率降低会直接压缩特征图尺寸显存占用下降最明显。再不够就冻结backbone前几层只训练检测头在02train.py里找到冻结相关配置把冻结层数从10提升到20效果等价于减小模型可学习参数量。5.3 导出ONNX用于轻量部署PyQt工具适合本地演示但若部署到水下机器人的边缘盒子通常需要转ONNXpython export.py --weights best.pt --include onnx --img-size 640导出后用onnxruntime加载推理速度比PyTorch动态图快30%以上。注意--img-size必须与训练时保持一致否则前处理resize产生偏差检测框会整体偏移。导出的ONNX默认固定分辨率动态分辨率需要手动修改graph输入维度代价是推理速度略降。5.4 从results.csv统计类别表现训练完成后的results.csv里每一列都有mAP信息按类别分析能快速定位弱点df pd.read_csv(results.csv) cls_cols [c for c in df.columns if map in c and 0.5 in c] print(df[cls_cols].tail(1))cls_cols匹配所有列名中包含map和0.5且不含班级别的字段打印最后一个epoch的各类别mAP。横向对比哪一类在0.3以下就该针对那一类补充训练数据或调整类别损失权重。本文还有配套的精品资源点击获取
返回列表