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])
运行方式
- 将上述代码保存为
weld_centerline.py。 - 准备一张焊缝灰度图像(或彩色图像,代码会自动转灰度),命名为
weld_image.jpg放在同目录下。 - 运行命令:
python weld_centerline.py
验证方法
程序运行后应弹出两个并排图像窗口:左侧为原始图像,右侧为标注结果。验证通过条件如下:
- 绿色点构成两条大致平行的曲线,分别对应焊缝的上下边缘;
- 红色点位于两条绿色曲线中间,形成一条连续的中心线;
- 控制台输出的中心点数量大于10,且前几个点的y坐标递增。
若检测结果不理想,可调整Canny阈值(第3步的50和150)或形态学核大小(第4步的15和3)以适应不同焊缝宽度和对比度。
常见问题
- 检测不到轮廓:图像对比度过低时,可先尝试直方图均衡化增强对比度。
- 中心线断裂:增大形态学闭运算的核宽度(如从15改为25),以连接更宽的断裂带。
- 两条边缘交叉:若焊缝有强烈反光导致边缘混乱,可先使用ROI裁剪只保留焊缝区域。