基于YOLO26的水下海洋生物检测系统【YOLO26+数据集+Python项目源码+UI界面】

基于YOLO26的水下海洋生物检测系统【YOLO26+数据集+Python项目源码+UI界面】

导读

本文基于YOLO26轻量级模型,开发一套面向水下场景的智能海洋生物检测系统。系统聚焦水下目标检测任务,支持10类海洋生物及人类目标识别,涵盖海参、海胆、扇贝、海星、鱼类、珊瑚、潜水员、乌贼、海龟、水母。项目提供PyQt5桌面界面与Flask Web界面双端支持,集成单张图片检测、批量文件夹检测、视频流检测及实时摄像头检测功能,支持检测结果可视化与结构化数据导出,可应用于海洋生态监测、水下作业辅助等场景,兼具检测精度与实时性。


一、软件功能演示

演示视频

1. 单张、多张图片检测

单张图片检测

通过界面选择单张水下图片,系统自动完成目标检测并在界面展示标注结果,同时生成结构化检测数据统计:

序号 图片名称 检测类型 目标类别 置信度 坐标位置
1 000003.jpg 图片检测 scallop(扇贝) 0.89 (124, 167)-(251, 282)
2 000003.jpg 图片检测 fish(鱼类) 0.76 (302, 114)-(415, 201)
3 000003.jpg 图片检测 holothurian(海参) 0.68 (52, 311)-(147, 405)

界面支持目标选中查看,点击检测结果列表可查看目标详细坐标信息(Xmin/Ymin/Xmax/Ymax),并支持单张结果图片保存。

文件夹批量检测

选择包含多张水下图片的文件夹,系统自动遍历所有图片完成批量检测,生成汇总统计数据:

  • 总检测目标数:24
  • 检测类别数:6
  • 高频检测目标:fish(8次)、scallop(6次)

同时生成所有图片的检测结果表格,支持导出为CSV文件存档。

2. 视频检测

上传水下作业视频或实时监控视频,系统实时逐帧检测画面中的海洋生物,动态更新检测统计信息:

序号 画面类型 检测类型 目标类别 置信度 坐标位置
1 实时画面 视频检测 diver(潜水员) 0.92 (197, 144)-(311, 286)
2 实时画面 视频检测 turtle(海龟) 0.87 (89, 116)-(225, 281)
3 实时画面 视频检测 jellyfish(水母) 0.79 (401, 203)-(489, 312)

界面支持视频暂停/播放控制,可随时截取当前帧检测结果保存。

3. 摄像头实时检测

连接USB摄像头或水下专用摄像头,系统实时采集画面并进行检测,适用于水下作业现场实时监测场景。检测结果与视频检测逻辑一致,支持实时统计更新与画面截图保存。

4. 保存检测图片与信息

系统支持两种结果保存方式:

  1. 检测图片保存:将标注后的图片保存至指定目录,图片名称保留原文件名并添加"_detected"后缀
  2. 检测数据保存:将所有检测目标的类别、置信度、坐标等信息导出为CSV文件,格式示例如下:
图片名称 目标类别 置信度 Xmin Ymin Xmax Ymax
000003.jpg scallop(扇贝) 0.89 124 167 251 282
000003.jpg fish(鱼类) 0.76 302 114 415 201
000060.jpg diver(潜水员) 0.91 50 89 187 320

二、环境搭建

本文提供两种环境搭建方式,可根据自身需求选择:

1. 通用pip安装教程

适用于大多数Python环境,通过项目提供的requirements.txt一键安装所有依赖:

bash 复制代码
# 切换至项目根目录
cd "05 Underwater sea creature detection"

# 安装依赖库
pip install -r requirements.txt

2. Conda环境安装教程

适用于Anaconda/Miniconda环境用户,可通过配置文件创建独立环境:

bash 复制代码
# 假设项目提供environment.yml文件(若未提供可自行基于requirements.txt生成)
conda env create -f environment.yml

# 激活环境
conda activate yolo26-underwater

注意:若使用NVIDIA GPU加速,需确保已安装对应版本的CUDA与cuDNN,PyTorch会自动适配GPU环境;无GPU设备可使用CPU运行,但检测速度会有所降低。


三、模型训练、评估、预测

1. 数据集准备

数据集基本信息

本项目使用的水下海洋生物数据集包含10类目标,总计约12000张标注图片,采用VOC XML格式标注,数据集按8:1:1比例划分为训练集、验证集和测试集。

格式转换(XML转YOLO格式)

使用xml2txt.py将VOC XML标注转换为YOLO所需的TXT格式:

python 复制代码
import os
import xml.etree.ElementTree as ET

# 定义类别顺序(需与dataset.yaml一致)
categories = ['holothurian', 'echinus', 'scallop', 'starfish', 'fish', 
              'corals', 'diver', 'cuttlefish', 'turtle', 'jellyfish']
category_to_index = {category: index for index, category in enumerate(categories)}

# 定义输入输出路径
input_folder = r'./VOCData/VOdevkit/Annotations'  # XML标注文件夹
output_folder = r'./VOCData/VOdevkit/labels'       # 输出TXT文件夹

os.makedirs(output_folder, exist_ok=True)

for filename in os.listdir(input_folder):
    if filename.endswith('.xml'):
        xml_path = os.path.join(input_folder, filename)
        tree = ET.parse(xml_path)
        root = tree.getroot()
        
        # 获取图片尺寸
        size = root.find('size')
        width = int(size.find('width').text)
        height = int(size.find('height').text)
        
        objects = []
        for obj in root.findall('object'):
            name = obj.find('name').text
            if name not in category_to_index:
                continue
            
            # 获取边界框坐标
            bndbox = obj.find('bndbox')
            xmin = int(bndbox.find('xmin').text)
            ymin = int(bndbox.find('ymin').text)
            xmax = int(bndbox.find('xmax').text)
            ymax = int(bndbox.find('ymax').text)
            
            # 转换为YOLO格式(归一化中心点坐标+宽高)
            x_center = (xmin + xmax) / 2.0 / width
            y_center = (ymin + ymax) / 2.0 / height
            w = (xmax - xmin) / width
            h = (ymax - ymin) / height
            
            objects.append(f"{category_to_index[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}")
        
        # 保存TXT文件
        txt_filename = os.path.splitext(filename)[0] + '.txt'
        txt_path = os.path.join(output_folder, txt_filename)
        with open(txt_path, 'w') as f:
            f.write('\n'.join(objects))

print("XML转YOLO格式完成!")
数据集划分

使用SplitDataset.py将原始数据集划分为训练集、验证集和测试集:

python 复制代码
import os
import shutil
import random

def split_data(file_path, label_path, new_file_path, train_rate=0.8, val_rate=0.1, test_rate=0.1):
    # 匹配图片与标签文件
    images = os.listdir(file_path)
    labels = os.listdir(label_path)
    images_no_ext = {os.path.splitext(img)[0]: img for img in images}
    labels_no_ext = {os.path.splitext(lbl)[0]: lbl for lbl in labels}
    
    # 检查匹配情况
    matched_data = []
    for name in images_no_ext:
        if name in labels_no_ext:
            matched_data.append((name, images_no_ext[name], labels_no_ext[name]))
    
    unmatched_images = [images_no_ext[name] for name in images_no_ext if name not in labels_no_ext]
    if unmatched_images:
        print("未匹配的图片文件:", unmatched_images)
    
    unmatched_labels = [labels_no_ext[name] for name in labels_no_ext if name not in images_no_ext]
    if unmatched_labels:
        print("未匹配的标签文件:", unmatched_labels)
    
    # 随机打乱数据
    random.shuffle(matched_data)
    total = len(matched_data)
    
    # 划分数据集
    train_data = matched_data[:int(train_rate*total)]
    val_data = matched_data[int(train_rate*total):int((train_rate+val_rate)*total)]
    test_data = matched_data[int((train_rate+val_rate)*total):]
    
    # 创建目录并复制文件
    def copy_files(data_set, set_name):
        img_dir = os.path.join(new_file_path, set_name, 'images')
        lbl_dir = os.path.join(new_file_path, set_name, 'labels')
        os.makedirs(img_dir, exist_ok=True)
        os.makedirs(lbl_dir, exist_ok=True)
        
        for name, img_file, lbl_file in data_set:
            shutil.copy(os.path.join(file_path, img_file), os.path.join(img_dir, img_file))
            shutil.copy(os.path.join(label_path, lbl_file), os.path.join(lbl_dir, lbl_file))
    
    copy_files(train_data, 'train')
    copy_files(val_data, 'val')
    copy_files(test_data, 'test')
    
    print(f"数据集划分完成:训练集{len(train_data)}张,验证集{len(val_data)}张,测试集{len(test_data)}张")

if __name__ == '__main__':
    file_path = r'./VOCData/VOdevkit/images'    # 原始图片文件夹
    label_path = r'./VOCData/VOdevkit/labels'   # 转换后的TXT标签文件夹
    new_file_path = r'./VOCData/VOdevkit'       # 划分后数据集保存路径
    split_data(file_path, label_path, new_file_path)
数据集配置文件

编辑VOCData/VOdevkit/dataset.yaml,配置数据集路径与类别信息:

yaml 复制代码
# 数据集根目录(可改为绝对路径)
path: ./VOCData/VOdevkit
# 训练/验证/测试集路径(相对path)
train: train/images
val: val/images
test: test/images

# 类别总数
nc: 10
# 类别名称(顺序需与转换脚本一致)
names:
  0: holothurian
  1: echinus
  2: scallop
  3: starfish
  4: fish
  5: corals
  6: diver
  7: cuttlefish
  8: turtle
  9: jellyfish

2. 模型训练

使用train.py启动YOLO26模型训练:

python 复制代码
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO

if __name__ == '__main__':
    # 加载YOLO26n预训练模型
    model = YOLO('yolo26n.pt')
    
    # 启动训练
    results = model.train(
        data=r'./VOCData/VOdevkit/dataset.yaml',  # 数据集配置文件路径
        epochs=100,                              # 训练轮次
        batch=16,                                # 批量大小(根据GPU显存调整)
        imgsz=640,                               # 训练图像尺寸
        workers=8,                               # 数据加载线程数
        device=0,                                # GPU设备编号,无GPU则改为'cpu'
        optimizer='AdamW',                       # 优化器选择
        amp=True,                                # 开启自动混合精度训练
        cache=True                               # 缓存数据集(加速训练)
    )

启动训练命令:

bash 复制代码
python train.py

训练过程中,系统会实时输出损失值、精度等指标,训练日志与结果文件会保存至runs/detect/train目录。

3. 训练结果评估

训练完成后,所有结果文件保存在runs/detect/train目录下,关键文件说明:

文件名称 用途说明
weights/best.pt 验证集精度最优的模型权重(推荐使用)
weights/last.pt 最后一轮训练的模型权重
results.csv 训练过程中所有指标的数值记录(可用于绘制训练曲线)
results.png 训练损失曲线、精度曲线可视化图
confusion_matrix.png 混淆矩阵可视化,展示各类别检测的正误情况
BoxPR_curve.png PR曲线,展示不同置信度下的精度-召回率关系
labels.jpg 数据集标签分布统计,展示各类别样本数量

使用val.py进行模型验证,获取详细评估指标:

python 复制代码
from ultralytics import YOLO
import numpy as np
from datetime import datetime

# 加载最优模型权重
model = YOLO(r"./runs/detect/train/weights/best.pt")

# 在验证集上评估模型
metrics = model.val(data=r"./VOCData/VOdevkit/dataset.yaml")

# 计算平均精度与召回率
mean_p = np.mean(metrics.box.p)
mean_r = np.mean(metrics.box.r)

# 保存评估结果
content = []
content.append("="*60)
content.append(f"验证时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
content.append("="*60)
content.append(f"整体 mAP50:        {metrics.box.map50:.4f}")
content.append(f"整体 mAP50-95:     {metrics.box.map:.4f}")
content.append(f"平均 Precision:    {mean_p:.4f}")
content.append(f"平均 Recall:       {mean_r:.4f}")
content.append("\n各类别详细指标:")
for i, (name, p, r, map50, map) in enumerate(zip(metrics.names, metrics.box.p, metrics.box.r, metrics.box.map50, metrics.box.map)):
    content.append(f"类别 {i} ({name}):")
    content.append(f"  Precision: {p:.4f}")
    content.append(f"  Recall:    {r:.4f}")
    content.append(f"  mAP50:     {map50:.4f}")
    content.append(f"  mAP50-95:  {map:.4f}")

with open("./runs/detect/train/val_results.txt", "w", encoding="utf-8") as f:
    f.write("\n".join(content))

print("验证结果已保存至val_results.txt")

4. 模型预测

项目支持多场景预测,以下为核心预测方式示例:

单张图片预测
python 复制代码
from ultralytics import YOLO
import os

# 加载训练好的模型
root = os.getcwd()
model = YOLO(rf'{root}/runs/detect/train/weights/best.pt')

# 单张图片预测
source = r'./img_video/test_imgs/000003.jpg'
results = model.predict(source, save=True, conf=0.5)  # conf为置信度阈值

# 显示检测结果
results[0].show()

# 保存检测结果图片
results[0].save(filename=r"./results/000003_detected.jpg")
文件夹批量预测
python 复制代码
# 批量预测文件夹内所有图片
source = r'./img_video/test_imgs'
results = model.predict(source, save=True, conf=0.5, batch=8)
视频预测
python 复制代码
# 视频文件预测
source = r'./img_video/test_MP4.mp4'
results = model.predict(source, save=True, conf=0.5, show=True)  # show=True实时显示画面
摄像头实时预测
python 复制代码
# 摄像头实时检测(0为默认摄像头)
source = 0
results = model.predict(source, save=True, conf=0.5, show=True)

YOLO26提供n/s/m/l/x多种规模模型,精度与速度权衡关系为:精度x>l>m>s>n,速度n>s>m>l>x,可根据实际场景选择合适模型。


文末互动区

如果您觉得本项目有帮助,欢迎点赞、分享支持!若需要完整项目代码、数据集或有技术问题,欢迎留言交流。