简介:基于 YOLOv8 的石油管道焊缝缺陷 X 光检测系统,是一套完整的深度学习目标检测毕业设计项目。面向计算机视觉、人工智能、自动化等专业的本专科学生及开发者,适用于毕设、课程设计或初期立项演示;项目聚焦工业质检场景,能够对 X 光图像中的焊缝缺陷进行自动识别与定位,同时提供可视化操作页面,降低使用门槛。压缩包共含 8 个文件,包括 3 个 Python 源码文件(涉及可视化界面、模型训练与视频检测)、3 个 PyTorch 模型权重文件,以及 2 个文本说明文档,整体大小约 15.91MB;已有 52 人学习使用。除了可直接运行的代码外,还附带完整数据集与部署说明,并支持输出核心指标曲线、混淆矩阵、F1 分数曲线、精确率-召回率曲线、验证集预测结果和标签分布图,便于答辩展示与效果评估;代码经过测试,拿来即用,也可在此基础上修改扩展其他检测功能。
1. X光焊缝缺陷检测为什么必须用目标检测落地
石油管道焊缝的X光底片上,气孔、夹渣、未熔合这类缺陷往往只占图像面积的千分之几,人工读片一张要花两三分钟,遇到口径大、焊缝长的片子更耗时。目标检测在这个场景下的意义不是“判断有没有缺陷”,而是把缺陷位置、类别、置信度一次性输出,直接供缺陷评级使用。YOLOv8在这类工业视觉任务上收敛快、部署成本低,v8n权重不到6MB,CPU也能跑到可用的推理帧率。这套资源包含了完整数据集、训练脚本、可视化界面和部署说明,适合本科毕设做完整系统,也适合工业检测工程师快速验证目标检测在焊缝X光缺陷识别上的可行性。
2. 焊缝X光数据集的预处理与YOLO标注格式转换
2.1 焊缝X光图像特点与标注策略
工业X光设备的输出分辨率通常在1024×1024到2048×2048之间,远高于普通摄像头图像,而缺陷目标只有30×30到200×200像素。直接把原图送入YOLOv8训练,若缩放至640×640,小目标信息会严重丢失。资源里默认的imgsz=640能覆盖大部分缺陷尺寸,但对裂纹这类细长目标,需要标注框严格贴合目标边界。
我一般先用LabelImg或X-AnyLabeling对焊缝缺陷做矩形框标注,缺陷类别依据NB/T 47013标准归结为四类:圆形缺陷(气孔、夹钨)、条形缺陷(未熔合、未焊透)、裂纹和夹渣。标注完的XML或JSON文件无法直接用于YOLO训练,必须转换成YOLO格式的归一化txt文件。
2.2 从标注文件到YOLO txt的转换脚本
import os import xml.etree.ElementTree as ET def xml_to_yolo(xml_path, out_path, class_map): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) with open(out_path, "w", encoding="utf-8") as f: for obj in root.iter("object"): cls_name = obj.find("name").text if cls_name not in class_map: continue box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) # 转为归一化的中心点宽高格式 x_center = (x1 + x2) / 2 / img_w y_center = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h f.write(f"{class_map[cls_name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n") class_map = {"porosity": 0, "slag": 1, "lack_fusion": 2, "crack": 3} xml_to_yolo("annotations/01.xml", "labels/01.txt", class_map)转换逻辑不复杂,核心是把像素坐标归一化到0到1区间,再换算成YOLO要求的中心点加宽高格式。缩放坐标必须在归一化之前完成,否则图像resize后目标框会偏移。class_map可直接扩展,如果数据集里有第五类缺陷,追加一个键值对即可。注意类别序号从0开始,如果从1开始编号,YOLO训练会报标签越界错误。
2.3 数据集划分与目录结构的工程约定
训练时YOLO会读取data.yaml,按目录路径自动分配训练集和验证集。目录结构的推荐组织形式如下:
| 路径 | 说明 |
|---|---|
| dataset/images/train | 训练图像 |
| dataset/images/val | 验证图像 |
| dataset/labels/train | 与训练图像同名的txt标注 |
| dataset/labels/val | 与验证图像同名的txt标注 |
| dataset/data.yaml | 数据集描述文件 |
划分脚本用shutil按8:2比例随机抽取即可。要注意的是,同一个焊接口的多张X光片应放在同一个分区,避免同一焊口的相似缺陷同时出现在训练集和验证集,导致mAP虚高。
import shutil import random from pathlib import Path random.seed(42) img_dir = Path("raw_images") label_dir = Path("raw_labels") train_ratio = 0.8 all_imgs = list(img_dir.glob("*.png")) random.shuffle(all_imgs) split_idx = int(len(all_imgs) * train_ratio) for i, img in enumerate(all_imgs): dest = "train" if i < split_idx else "val" shutil.copy(img, f"dataset/images/{dest}/") shutil.copy(label_dir / f"{img.stem}.txt", f"dataset/labels/{dest}/")这里对全部样本做了纯随机划分,是工程上的快速做法。如果原始文件名包含管道编号,更稳妥的方式是先按管道编号分组,再对组做随机划分,避免同一管道的X光片泄漏到验证集,导致评估结果失真。
2.4 data.yaml配置与类别权重处理
train: dataset/images/train val: dataset/images/val nc: 4 names: ['porosity', 'slag', 'lack_fusion', 'crack']data.yaml中的nc和names必须与转换脚本中的class_map一致。当某些缺陷类别(如裂纹)样本量远小于其他类别时,可在训练参数中设置class_weights。Ultralytics默认不做类别加权,自己实现时需要先统计每个类别的标签框数量,再按反频率计算权重向量传入损失函数。
3. YOLOv8n与yolo11n选型、训练参数设置与指标曲线判读
3.1 模型规模:为什么同时准备yolov8n.pt和yolo11n.pt
项目里同时存在yolov8n.pt和yolo11n.pt两个预训练权重文件。n代表nano级别,参数量约3.2M,FLOPs约8.7G。选择nano级别的原因很直接:焊缝X光缺陷类别少,通常四到五类,目标形态相对固定,不需要大容量模型去拟合复杂特征。小模型在小数据集上反而更不容易过拟合。
| 模型 | 参数量 | 输入尺寸 | COCO mAP50 | 单张CPU推理耗时 |
|---|---|---|---|---|
| YOLOv8n | 3.2M | 640 | 37.3 | 约50ms |
| YOLOv8s | 11.2M | 640 | 44.9 | 约90ms |
| YOLOv8m | 25.9M | 640 | 50.2 | 约200ms |
| YOLO11n | 2.6M | 640 | 39.5 | 约45ms |
yolo11n是Ultralytics后续推出的新结构,用C3k2模块替代了C2f,在同等推理开销下精度略高。实际使用中,如果不追求极限指标,yolov8n作为预训练权重完全够用;若训练集超过两千张,可以换yolo11n再训练一轮。数据量、缺陷形态复杂度是选型的核心依据,而不是一味追求高mAP。
3.2 train_mode.py中的关键超参数解析
from ultralytics import YOLO if __name__ == "__main__": model = YOLO("yolov8n.pt") # 加载COCO预训练权重 results = model.train( data="dataset/data.yaml", epochs=120, imgsz=640, batch=16, lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, patience=20, device="cuda:0", workers=4, seed=42, save_period=10, )epochs设120是因为焊缝缺陷数据集普遍不大,几百到两三千张,100到150轮足够收敛。imgsz=640与预训练权重输入尺寸一致,改大能保留小目标细节但显存占用上涨明显。batch=16在12GB显存下比较稳妥,显存不够就降到8。
lr0=0.01是Ultralytics的默认推荐值,warmup_epochs=3让模型先以小学习率热身,避免前几个epoch梯度震荡。patience=20表示验证集mAP连续20轮不提升就提前停止,既省时间又能防止后期过拟合。从经验来看,焊缝缺陷数据集的收敛轮次通常在60到90轮之间,120轮的上限留出了足够余量。
3.3 训练产出的指标文件与结果判读
训练结束后,runs/detect/train目录下会自动输出results.png、confusion_matrix.png、PR_curve.png、F1_curve.png、labels.jpg等文件。results.png包含train和val的box_loss、cls_loss、dfl_loss以及mAP50和mAP50-95曲线。如果val的mAP50曲线平稳上升后进入平台期,而val的cls_loss出现回升,说明训练过了最佳轮次,应取best.pt而非last.pt。
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") metrics = model.val(data="dataset/data.yaml", split="val") print(metrics.box.map50) # mAP50,缺陷检测主要看这个指标 print(metrics.box.map) # mAP50-95 print(metrics.box.mp) # 所有类别的平均精度均值python代码里metrics.box.map50直接打印验证集mAP50,答辩或汇报时可直接引用数值。metrics.box.map是COCO风格的mAP50-95,在小目标缺陷场景下会比mAP50低不少,这是正常的,不必担心。如果想看每个类别的AP,可以用metrics.box.ap搭配ap_class_index整理成表格。
4. Visual_interface.py与Detection_video.py的工程化推理实现
4.1 可视化界面加载模型与推理
import tkinter as tk from tkinter import filedialog, Label, Button from PIL import Image, ImageTk from ultralytics import YOLO class WeldDetectApp: def __init__(self, root, model_path): self.root = root self.model = YOLO(model_path) root.title("焊缝缺陷X光检测系统") root.geometry("900x700") # 控件排布:上方按钮区,下方图像展示区 btn_frame = tk.Frame(root) btn_frame.pack(pady=8) Button(btn_frame, text="打开X光图像", command=self.open_image).pack(side=tk.LEFT, padx=5) Button(btn_frame, text="退出", command=root.quit).pack(side=tk.LEFT, padx=5) self.info_label = Label(root, text="请选择X光图像", font=("SimHei", 12)) self.info_label.pack() self.img_label = Label(root) self.img_label.pack(fill=tk.BOTH, expand=True) def open_image(self): path = filedialog.askopenfilename(filetypes=[("Image", "*.png *.jpg *.bmp")]) if not path: return result = self.model.predict(source=path, conf=0.35, iou=0.45, imgsz=640) annotated = result[0].plot() # 显示缺陷统计信息 num = len(result[0].boxes) self.info_label.config(text=f"检测到缺陷数量: {num}") # OpenCV BGR转RGB后再用Tkinter显示 img = Image.fromarray(annotated[..., ::-1]) img.thumbnail((850, 650)) photo = ImageTk.PhotoImage(img) self.img_label.configure(image=photo) self.img_label.image = photo if __name__ == "__main__": root = tk.Tk() WeldDetectApp(root, "best.pt") root.mainloop()conf=0.35表示只显示置信度高于0.35的检测框。焊缝X光缺陷中,裂纹这类小目标置信度普遍偏低,阈值设在0.5很容易漏检;0.3到0.4之间能在漏检和误检之间取得平衡。result[0].plot()返回的是标注了目标框和类别的BGR图像,需要转换通道顺序后再交给Tkinter显示。
Tkinter作为GUI框架不算美观但足够稳定,不依赖额外安装包。资源和环境里已经包含了Visual_interface.py,直接运行即可打开界面。如果要给答辩评委演示,建议把打开图像的默认路径指向验证集目录,现场点击更快。
4.2 视频流检测的帧处理逻辑
import cv2 from ultralytics import YOLO model = YOLO("best.pt") cap = cv2.VideoCapture("weld_video.mp4") fps = cap.get(cv2.CAP_PROP_FPS) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) out = cv2.VideoWriter("output.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height)) while cap.isOpened(): ret, frame = cap.read() if not ret: break # 单帧推理,verbose=False避免终端刷屏 results = model.predict(frame, conf=0.35, iou=0.45, imgsz=640, verbose=False) annotated = results[0].plot() # 在帧左上角叠加当前缺陷数量 num_defects = len(results[0].boxes) cv2.putText(annotated, f"defects: {num_defects}", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) out.write(annotated) cap.release() out.release()视频推理和单张图像推理原理相同,差别只在循环读帧和按帧写视频。缺陷数量文本叠加在左上角,避开焊缝主体区域。如果视频分辨率超过1080p,建议先缩放再推理,高分辨率帧对推理耗时影响明显,帧率会从十几帧掉到三五帧。
Detection_video.py中同样逻辑可直接复用。视频输出采用mp4v编码,兼容常见播放器,如果写出来的文件无法播放,换成XVID编码再试。
4.3 推理置信度阈值与NMS的配合
from ultralytics import YOLO model = YOLO("weights/best.pt") def predict_with_threshold(img, conf=0.35, iou=0.45): result = model.predict(img, conf=conf, iou=iou, imgsz=640)[0] boxes = result.boxes if len(boxes) == 0: # 低置信度回退,减少漏检 result = model.predict(img, conf=0.20, iou=0.45, imgsz=640)[0] return result当高置信度阈值下没有检出任何缺陷时,用更低的置信度阈值再推理一次,作为漏检兜底策略。工业检测场景下“不漏检”比“单帧快”更重要,这种二次推理方式会损失一些效率,但能有效兜住裂纹这类低置信度小目标。
NMS的iou参数控制重叠框的合并力度。焊缝X光图上夹渣和未熔合经常紧邻,如果iou设太小(比如0.2),同一个缺陷会被拆成两个框;设置0.45左右,重叠的目标框会被合理合并。
5. 混淆矩阵与PR曲线定位模型薄弱点的排查方法
打开confusion_matrix_normalized.png,先看对角线以外的错误集中在哪些类别。焊缝缺陷检测中最常见的错误是夹渣与未熔合互相混淆,两者在X光片上的灰度特征接近,都呈现条状或不规则块状。如果混淆矩阵中这两个类别的互相误检率超过20%,说明它们在特征空间中的可分性不足,单纯加训练数据效果有限,需要考虑在标注时合并成一个大类,或引入额外的纹理特征辅助区分。
PR曲线上某一类别的曲线整体偏低时,优先检查该类别的训练样本量。例如裂纹样本只有几十张,而气孔样本上千张,F1曲线中裂纹的峰值会明显低于其他缺陷。解决方法是收集更多裂纹样本,或者做离线增强——旋转角度限制在15度以内,不要做90度翻转,横向裂纹与纵向裂纹的力学意义不同,翻转会破坏语义。
F1-confidence曲线的最佳置信度阈值可以直接从曲线图上读出来,F1值最高点对应的横坐标就是当前类别平衡下最合适的conf。超过一半的情况里,这个值会比默认的0.25高,尤其对气孔这类形态规则的缺陷,阈值提升到0.4可以在不损失召回的前提下滤掉大量背景误检。打开Visual_interface.py,把model.predict中的conf参数修改为读出的最佳值,界面检测的误报率会明显下降,答辩时展示的检测结果也更干净。
如果macro F1偏低但mAP50尚可,说明各类别间的置信度分布偏差较大,此时重点关注AP最低的类别。使用best.pt在验证集上逐类输出指标,找出AP低于0.7的类别,针对这些样本做难例挖掘——从训练集里把预测失败的部分提出来,重新标注后替换入训练集,再训一轮通常能回收3到5个点的AP。
本文还有配套的精品资源,点击获取