2. 焊缝特征提取与定位

任务目标

本案例从一张包含焊缝的灰度图像中,通过边缘检测与形态学操作提取焊缝边缘轮廓,并计算焊缝中心线的像素位置。输入为单张焊接工件表面图像,输出为中心线的x坐标数组及可视化标注结果。

环境与依赖

需要Python 3.8+,并安装以下库:

pip install opencv-python numpy matplotlib

实现代码

以下代码完整实现了从图像读取到中心线计算的整个流程:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def extract_weld_centerline(image_path):
    # 1. 读取图像并转为灰度
    img = cv2.imread(image_path)
    if img is None:
        raise FileNotFoundError(f"无法读取图像: {image_path}")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 2. 高斯滤波降噪
    blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)

    # 3. Canny边缘检测
    edges = cv2.Canny(blurred, 50, 150)

    # 4. 形态学闭运算连接断裂边缘
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 3))
    closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)

    # 5. 提取轮廓并筛选最长的两条(焊缝上下边缘)
    contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if len(contours) < 2:
        raise ValueError("未检测到足够的焊缝边缘轮廓")

    # 按轮廓长度降序排列,取前两条
    contours = sorted(contours, key=cv2.contourArea, reverse=True)[:2]

    # 6. 计算每条轮廓的逐行x坐标(取每行最左侧点)
    def contour_to_line(contour):
        pts = contour.reshape(-1, 2)
        line = {}
        for x, y in pts:
            y = int(y)
            if y not in line or x < line[y]:
                line[y] = int(x)
        # 按y排序返回
        return sorted(line.items())  # [(y, x), ...]

    line1 = contour_to_line(contours[0])
    line2 = contour_to_line(contours[1])

    # 7. 计算中心线:对每个y坐标取两条边缘x的平均值
    # 先构建y→x映射
    dict1 = dict(line1)
    dict2 = dict(line2)
    all_ys = sorted(set(dict1.keys()) & set(dict2.keys()))
    if len(all_ys) < 10:
        raise ValueError("边缘重叠区域过小,无法计算中心线")

    centerline = [(y, (dict1[y] + dict2[y]) / 2.0) for y in all_ys]

    # 8. 可视化结果
    vis_img = img.copy()
    for y, x in line1:
        cv2.circle(vis_img, (x, y), 1, (0, 255, 0), -1)
    for y, x in line2:
        cv2.circle(vis_img, (x, y), 1, (0, 255, 0), -1)
    for y, x in centerline:
        cv2.circle(vis_img, (int(x), y), 1, (0, 0, 255), -1)

    # 显示结果
    plt.figure(figsize=(12, 5))
    plt.subplot(1, 2, 1)
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.title("原始图像")
    plt.axis("off")

    plt.subplot(1, 2, 2)
    plt.imshow(cv2.cvtColor(vis_img, cv2.COLOR_BGR2RGB))
    plt.title("绿色=边缘, 红色=中心线")
    plt.axis("off")
    plt.tight_layout()
    plt.show()

    return centerline

if __name__ == "__main__":
    # 使用示例(请替换为实际图像路径)
    result = extract_weld_centerline("weld_image.jpg")
    print(f"检测到中心线点数: {len(result)}")
    print("前5个中心点(y, x):", result[:5])

运行方式

  1. 将上述代码保存为 weld_centerline.py。
  2. 准备一张焊缝灰度图像(或彩色图像,代码会自动转灰度),命名为 weld_image.jpg 放在同目录下。
  3. 运行命令:python weld_centerline.py

验证方法

程序运行后应弹出两个并排图像窗口:左侧为原始图像,右侧为标注结果。验证通过条件如下:

  • 绿色点构成两条大致平行的曲线,分别对应焊缝的上下边缘;
  • 红色点位于两条绿色曲线中间,形成一条连续的中心线;
  • 控制台输出的中心点数量大于10,且前几个点的y坐标递增。

若检测结果不理想,可调整Canny阈值(第3步的50和150)或形态学核大小(第4步的15和3)以适应不同焊缝宽度和对比度。

常见问题

  • 检测不到轮廓:图像对比度过低时,可先尝试直方图均衡化增强对比度。
  • 中心线断裂:增大形态学闭运算的核宽度(如从15改为25),以连接更宽的断裂带。
  • 两条边缘交叉:若焊缝有强烈反光导致边缘混乱,可先使用ROI裁剪只保留焊缝区域。