目录
1. 概述
模板匹配 (template matching)是属于特征识别的一种方法, 是一种最基本最原始的特征识别方法。
它研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。
最原始的方法往往具有一定的局限性, 模板匹配其自身的局限性主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。
2. 一些基本原理
首先来看什么是模板, 模板通俗的来讲就是一张小图像; 模板匹配就是在一张较大的图像上, 找到这个小模板,
我们可以通过一定的算法可以在图中找到它,并确定其坐标位置, 这就是模板匹配。
暂且以8位灰度图像为例,模板 T(m,n) 叠放在被搜索图 S(W,H) 上平移,模板和 被搜索图 重叠的那块区域
叫做子图 , 其中 i , j 分别为子图左上角 在被搜索图S上的坐标,搜索范围是:1≤ i ≤W-n,1≤ j ≤H-m, 也就是从
左上角开始搜索, 知道遇到边界为止, 然后在向下一行继续搜索 ,
直到遍历完整张图像, 就返回相似度最高的 result 。
那么我们是如何判别模板与原图像之间的相似度呢?
那就要用到下面的这几个公式了:(不局限于这几个, 我写的是opencv当中实现了的几种)
- 平方差匹配法
I : 输入图像 ; T : 模板; R : 匹配结果
其中的 x' 和 y' 指的就是模板左上角坐标; x,y 指的就是图像的左上角坐标,
二者相加, 也就是模板在图像当中的位置 ;即 (x+x' , y+ y')
这个公式就是一个简单的作差再平方的操作, 如果相似度高,则计算出来的值也就越小。
在相似度最高的时候, 值为0 。
- 相关匹配法01
这个方法和上面的相似, 只不过是由 像素值的平方差改成了 像素值的乘法, 得到的结果越大, 则表示
匹配的而程度越高, 0 则表示最坏的匹配结果。
如果说是平方差的话, 还是很好理解, 乘法能代表相似度么???
或者说我应当从 点乘的定义 来入手理解这个公式?
- 相关匹配法02
这类方法将 模板对其均值的相对值 与 图像对其均值的相对值 这二者之间进行匹配,
1 表示完美匹配, -1 表示最糟糕的匹配, 0 则表示没有任何的相关性;
(这个算法我也不是很理解....暂时先放在这里吧)
除了这三种算法的本身, OpenCV当中还有其归一化的形式, 即:
03 OpenCV的实现
模板匹配主要就是两个函数
首先是 cv2.matchTemplate ()
它的参数列表如下,
image : 就是被匹配的图片,
templ : 这个就是我们的模板
method : 这是要使用的方法, 也就是上面我们介绍的那几种。
关于函数的全部细节, 可以在这里找到:
https://docs.opencv.org/3.3.0/df/dfb/group__imgproc__object.html#ga586ebfb0a7fb604b35a23d85391329be
关于这个函数的返回值,官方的文档当中是这样描述的:
After the function finishes the comparison, the best matches can be found as global minimums (when TM_SQDIFF was used) or maximums (when TM_CCORR or TM_CCOEFF was used) using the minMaxLoc function.
这个函数的返回值 (result )是匹配到的目标图像,
当我们获得了这个结果之后, 接下来可以使用 cv2. minMaxLoc 来得到最佳的匹配位置;
cv2.minMaxLoc 的介绍如下 :
# 随便找一组图片来试一下
#
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
----- output ------
0.0 1.0 (0, 0) (304, 250)
所以, 这行代码的四个值 分别代表 : 最小值, 最大值, 最小值的位置, 最大值的位置
( ps : 如果你看不懂这里, 你可以先去看看代码 , 不懂再回来看, 或许会对你有一些帮助 )
上面我们说了 , 当你使用的方法是 TM_SQDIFF 或者是它的 NORMED(归一化)形式,那么你就
需要找最大值的位置, 这才是匹配到的东西;
当你 使用的是 TM_CCORR or TM_CCOEFF 以及归一化的形式时, 你要找最小值的位置, 这点要注意,
不要把算法和这个值搞混淆了。。
4 Demo_1
首先加载两张图片, 来两张最近比较火的芝麻街;
import cv2
the_temp = cv2.imread(r'images\sheet_template.jpg')
the_img = cv2.imread(r'images\sheet_image.jpg')
cv2.namedWindow('img', cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('template', cv2.WINDOW_AUTOSIZE)
cv2.imshow('img', the_img)
cv2.imshow('template', the_temp)
然后指定方法, 并调用 cv2.matchTemplate 函数
methods = [cv2.TM_SQDIFF_NORMED, cv2.TM_CCORR_NORMED,
cv2.TM_CCOEFF_NORMED]
the_method = methods[0]
result = cv2.matchTemplate(image=the_img, templ=the_temp,
method=the_method)
得到了 result , 其中包含有 min_val, max_val, min_loc, max_loc
我们接下来使用 cv2.minMaxLoc 来对其解析。
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
print(min_val, max_val, min_loc, max_loc)
min_val, max_val, min_loc, max_loc 的取值分别如下 :
0.001219973899424076 0.6712338328361511 (269, 37) (330, 146)
我们使用的是 cv2.TM_SQDIFF_NORMED , 所以我们要找 min_loc , 也就是 (269, 37) 。
这是匹配到的图像的左上角那个点的坐标, 我们还需要找到其右下角的点,然后使用 cv2.rectangle 来绘制一个方框,
右下角的点我们用这个 min_loc 的坐标加上 模板的 height 和 width 来得到。
left_top = min_loc
temp_height, temp_width = the_temp.shape[:2]
print(temp_height, temp_width)
right_bottom = (left_top[0] + temp_width, left_top[1] + temp_height)
print(right_bottom)
-----output -----
105 122
(391, 142)
到这里, 基本上就算是大功告成了
只需要使用 cvrestangle 来画个框就 ok 了。
# 后面的两个参数代表 color 和 thickness
cv2.rectangle(img=the_img, pt1=left_top, pt2=right_bottom, color=(0, 0, 255),
thickness=2)
cv2.imshow('match_result', the_img)
匹配多个物体