简介面向海洋工程与基础设施巡检场景这份ultralytics-yolov8水下管道检测识别项目包集成了基于YOLOv8的检测方案涵盖标注数据集、训练好的模型与配套使用教程可帮助巡检人员或CV研究者快速构建水下管道识别流程。压缩包包含2000个文件以1985个xml标签文件为主辅以md说明文档、data.yaml配置与必要txt文件整体大小878.42MB。数据集内置7971张已标注图像同时提供YOLO格式txt与VOC格式xml标签并预先划分好train、val、test子集附带data.yaml可直接用于YOLOv5/v8/v9/v10/v11/v12系列算法训练类别仅针对underwater-pipe单类目标专一性较强。目前已有118人学习浏览适合水下管道巡检、海洋机器人视觉识别等方向的入门与进阶参考附带的教程和可视化参考链接也有助于对照验证模型效果。1. 水下管道检测资源一份可以直接开工的YOLOv8工程包水下管道巡检是海洋工程里高频但难落地的场景难点不在模型结构而在标注数据。这份资源包的价值恰好落在这里7971张真实场景图同时给了YOLO格式txt和VOC格式xml双份标签train/val/test已经划分好data.yaml写好了类别和路径训练好的权重也附在包里。拿到手不用重新标注改一下data.yaml里的绝对路径就能跑。适合三类人海洋工程做视觉检测的工程师、需要真实数据集做课题的学生、想找一个干净YOLO项目完整走通训练到部署流程的初学者。检测类别只有一类underwater-pipe针对性极强海洋工程基础设施巡检是它最直接的应用面。2. 数据集结构与双格式标签7971张图的组织方式和转换方法2.1 目录划分与data.yaml拆解解压后建议先看整个目录树。标准的项目结构应该是这样的project/ ├── data.yaml ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels_yolo/ # YOLO格式txt ├── labels_voc/ # VOC格式xml ├── best.pt # 训练好的模型权重 └── README.mdimages和labels的对应关系要特别关注如果你看到images/train下面有6000张图labels_yolo/train下面也必须有6000个txt一一对应。数量不一致说明标注文件缺失或图片重复训练时会出现警告。接下来是data.yaml这个文件是整个训练流程的入口Ultralytics框架加载数据集时全靠它。里面通常写着# 路径配置 train: /your_abs_path/train/images val: /your_abs_path/val/images test: /your_abs_path/test/images # 类别定义 nc: 1 names: 0: underwater-pipe重点是路径部分。我用过很多次YOLO训练最常见的问题就在这一段train和val的路径如果没写全或者用的是相对路径训练脚本报出的错误信息又不直观经常是“train set is empty”之类的提示排查起来很费劲。我的经验是拿到资源后第一步不是训练而是先把data.yaml里的路径全部改成当前机器上的绝对路径然后写一个三行脚本验证一下import os import yaml with open(data.yaml, r) as f: cfg yaml.safe_load(f) for key in [train, val, test]: p cfg[key] print(key, p, os.path.exists(p))每个字段都输出True说明路径没问题。很多第一次用这个资源包的人卡住的地方都在这里先从路径下手排查可以节省大量时间。2.2 YOLO/TXT与VOC/XML的转换脚本对于很多下游任务来说标签格式往往决定能否直接复用现有代码。比如有些检测代码读xml、有些读txt甚至有的工程只接受单个文件。这个资源包给了双份标签是件好事但使用的时候还是要注意双份标签的生成时间可能不完全同步用之前最好抽查几个文件对比。我自己做转换时比较喜欢用一段独立的Python脚本。下面是把YOLO的txt转成VOC的xml的代码假设图像宽度和高度已知import os from lxml import etree def yolo_txt_to_voc_xml(img_path, txt_path, xml_out, class_names, img_w, img_h): 把YOLO格式txt转成VOC格式xml。 txt每行: class_id cx cy w hcx/cy/w/h都是归一化到[0,1]的浮点数。 root etree.Element(annotation) folder etree.SubElement(root, folder) folder.text os.path.basename(os.path.dirname(img_path)) or images filename etree.SubElement(root, filename) filename.text os.path.basename(img_path) size etree.SubElement(root, size) width etree.SubElement(size, width) width.text str(img_w) height etree.SubElement(size, height) height.text str(img_h) depth etree.SubElement(size, depth) depth.text 3 if not os.path.exists(txt_path): return with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) cx float(parts[1]) cy float(parts[2]) w float(parts[3]) h float(parts[4]) # 反归一化得到绝对像素坐标 xmin int((cx - w / 2) * img_w) ymin int((cy - h / 2) * img_h) xmax int((cx w / 2) * img_w) ymax int((cy h / 2) * img_h) # 坐标越界保护VOC对越界框处理不友好 xmin max(0, min(xmin, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) xmax max(0, min(xmax, img_w - 1)) ymax max(0, min(ymax, img_h - 1)) obj etree.SubElement(root, object) name etree.SubElement(obj, name) name.text class_names[cls_id] pose etree.SubElement(obj, pose) pose.text Unspecified truncated etree.SubElement(obj, truncated) truncated.text 0 difficult etree.SubElement(obj, difficult) difficult.text 0 bndbox etree.SubElement(obj, bndbox) xmin_el etree.SubElement(bndbox, xmin) xmin_el.text str(xmin) ymin_el etree.SubElement(bndbox, ymin) ymin_el.text str(ymin) xmax_el etree.SubElement(bndbox, xmax) xmax_el.text str(xmax) ymax_el etree.SubElement(bndbox, ymax) ymax_el.text str(ymax) tree etree.ElementTree(root) tree.write(xml_out, encodingutf-8, xml_declarationTrue) if __name__ __main__: class_names [underwater-pipe] yolo_txt_to_voc_xml( img_pathsample.jpg, txt_pathsample.txt, xml_outsample.xml, class_namesclass_names, img_w640, img_h480, )这段代码有几个需要留意的边界首先是class_names列表顺序必须和训练时data.yaml里的names一致否则转换出来的tag名字就是错的其次是img_w和img_h如果是从图片读取的要先用PIL或OpenCV确认尺寸拿错尺寸转换出来的坐标会整体偏移。2.3 标注质量的快速体检接手别人标注的数据集最怕的是标签噪声水下场景尤其明显管道被泥雾遮挡、目标太小、漏标、误标。在开始训练前可以先做一次快速统计import os label_dir labels_yolo/train total_boxes 0 empty_files 0 for filename in os.listdir(label_dir): if not filename.endswith(.txt): continue path os.path.join(label_dir, filename) with open(path, r) as f: lines [line for line in f.read().strip().split(\n) if line.strip()] total_boxes len(lines) if len(lines) 0: empty_files 1 print(ftotal boxes: {total_boxes}, empty files: {empty_files})如果空文件比例超过3%说明标注阶段有过漏标建议先补标或把空文件对应的图片挑出来检查。另外统计框的宽高分布会很有帮助大部分框如果宽度小于图像宽度的1/20说明目标是典型的小目标训练时imgsz可以考虑加大到768甚至896。3. 用YOLOv8训练水下管道模型环境搭建、参数设定与训练闭环3.1 环境安装与依赖版本选择Ultralytics YOLOv8训练依赖的核心库是ultralytics和PyTorch。很多人直接pip install ultralytics报错最常见的错误是could not find a version that satisfies the requirement ultralytics这通常是pip源问题或Python版本过低。我一般建议用conda新建一个干净环境conda create -n yolo python3.10 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralyticstorch版本必须和CUDA驱动匹配。cu121对应CUDA 12.1如果你的显卡驱动只支持CUDA 11.x装上去会报驱动不匹配。新驱动基本都支持12.x但如果用的是GTX 1660 Ti这类老卡建议改用cu118或干脆装CPU版本否则训练速度极慢且不稳定。装完ultralytics之后跑一条命令验证python -c from ultralytics import YOLO; print(YOLO.__name__)如果能输出YOLO说明环境没问题。常见的一个坑是OpenCV的头文件版本和torch冲突直观表现是import ultralytics时直接段错误这时需要重新装opencv-python-headless版pip uninstall opencv-python opencv-contrib-python -y pip install opencv-python-headless3.2 训练参数与启动命令环境没问题后把data.yaml修改好直接开始训练yolo detect train data/path/to/data.yaml modelyolov8n.pt epochs150 imgsz640 batch16 device0参数解析data指向上一步修改好的data.yaml建议使用绝对路径。model如果想从头训练用yolov8n.yaml如果想用预训练权重做迁移学习用yolov8n.pt。通常用.pt比.yaml效果更好因为COCO预训练权重提供了底层特征。epochs150是单类任务的合理值mAP曲线一般第60-90轮开始收敛但数据量很大时可能100轮还不够。imgsz输入分辨率。水下管道检测时目标可能很小把imgsz从640提高到768能提升小目标召回率代价是训练时间和显存上升约50%。batch取决于显存。一张8G显存的卡跑yolov8nimgsz640batch最多16换成yolov8m只能跑到4。建议打开amp混合精度训练默认是开启的不需要额外配置。GTX 1660 Ti跑yolov8的常见问题是显存只有6G我尝试过batch16直接OOM。把batch降到8、加上amp可以把150个epoch跑完单epoch时间大约2分钟总共5小时左右。训练过程中如果想实时看损失函数曲线Ultralytics默认会在runs/detect/train/expXXX目录下生成results.png包含loss、precision、recall、mAP的曲线。嫌内置图不够细的话可以开启tensorboard回调yolo detect train datadata.yaml modelyolov8n.pt epochs150 imgsz640 batch16 projectruns nameexp1 plotsTrueplotsTrue会额外输出混淆矩阵、F1曲线等分析类别不平衡和漏检原因时非常有用。3.3 训练日志阅读与权重选择训练结束时看几个关键文件results.png和weights/best.pt、weights/last.pt。怎么读懂results.png里的六条曲线三点经验train/loss不断下降是必须的如果train/loss在早期就不降多半是学习率或数据问题。对比val/loss和train/lossval/loss如果出现持续上升而train/loss还在下降就是过拟合信号单类检测任务数据量足够大过拟合风险相对低但也不是没有。precision和recall两条曲线是此消彼长的不要单看一路。mAP50-95比mAP50更能反映模型对边界框质量的把控能力。选权重时我的习惯先看mAP50-95的最高点定位是哪个epoch然后去weights目录里找对应epoch的权重文件。Ultralytics只保留best.pt和last.ptbest.pt就是验证集上mAP50-95最高时保存的权重。如果只是快速验证用best.pt就行如果要继续训练best.pt和last.pt都可以做起点我一般用last.pt继续训练因为它在训练轨迹的最末端继承性更顺。4. 用训练好的模型做推理单图、批量、视频与性能调优4.1 单图与批量推理代码推理是所有用户最关心的环节模型训练好了怎么拿它去检测。Ultralytics封装得很简单几行代码就能跑from ultralytics import YOLO model YOLO(best.pt) # 先确认类别映射正确避免names索引错位 print(model.names) # 单张图片推理 results model.predict(test_images/sample.jpg, conf0.35, iou0.45) # 批量推理source可以是目录 results model.predict(test_images/, conf0.35, iou0.45, saveTrue, projectoutput, namerun1) for r in results: # 每个r对应一张图的结果 for box in r.boxes: xyxy box.xyxy.cpu().numpy()[0] conf box.conf.cpu().numpy()[0] cls int(box.cls.cpu().numpy()[0]) print(fclass{model.names[cls]}, conf{conf:.2f}, bbox{xyxy})参数解释conf是置信度阈值水下图像环境复杂建议先用0.3试跑一次效果漏检多就降到0.25误检多就升到0.5。iou是NMS的IoU阈值默认0.45比较好用目标密集时可以降到0.35。saveTrue保存标注框的图片save_txtTrue保存每个目标的坐标。project/name控制输出目录默认是runs/detect/predict。使用这个模型时要记住模型只检测underwater-pipe这一类目标如果图片里出现其他类型的管道或结构物它不会给出任何预测框。遇到这种情况不用担心说明模型精度集中且不会误检其他类别。4.2 检测结果结构化输出如果你需要把检测结果做成json或csv方便后续处理可以用下面的方式提取import json results model.predict(test_images/, conf0.35, iou0.45) output [] for idx, r in enumerate(results): img_id fimage_{idx} detections [] for box in r.boxes: x1, y1, x2, y2 box.xyxy.cpu().numpy()[0] conf float(box.conf.cpu().numpy()[0]) detections.append({ bbox: [float(x1), float(y1), float(x2), float(y2)], confidence: conf, }) output.append({image_id: img_id, detections: detections}) with open(detections.json, w) as f: json.dump(output, f, indent2)这样输出的json可以直接接入后续的业务逻辑。很多做海洋工程系统的人会把检测结果接进数据库或监控大屏json格式比yolo默认的txt更容易解析。4.3 视频与实时检测如果要检测水下视频或实时摄像头画面YOLOv8也做了封装yolo detect predict modelbest.pt sourcevideo.mp4 conf0.3 iou0.45 saveTrue视频推理能直接输出带框的视频但有两个需要提前处理的问题。一个是帧率和时长保持一致源视频fps如果是30输出也应该是30否则视频播放速度看起来不对。另一个是处理性能如果用CPU推理高清视频大概率跑不到实时帧率。我的处理方式是限制推理分辨率把视频帧缩放成640后再送进模型保存时再映射回原始坐标。4.4 推理速度优化推理速度决定了这个模型能不能用于实时巡检。三个优化手段按性价比排序第一半精度推理。默认模型加载是fp32改成fp16能省一半显存和不少时间model YOLO(best.pt).half()第二批量推理。如果是离线分析一批图片把batch_size设成8或16吞吐量能提升两倍以上注意batch值不要超过显存容量。第三如果项目部署在边缘设备上比如RK3588这类ARM平台PyTorch模型跑不动实时必须导出ONNX再转成RKNN格式转换之后推理速度能快一个数量级。这部分内容放到第6章细说。5. 水下管道检测避坑五个最容易翻车的地方5.1 data.yaml路径写错导致训练集为空现象训练启动后terminal打印的train/val图片数量均为0或者显示“WARNING no train images found”训练无法正常进行。原因data.yaml里的train/val/test路径是写死的绝对路径但把项目复制到了另一台机器上后路径失效了也有可能是相对路径在当前工作目录下解析不到正确位置。解决用编辑器打开data.yaml确认train、val两个字段指向实际存在的图片目录。最好用Python验证一下import os import yaml cfg yaml.safe_load(open(data.yaml)) for key in [train, val, test]: p cfg[key] print(key, p, os.path.exists(p))如果返回False就把路径改成当前机器的绝对路径。这个操作看起来基础但训练启动报错时第一个检查的就是它。5.2 显存不足直接OOM现象启动训练不到10秒就报CUDA out of memory程序终止。原因batch值超过显卡显存能容纳的范围尤其是GTX 1660 Ti这类6G显存的卡batch16必爆。解决先把batch降到8同时确认ampTrue默认开启。如果还报OOM把imgsz降到480或者换用yolov8n这种最小体积的模型。amp混合精度是最省事的手段训练速度和显存占用优化非常明显。5.3 class id与names顺序错乱现象推理时打印出的类别名是数字0而不是underwater-pipe或者标签名和实际目标对不上。原因推理代码里用model.names去索引类别名但模型文件里的names定义可能保留着COCO预训练的默认类别加载后没有覆盖。解决在推理前强制把model.names改成正确的映射model YOLO(best.pt) model.names {0: underwater-pipe}如果想彻底解决在训练脚本里把data.yaml中的names设置为与基础数据集一致训练出的模型就会自带正确的names。5.4 训练epoch过多导致过拟合现象train/loss持续下降但val/loss在第90轮后开始反弹precision不再上升mAP50-95震荡。原因训练轮数太多模型开始记住训练集中的细节噪声对验证集泛化能力反而下降。解决把epochs降到120或者观察results.png如果val曲线连续5个epoch没有下降就手动停止用best.pt作为最终产物。单类检测场景数据量接近8000张时150epoch基本在第80-100轮就已经最优后面的震荡不会带来收益。5.5 水下偏色导致的推理误检现象在外场水槽或真实海域拍摄的照片上模型把水草、缆绳、石头检测成underwater-pipe而且置信度不低。原因训练集里图片光照条件、水色、浑浊度过于一致模型学到的特征偏向色彩而不是纹理结构。水下单通道图像天然偏蓝绿色模型容易把色彩通道当成强特征。解决在训练时做数据增强尤其是色调、饱和度、亮度的随机调整让模型学会不完全依赖颜色。推理端可以先把输入图片做白平衡预处理再送进模型import cv2 import numpy as np def white_balance(img): result cv2.cvtColor(img, cv2.COLOR_BGR2LAB) avg_a np.mean(result[:, :, 1]) avg_b np.mean(result[:, :, 2]) result[:, :, 1] result[:, :, 1] - ((avg_a - 128) * (result[:, :, 0] / 255.0) * 1.1) result[:, :, 2] result[:, :, 2] - ((avg_b - 128) * (result[:, :, 0] / 255.0) * 1.1) result cv2.cvtColor(result, cv2.COLOR_LAB2BGR) return result预处理后再送入模型误检率会明显下降。6. 效果验证与部署选型mAP指标、ONNX导出与硬样本验收6.1 用独立test集打分经过训练和推理调参后模型是否达到可用水平需要用独立的test集来评估。不要用val集代替test集因为val集在训练过程中参与过权重选择有过拟合嫌疑。这个资源包已经把test目录单独划分好直接用yolo detect val datadata.yaml modelbest.pt imgsz640 splittest输出结果是一个包含Precision、Recall、mAP50、mAP50-95的表格。对水下管道检测来说mAP50-95达到0.7以上Precision和Recall都在0.8以上基本上就能满足大多数巡检需求。6.2 导出ONNX并确认精度如果模型要部署到边缘设备或服务器端把PyTorch权重导出成ONNX是一个标准步骤yolo export modelbest.pt formatonnx imgsz640 dynamicFalse导出后用onnxruntime加载ONNX模型和PyTorch结果做一次逐框对比确认坐标和置信度没有明显差异。输出文件会生成best.onnx可以直接被ONNXRuntime、TensorRT或者RK3588的RKNN工具链消费。需要注意ONNX推理时输入图像预处理BGR转RGB、归一化必须和训练时保持一致否则精度会打折。6.3 硬样本人工验收指标之外我最后一定做一次硬样本人工抽检。从test集里挑出三类图片水下浑浊度高的、管道与背景纹理相似的、目标非常小的各抽10张用模型检测并保存结果。检查重点不是精确的mAP分数而是边界框是否贴合目标框是否偏上、偏下、框了一半。凡是模型在这种极端样本上明显翻车的我不会直接部署会回退到数据增强或在训练集中补充这类样本重训。从那以后我每次做水下目标检测项目都强制走一遍这个流程先跑test集拿指标再导出onnx做部署验证最后人工看30张挑出来的硬样本。指标达标但硬样本翻车的模型不上线。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?