Opencv-Python学习笔记(十二):模板匹配

本篇博客记录学习OpenCV-Python模板匹配的相关知识。

  • 使用模板匹配在一幅图像中查找目标。
  • 学习到的函数有: cv2.matchTemplate()cv2.minMaxLoc()。

原理

模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。 OpenCV 为我们提供了函数: cv2.matchTemplate()。如同 2D 卷积,它也是用模板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和与其对应的输入图像的子区域进行比较。OpenCV 提供了几种不同的比较方法(见下文)。其返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配
程度。如果输入图像的大小是( W乘H),模板的大小是( w乘h),输出的结果的大小就是( W-w+1, H-h+1)。当你得到这幅图之后,就可以使用函数cv2.minMaxLoc() 来找到其中的最小值和最大值的位置了。第一个值为矩形左上角的点,( w, h)为模板矩形的宽和高。这个矩形就是找到的模板区域了。
OpenCV 提供的几种不同的比较方法:

  • TM_SQDIFF:计算平方不同,计算出来的值越小,越相关。
  • TM_CCORR:计算相关性,计算出来的值越大,越相关。
  • TM_CCOEFF:计算相关系数,计算出来的值越大,越相关。
  • TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近于0,越相关。
  • TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近于1,越相关。
  • TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近于1,越相关。

接下来我们用一个示例进行验证,虽然有六种不同的比较方法,但是在实际应用中一般会使用归一化的比较方法,下面的示例我我们用TM_SQDIFF_NORMED比较方法。

模板图片:

Opencv-Python学习笔记(十二):模板匹配_第1张图片

 实验代码:

import cv2 as cv
import numpy as np


# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def template_demo():
    tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/rabbit.jpg")  # 模板图片
    print("模板大小:", tpl.shape)
    target = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu22.jpg")  # 原图
    print("原图大小:", target.shape)
    cv.imshow("template", tpl)
    cv.imshow("target", target)
    methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED]  # 三种模板匹配方法
    th, tw = tpl.shape[:2]

    result = cv.matchTemplate(target, tpl, cv.TM_SQDIFF_NORMED)  # 得到匹配结果
    print("结果大小:", result.shape)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
    print("min_val", min_val)
    print("max_val", max_val)
    print("min_loc", min_loc)
    print("max_loc", max_loc)
    tl = min_loc
    br = (tl[0] + tw, tl[1] + th)
    cv.rectangle(target, tl, br, (0, 0, 255), 2)  # tl为左上角坐标,br为右下角坐标,从而画出矩形
    cv.imshow("target", target)
    

if __name__ == '__main__':
    template_demo()

    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()  # 关闭所有窗口

 打印结果值:Opencv-Python学习笔记(十二):模板匹配_第2张图片

可见其返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配程度。如果输入图像的大小是( 727,1011),模板的大小是( 317,256),输出的结果的大小就是( 727-317+1, 1011-256+1)-->(411,756) 。

接下来我们看一下其他两种归一化的比较方法效果图。

实验代码:

import cv2 as cv
import numpy as np


# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def template_demo():
    tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/rabbit.jpg")  # 模板图片
    print("模板大小:", tpl.shape)
    target = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu22.jpg")  # 原图
    print("原图大小:", target.shape)
    cv.imshow("template", tpl)
    cv.imshow("target", target)
    methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED]  # 三种模板匹配方法
    th, tw = tpl.shape[:2]

    for md in methods:
        print(md)
        result = cv.matchTemplate(target, tpl, md)  # 得到匹配结果
        print("结果大小:", result.shape)
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
        print("min_val", min_val)
        print("max_val", max_val)
        print("min_loc", min_loc)
        print("max_loc", max_loc)
        if md == cv.TM_SQDIFF_NORMED:  # cv.TM_SQDIFF_NORMED最小时最相似,其他最大时最相似
            tl = min_loc  # 左上角点位
        else:
            tl = max_loc

        br = (tl[0] + tw, tl[1] + th)
        cv.rectangle(target, tl, br, (0, 0, 255), 2)  # tl为左上角坐标,br为右下角坐标,从而画出矩形
        cv.imshow("match-"+np.str(md), target)


if __name__ == '__main__':
    template_demo()

    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()  # 关闭所有窗口

效果图:

数据结果:

Opencv-Python学习笔记(十二):模板匹配_第3张图片 上面介绍的都是单个匹配,但是如果我们要是进行人脸检测,虽然实际应用是通过机器学习算法实现,但是用我们今天学习的知识也是可以实现的,可能用上面的方法就有点行不通了,因为一张图中会有多张人脸,这时候我们就需要匹配多个对象。下面用个简单的例子进行测试。

步骤也是一样的:

  • 读入模板图像和目标图像
  • 进行模板匹配(打印出返回值,方便下一步操作的判断)
  • TM_CCOEFF_NORMED方法进行比较判断根据上一步的打印值,设定一个阈值
  • 然后寻找位置,将大于0.8的位置保存起来
  • 在匹配到的区域绘制矩形

实验代码:

import cv2 as cv
import numpy as np


# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def more_template_demo():
    tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario_coin.jpg")  # 模板图片
    target = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario.jpg")  # 原图
    cv.imshow("template", tpl)
    cv.imshow("target", target)
    th, tw = tpl.shape[:2]
    result = cv.matchTemplate(target, tpl, cv.TM_CCOEFF_NORMED)  # 得到匹配结果
    print("result", result)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
    print("max_val", max_val)
    threshold = 0.8
    # 取模板匹配程度大于80%的坐标
    local = np.where(result >= threshold)
    for tl in zip(*local[::-1]):  # *表示可选参数
        botton_right = (tl[0] + tw, tl[1] + th)
        cv.rectangle(target, tl, botton_right, (0, 0, 255), 2)
    cv.imshow("target", target)


if __name__ == '__main__':
   
    more_template_demo()
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()  # 关闭所有窗口

实验效果图:

Opencv-Python学习笔记(十二):模板匹配_第4张图片

整体工程代码:

 

import cv2 as cv
import numpy as np


# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def template_demo():
    tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/rabbit.jpg")  # 模板图片
    print("模板大小:", tpl.shape)
    target = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu22.jpg")  # 原图
    print("原图大小:", target.shape)
    cv.imshow("template", tpl)
    cv.imshow("target", target)
    methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED]  # 三种模板匹配方法
    th, tw = tpl.shape[:2]

    # result = cv.matchTemplate(target, tpl, cv.TM_SQDIFF_NORMED)  # 得到匹配结果
    # print("结果大小:", result.shape)
    # min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
    # print("min_val", min_val)
    # print("max_val", max_val)
    # print("min_loc", min_loc)
    # print("max_loc", max_loc)
    # tl = min_loc
    # br = (tl[0] + tw, tl[1] + th)
    # cv.rectangle(target, tl, br, (0, 0, 255), 2)  # tl为左上角坐标,br为右下角坐标,从而画出矩形
    # cv.imshow("target", target)
    for md in methods:
        print(md)
        result = cv.matchTemplate(target, tpl, md)  # 得到匹配结果
        print("结果大小:", result.shape)
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
        print("min_val", min_val)
        print("max_val", max_val)
        print("min_loc", min_loc)
        print("max_loc", max_loc)
        if md == cv.TM_SQDIFF_NORMED:  # cv.TM_SQDIFF_NORMED最小时最相似,其他最大时最相似
            tl = min_loc  # 左上角点位
        else:
            tl = max_loc

        br = (tl[0] + tw, tl[1] + th)
        cv.rectangle(target, tl, br, (0, 0, 255), 2)  # tl为左上角坐标,br为右下角坐标,从而画出矩形
        cv.imshow("match-" + np.str(md), target)
        # cv.imshow("match-" + np.str(md), result)


def more_template_demo():
    tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario_coin.jpg")  # 模板图片
    target = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario.jpg")  # 原图
    cv.imshow("template", tpl)
    cv.imshow("target", target)
    th, tw = tpl.shape[:2]
    result = cv.matchTemplate(target, tpl, cv.TM_CCOEFF_NORMED)  # 得到匹配结果
    print("result", result)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
    print("max_val", max_val)
    threshold = 0.8
    # 取模板匹配程度大于80%的坐标
    local = np.where(result >= threshold)
    for tl in zip(*local[::-1]):  # *表示可选参数
        botton_right = (tl[0] + tw, tl[1] + th)
        cv.rectangle(target, tl, botton_right, (0, 0, 255), 2)
    cv.imshow("target", target)


if __name__ == '__main__':
    # template_demo()
    more_template_demo()
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()  # 关闭所有窗口

 

你可能感兴趣的:(Python+opencv4,Python3,OpenCV4,Pycharm2019)