OpenCV-Python系列之模板匹配
我们在之前讨论OpenCV的轮廓以及直方图时已经接触过类似的匹配,事实上,它们原理基本上差不多,都是用一幅模板图像和原图进行匹配,从而找到原图中相应的地方,作为OpenCV中的一种最基本的目标识别的方法,模板匹配有其一定的作用,今天我们来具体的进行讨论。
模板匹配是一种用于在较大图像中搜索和查找模板图像位置的方法。为此,OpenCV带有一个函数cv.matchTemplate()。
它只是将模板图像滑动到输入图像上(就像在2D卷积中一样),然后在模板图像下比较模板和输入图像的拼图。
如果输入图像的大小为(WxH),而模板图像的大小为(wxh),则输出图像的大小将为(W-w + 1,H-h + 1)。得到结果后,可以使用cv.minMaxLoc()函数查找最大/最小值在哪。将其作为矩形的左上角,并以(w,h)作为矩形的宽度和高度,该矩形是模板的区域。
现在我们来进行分析,先来看第一个函数:
result=cv.matchTemplate(image, templ, method[, result[, mask]])
参数image:待搜索的图像(大图)
参数temple:搜索模板,需要和原图一样的数据类型且尺寸不能大于源图像
参数result:比较结果的映射图像,其必须为单通道,32位浮点型图像,如果原图(待搜索图像)尺寸为W*H,而temple尺寸为w*h,则result尺寸一定是
(W-w+1)*(H-h+1)
参数method:指定匹配方法,有如下几种:
CV_TM_SQDIFF:平方差匹配法;
CV_TM_SQDIFF_NORMED:归一化平方差匹配法;
CV_TM_CCORR:相关匹配法;
CV_TM_CCORR_NORMED:归一化相关匹配法;
CV_TM_CCOEFF:系数匹配法;
CV_TM_CCOEFF_NORMED:化相关系数匹配法。
用T表示模板图像,I表示待匹配图像,切模板图像的宽为w高为h,用R表示匹配结果,匹配过程如下图所示:
上述6中匹配方法可用以下公式进行描述:
(1)TM_SQDIFF:
该方法名为平方差匹配法,计算的公式如式(6.9)所示,这种方法利用平方差来进行匹配,当模板与滑动窗口完全匹配时计算数值为0,两者匹配度越低计算数值越大。
其中 表示模板图像, 表示原图像。
(2)TM_SQDIFF_NORMED:
该方法名为归一化平方差匹配方法,计算公式如式(6.10)所示,这种方法是将平方差方法进行归一化,使得输入结果缩放到了0到1之间,当模板与滑动窗口完全匹配时计算数值为0,两者匹配度越低计算数值越大。
(3)TM_CCORR:
该方法名为相关匹配法,计算公式如式(6.11)所示,这类方法采用模板和图像间的乘法操作,所以数值越大表示匹配效果越好,0表示最坏的匹配结果。
(4)TM_CCORR_NORMED:
该方法名为归一化相关匹配法,计算公式如式(6.12)所示,这种方法是将相关匹配法进行归一化,使得输入结果缩放到了0到1之间,当模板与滑动窗口完全匹配时计算数值为1,两者完全不匹配时计算结果为0。
(5)TM_CCOEFF:
该方法名为系数匹配法,计算公式如式(6.13)所示,这种方法采用相关匹配方法对模板减去均值的结果和原图像减去均值的结果进行匹配,这种方法可以很好的解决模板图像和原图像之间由于亮度不同而产生的影响。该方法中模板与滑动窗口匹配度越高计算数值越大,匹配度越低计算数值越小,并且该方法计算结果可以为负数。
其中:
(6)TM_CCOEFF_NORMED:
该方法名为归一化系数匹配法,计算公式如式(6.15)所示,这种方法将系数匹配方法进行归一化,使得输入结果缩放到了1到-1之间,当模板与滑动窗口完全匹配时计算数值为1,当两者完全不匹配时计算结果为-1。
再来看另一个函数:
minVal,maxVal,minLoc,maxLoc=cv.minMaxLoc(src [,mask])
函数功能:假设有一个矩阵a,现在需要求这个矩阵的最小值,最大值,并得到最大值,最小值的索引。咋一看感觉很复杂,但使用这个cv2.minMaxLoc()函数就可全部解决。函数返回的四个值就是上述所要得到的。
现在我们使用模板图像:
原图:
我们将尝试所有比较方法,以便我们可以看到它们的结果如何(如果使用cv.TM_SQDIFF作为比较方法,则最小值提供最佳匹配,其余的方法使用最大值进行最佳匹配),我们来看代码:
def template(): img = cv.imread('min.jpg', 0) img2 = img.copy() template = cv.imread('temp.jpg', 0) w, h = template.shape[::-1] # 列表中所有的6种比较方法 methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR', 'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED'] for meth in methods: img = img2.copy() method = eval(meth) # 应用模板匹配 res = cv.matchTemplate(img, template, method) min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) # 如果方法是TM_SQDIFF或TM_SQDIFF_NORMED,则取最小值 if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]: top_left = min_loc else: top_left = max_loc bottom_right = (top_left[0] + w, top_left[1] + h) cv.rectangle(img, top_left, bottom_right, 255, 2) plt.subplot(121), plt.imshow(res, cmap='gray') plt.title('Matching Result'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img, cmap='gray') plt.title('Detected Point'), plt.xticks([]), plt.yticks([]) plt.suptitle(meth) plt.show()
来看结果,cv.TM_CCOEFF方法:
cv.TM_CCOEFF_NORMED:
cv. TM_CCORR:
cv.TM_CCORR_NORMED:
cv.TM_SQDIFF:
cv.TM_SQDIFF_NORMED:
由此我们完成了OpenCV的模板匹配,它属于最简单的目标识别,对图像的要求比较高,当原图进行了旋转或者是缩放时,这种匹配方法经常会失效,这个大家可以自行实验,将原图放大或者是缩小,又或者是对图像进行一定角度的旋转,都会导致一定程度的失效,对于这种缺陷,我们在后面会介绍更为强大的特征匹配的方法。