B11 - 图像处理 - 模板匹配

模板匹配的原理以及计算匹配的标准

什么是模板匹配?

  • 模板匹配的过程就是用模板图像作为一个滑动窗口在源图像中滑动,每滑动一个像素,记录该像素处匹配的程度,相似程度最大的位置对应模板匹配结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Mat m_input = Imgcodecs.imread(input_path); //输入
    Mat m_template = Imgcodecs.imread(template_path); //模板
    // 创建比较结果图
    int result_rows = m_input.rows() - m_template.rows() + 1;
    int result_cols = m_input.cols() - m_template.cols() + 1;
    Mat g_result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
    // 将结果图进行归一化
    Imgproc.matchTemplate(m_target, m_template, g_result, Imgproc.TM_CCORR_NORMED);
    Core.normalize(g_result, g_result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
    // 得到模板位置
    Core.MinMaxLocResult mmlr = Core.minMaxLoc(g_result);
    Point matchLocation = mmlr.maxLoc; // 此处使用maxLoc还是minLoc取决于使用的匹配算法
    System.out.println(matchLocation.x + "-----" + matchLocation.y);
    // 返回模板的中间坐标
    double x = matchLocation.x + (m_template.cols() / 2);
    double y = matchLocation.y + (m_template.rows() / 2);
    double[] tile = {x, y};

模板匹配的平方差匹配 (CV_TM_SQDIFF)?

  • 通过计算每个像素点的差的平方的和,和数学中统计里面的平方 差类似
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))2R(x, y)=\sum_{x^{\prime}, y^{\prime}}\left(T\left(x^{\prime}, y^{\prime}\right)-I\left(x+x^{\prime}, y+y^{\prime}\right)\right)^{2}

模板匹配的标准平方差匹配 (CV_TM_SQDIFF_NORMED)?

  • 对模板匹配的平方差匹配 (CV_TM_SQDIFF) 的进行了标准化处理,经过处理后,上面的值就不会太大
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))2x,yT(x,y)2x,yI(x+x,y+y)2R(x, y)=\frac{\sum_{x^{\prime}, y^{\prime}}\left(T\left(x^{\prime}, y^{\prime}\right)-I\left(x+x^{\prime}, y+y^{\prime}\right)\right)^{2}}{\sqrt{\sum_{x^{\prime}, y^{\prime}} T\left(x^{\prime}, y^{\prime}\right)^{2} \cdot \sum_{x^{\prime}, y^{\prime}} I\left(x+x^{\prime}, y+y^{\prime}\right)^{2}}}

模板匹配的相关匹配 (CV_TM_CCORR)?

  • 采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0 标识最坏的匹配效果
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))R(x, y)=\sum_{x^{\prime}, y^{\prime}}\left(T\left(x^{\prime}, y^{\prime}\right) \cdot I\left(x+x^{\prime}, y+y^{\prime}\right)\right)

模板匹配的标准相关匹配 (CV_TM_CCORR_NORMED)?

  • 对模板匹配的相关匹配 (CV_TM_CCORR) 进行了标准化处理,经过处理后,上面的值就不会太大
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))x,yT(x,y)2x,yI(x+x,y+y)2R(x, y)=\frac{\sum_{x^{\prime}, y^{\prime}}\left(T\left(x^{\prime}, y^{\prime}\right) \cdot I^{\prime}\left(x+x^{\prime}, y+y^{\prime}\right)\right)}{\sqrt{\sum_{x^{\prime}, y^{\prime}} T\left(x^{\prime}, y^{\prime}\right)^{2} \cdot \sum_{x^{\prime}, y^{\prime}} I\left(x+x^{\prime}, y+y^{\prime}\right)^{2}}}

模板匹配的相关匹配 (CV_TM_CCOEFF)?

  • 将模版对其均值的相对值与图像对其均值的相关值进行匹配,1 表示完美匹配,-1 表示糟糕 的匹配,0 表示没有任何相关性
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))R(x, y)=\sum_{x^{\prime}, y^{\prime}}\left(T^{\prime}\left(x^{\prime}, y^{\prime}\right) \cdot I\left(x+x^{\prime}, y+y^{\prime}\right)\right)

模板匹配的标准相关匹配 (CV_TM_CCOEFF_NORMED)?

  • 对模板匹配的相关匹配 (CV_TM_CCOEFF) 进行了标准化处理,经过处理后,上面的值就不会太大
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))x,yT(x,y)2x,yI(x+x,y+y)2R(x, y)=\frac{\sum_{x^{\prime}, y^{\prime}}\left(T^{\prime}\left(x^{\prime}, y^{\prime}\right) \cdot I^{\prime}\left(x+x^{\prime}, y+y^{\prime}\right)\right)}{\sqrt{\sum_{x^{\prime}, y^{\prime}} T^{\prime}\left(x^{\prime}, y^{\prime}\right)^{2} \cdot \sum_{x^{\prime}, y^{\prime}} I^{\prime}\left(x+x^{\prime}, y+y^{\prime}\right)^{2}}}

多对象模板匹配?

  • 单对象匹配 是找最大匹配的点,所以只能匹配一次
  • 多对象模板匹配设置阈值匹配多次
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    img_rgb = cv2.imread('mario.jpg')
    img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
    template = cv2.imread('mario_coin.jpg', 0)
    h, w = template.shape[:2]
    res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
    threshold = 0.8
    # 取匹配程度大于%80的坐标
    loc = np.where(res >= threshold)
    for pt in zip(*loc[::-1]): # *号表示可选参数
    bottom_right = (pt[0] + w, pt[1] + h)
    cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
    cv2.imshow('img_rgb', img_rgb)
    cv2.waitKey(0)

模板和原图存在差异,模板匹配还能找到原图的目标吗?

  • 模板匹配时,一般是从原图抠出部分区域作模板,如果原图像或目标图像发生变化时,模板匹配还能生效吗
  • 通过平滑模板或原图像人为造成差异,然后比较模板匹配的返回结果,以下 2 种改变模板匹配都正确处理了
    1
    2
    3
    4
    5
    6
    7
    8
    #读入图像,截图部分作为模板图片,平滑原图像
    img_src = cv2.imread('..\\lena.jpg' )
    img_templ = img_src[200:300,200:350].copy()
    img_src = cv2.blur(img_src,(33,33)) # 平滑处理
    #读入图像,截图部分作为模板图片,平滑模板
    img_src = cv2.imread('..\\lena.jpg' )
    img_templ = img_src[200:300,200:350].copy()
    img_templ = cv2.blur(img_templ,(33,33)) # 平滑处理