七夜星七夜月 2023-11-20 19:58 采纳率: 0%
浏览 11
已结题

PCB缺陷检测模板匹配

第一张图是search图,第二张图是template图。两张图的大小都是512*500的大小,并且图片内的黑色线条相同。但是search图中黑线的水平宽度像素为24,白线为22;template图中黑线的水平宽度像素为31,白线为28。也就是说两图的视野并不相同。现在我的问题是,应该怎么将这两张图进行配准,然后两张图片相减得出template图中的短路的部分。我贴上我的代码,主要的思想就是使用旋转放大,然后再通过边缘点使用cv2.matchTemplate进行配准,效果极差,没有达到想要的效果。

img

img

import numpy as np
import argparse
import imutils
import glob
import cv2
import datetime
from skimage.metrics import structural_similarity as compare_ssim
from skimage.metrics import peak_signal_noise_ratio as compare_psnr

# 读取模板图片 matching4.png template_bina.jpg
template = cv2.imread("template3_bina.bmp")
# 转换为灰度图片
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# 执行边缘检测
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]
# 显示模板
# cv2.imshow("Template", template)


# 读取测试图片并将其转化为灰度图片
image = cv2.imread("Search3_bina_512500.bmp")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
found = None
highest_matching_value = (0, 0)
start_angle = 1.0
end_angle = 5.0
step = 0.1

scale_array = np.linspace(0.9, 2.0, 50)
scale_array = np.append(scale_array, (1.6, 2.2, 1.0))
for scale in scale_array[::-1]:
    # 根据尺度大小对输入图片进行裁剪
    resized = imutils.resize(gray, width=int(gray.shape[1] * scale))
    r =  gray.shape[1]  / float(resized.shape[1])

    # 如果裁剪之后的图片小于模板的大小直接退出
    if resized.shape[0] < tH or resized.shape[1] < tW:
        break

    for angle in np.arange(start_angle, end_angle, step):
        center = (resized.shape[1] // 2, resized.shape[0] // 2)
        rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
        rotated_image = cv2.warpAffine(resized, rotation_matrix, (resized.shape[1], resized.shape[0]))

        # 首先进行边缘检测,然后执行模板检测,接着获取最小外接矩形
        edged = cv2.Canny(rotated_image, 50, 200)
        # edged = rotated_image
        result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF_NORMED)
        (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)

        clone = rotated_image[maxLoc[1]:maxLoc[1] + tH, maxLoc[0]:maxLoc[0] + tW]
        ssim = compare_ssim(clone, template)

        # 计算PSNR
        psnr= compare_psnr(clone, template)

        # 如果发现一个新的关联值则进行更新
        if found is None or maxVal > found[0]:
            found = (maxVal, maxLoc, r, angle, scale)
            print(maxVal, maxLoc, scale, angle, ssim, psnr)  # , maxVal_ssim, psnr
            clone = np.dstack([edged, edged, edged])
            cv2.rectangle(clone, (maxLoc[0], maxLoc[1]), (maxLoc[0] + tW, maxLoc[1] + tH), (0, 0, 255), 2)
            cv2.imwrite("Visualize.bmp", clone)

(maxVal, maxLoc, r, angle, scale) = found
resized = imutils.resize(image, width=int(image.shape[1] * scale))

center = (resized.shape[1] // 2, resized.shape[0] // 2)
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_image = cv2.warpAffine(resized, rotation_matrix, (resized.shape[1], resized.shape[0]))
# 计算测试图片中模板所在的具体位置,即左上角和右下角的坐标值,并乘上对应的裁剪因子
(startX, startY) = (int(maxLoc[0]) + highest_matching_value[0],
                    int(maxLoc[1]) + (highest_matching_value[1]))
(endX, endY) = (int((maxLoc[0] + tW)) + highest_matching_value[0],
                int((maxLoc[1] + tH)) + highest_matching_value[1])
print(maxVal, scale, angle)

c = rotated_image[startY:endY, startX:endX]
cv2.imwrite('matching4.bmp', c)
cv2.rectangle(rotated_image, (startX, startY), (endX, endY), (0, 0, 255), 2)
cv2.imwrite('matching_rect4.bmp', rotated_image)
cv2.waitKey(0)


  • 写回答

11条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2023-11-20 20:01
    关注

    引自强大而免费的微信小程序:皆我百晓生

    问题背景:
    模板匹配是一种常用的计算机视觉技术,用于在一个图像中寻找与给定模板最相似的区域。在本问题中,我们有两张大小相同的图像,分别是search图和template图。两张图的黑色线条的水平宽度像素不同,我们希望通过模板匹配的方法找到template图中的短路部分。

    解决思路:

    1. 读取template图和search图,并将它们转换为灰度图像。
    2. 对template图进行边缘检测,以得到可以进行匹配的模板。
    3. 定义一个搜索范围,通过缩放和旋转search图,尝试不同的缩放比例和角度进行模板匹配。这样可以适应search图和template图视野的不同。
    4. 对于每一次缩放和旋转,计算匹配结果的关联值,并记录最大关联值及其对应的位置、缩放比例和角度。
    5. 根据最大关联值的位置、缩放比例和角度,裁剪search图,得到匹配部分的图像。
    6. 将裁剪的图像保存,并画出匹配部分的矩形框。

    解决步骤:
    1.首先,我们需要导入所需的库和模块,并读取template图和search图。

    import numpy as np
    import argparse
    import imutils
    import glob
    import cv2
    import datetime
    from skimage.metrics import structural_similarity as compare_ssim
    from skimage.metrics import peak_signal_noise_ratio as compare_psnr
    
    # 读取模板图片
    template = cv2.imread("template3_bina.bmp")
    # 转换为灰度图片
    template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
    # 执行边缘检测
    template = cv2.Canny(template, 50, 200)
    (tH, tW) = template.shape[:2]
    
    # 读取搜索图片并将其转化为灰度图片
    image = cv2.imread("Search3_bina_512500.bmp")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    

    2.定义缩放和旋转的范围,并创建一个缩放比例的数组。

    found = None
    highest_matching_value = (0, 0)
    start_angle = 1.0
    end_angle = 5.0
    step = 0.1
    
    scale_array = np.linspace(0.9, 2.0, 50)
    scale_array = np.append(scale_array, (1.6, 2.2, 1.0))
    

    3.遍历缩放比例数组,并在每个比例下尝试不同的旋转角度。

    for scale in scale_array[::-1]:
        # 根据缩放比例对搜索图进行裁剪
        resized = imutils.resize(gray, width=int(gray.shape[1] * scale))
        r =  gray.shape[1]  / float(resized.shape[1])
    
        # 如果裁剪之后的图片小于模板的大小直接退出
        if resized.shape[0] < tH or resized.shape[1] < tW:
            break
    
        for angle in np.arange(start_angle, end_angle, step):
            center = (resized.shape[1] // 2, resized.shape[0] // 2)
            rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
            rotated_image = cv2.warpAffine(resized, rotation_matrix, (resized.shape[1], resized.shape[0]))
    

    4.对经过缩放和旋转的搜索图进行边缘检测,并执行模板匹配。

            # 首先进行边缘检测,然后执行模板检测,接着获取最小外接矩形
            edged = cv2.Canny(rotated_image, 50, 200)
            result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF_NORMED)
            (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)
    
            clone = rotated_image[maxLoc[1]:maxLoc[1] + tH, maxLoc[0]:maxLoc[0] + tW]
            ssim = compare_ssim(clone, template)
    
            # 计算PSNR
            psnr= compare_psnr(clone, template)
    

    5.更新最大关联值及其对应的位置、缩放比例和角度。画出匹配部分的矩形框。

            # 如果发现一个新的关联值则进行更新
            if found is None or maxVal > found[0]:
                found = (maxVal, maxLoc, r, angle, scale)
                print(maxVal, maxLoc, scale, angle, ssim, psnr)
                clone = np.dstack([edged, edged, edged])
                cv2.rectangle(clone, (maxLoc[0], maxLoc[1]), (maxLoc[0] + tW, maxLoc[1] + tH), (0, 0, 255), 2)
                cv2.imwrite("Visualize.bmp", clone)
    

    6.根据最大关联值的位置、缩放比例和角度,裁剪search图,得到匹配部分的图像,并保存。

    (maxVal, maxLoc, r, angle, scale) = found
    resized = imutils.resize(image, width=int(image.shape[1] * scale))
    
    center = (resized.shape[1] // 2, resized.shape[0] // 2)
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
    rotated_image = cv2.warpAffine(resized, rotation_matrix, (resized.shape[1], resized.shape[0]))
    # 计算匹配部分在搜索图中的位置
    (startX, startY) = (int(maxLoc[0]) + highest_matching_value[0], int(maxLoc[1]) + (highest_matching_value[1]))
    (endX, endY) = (int((maxLoc[0] + tW)) + highest_matching_value[0], int((maxLoc[1] + tH)) + highest_matching_value[1])
    
    # 裁剪匹配部分的图像并保存
    c = rotated_image[startY:endY, startX:endX]
    cv2.imwrite('matching4.bmp', c)
    

    代码解析:

    1. 代码的第一部分导入了所需的库和模块,并读取了模板图和搜索图。
    2. 代码的第二部分定义了模板匹配中需要用到的变量,并创建了缩放比例的数组。
    3. 代码的第三部分是通过缩放和旋转搜索图来寻找与模板图匹配的部分。在循环中,首先根据缩放比例对搜索图进行裁剪,然后在每个裁剪下尝试不同的旋转角度。
    4. 代码的第四部分对裁剪后的搜索图进行边缘检测和模板匹配,并计算匹配结果的关联值。
    5. 代码的第五部分更新最大关联值及其对应的位置、缩放比例和角度。并画出匹配部分的矩形框。
    6. 代码的第六部分根据最大关联值的位置、缩放比例和角度,裁剪匹配部分的图像,并保存。

    代码运行与结果分析:
    根据代码的逻辑,我们首先读取模板图和搜索图,并对模板图进行边缘检测。然后我们通过缩放和旋转搜索图,尝试不同的缩放比例和角度进行模板匹配。在每次匹配时,我们计算匹配结果的关联值,并记录最大关联值及其对应的位置、缩放比例和角度。最后,我们根据最大关联值的位置、缩放比例和角度,裁剪匹配部分的图像,并保存。可以按照上述步骤运行代码,得到结果。

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 11月23日
  • 修改了问题 11月20日
  • 创建了问题 11月20日

悬赏问题

  • ¥15 java代码写在记事本上后在cmd上运行时无报错但又没生成文件
  • ¥15 关于#python#的问题:在跑ldsc数据整理的时候一直抱这种错误,要么--out识别不了参数,要么--merge-alleles识别不了参数(操作系统-linux)
  • ¥15 PPOCRLabel
  • ¥15 混合键合键合机对准标识
  • ¥100 现在不懂的是如何将当前的相机中的照片,作为纹理贴图,映射到扫描出的模型上
  • ¥15 魔霸ROG7 pro,win11.息屏后会显示黑屏,如图,如何解决?(关键词-重新启动)
  • ¥15 有没有人知道这是哪里出了问题啊?要怎么改呀?
  • ¥200 C++表格文件处理-悬赏
  • ¥15 Windows Server2016本地登录失败
  • ¥15 复合卡卡号轨道写入芯片卡