利用OpenCV的函数matchTemplate()实现在图像中寻找、检索、搜索模板图像【图像模板匹配】
在博文 https://www.hhai.cc/thread-220-1-1.html 中我们利用直方图的反向投影原理可以寻找图像中具有某个直方图特征的部分。
但是有时候会遇到这样的情况:图像中某个部分的直方图与某个特征的直方图相同,但内容却完全不一样,这个时候我们通过直方图的反向投影原理找到的部分就不是我们希望找到的。
在这种情况下,我们可以通过直接比较图像的像素值来检索是否原图中与模板图像存在相同内容,这种通过比较像素值来寻找相同内容的方法叫作图像模板匹配。
OpenCV提供了函数matchTemplate()来实现图像模板匹配。
具体实现方法如下:
①在待匹配图像中选取与模板图像尺寸相同的滑动窗口,将模板图像的左上角作为锚点(关于滑动窗口锚点的概念,可以参考博文 https://www.hhai.cc/thread-177-1-1.html),然后去按某种模板匹配算法计算匹配程度值,这个值就作为锚点对应于原图中的匹配程度值,并计录在输出矩阵“result”中。
②窗口滑动顺序为从左上角开始向右滑动,滑动到最右边后向下滑动一行,然后从最左侧重新开始滑动。
设原图像的尺寸为W×H,模板图像的尺寸为w×h,则基于这样的滑动顺序、锚点的选择、以及模板图像本来有一定的宽度和高度,显然匹配程度值记录矩阵“result”的尺寸只需要(W-w+1)×(H-h+1)就可以了。有些朋友可能会问,真的是显然么?我怎么不觉得是显然呢?如果您觉得不是显然,那么您自己举个简单的例子在草稿纸上模拟下滑动过程就很容易明白了哈。如果实在是搞不明白,可以找昊虹君有偿咨询哈。
函数matchTemplate()的原型如下:
void cv::matchTemplate(InputArray image,InputArray templ,OutputArray result,int method,InputArray mask = noArray() )
result=cv.matchTemplate(image, templ, method[, result[, mask]])
参数意义如下:
image—待匹配模板的原图像。
templ—模板图像。
method—模板匹配时需要计算窗口覆盖区域与原图的匹配程度,可以用不同的方法,不同的方法对应于method不同的以值。各取值对应的计算式如下表所示:
result—模板匹配结果矩阵,这里面每一个点的值代表原图中某一点与锚点对齐时,用参数method选取的匹配程度计算方法所计算出的匹配程度值。设原图像的尺寸为W×H,模板图像的尺寸为w×h,则输出矩阵“result”的尺寸为(W-w+1)×(H-h+1),具体为什么是这个尺寸,我在上面的叙述中已经解释了这个问题。
mask—模板图像的掩码矩阵。它必须具有与templ具有相同的尺寸。它要么与模板图像具有相同数量的通道数,要么只有一个通道。如果数据类型为CV_8U,则掩码被解释为二进制掩码,这意味着仅使用掩码为非零的元素参与计算,并且与实际掩码值无关(权重等于1)。如果数据类型为 CV_32F,掩码值用作权重。
附函数matchTemplate()的Python示例代码:
# -*- coding: utf-8 -*-
# 出处:昊虹AI笔记网(hhai.cc)
# 用心记录计算机视觉和AI技术# OpenCV的版本为4.4.0# -*- coding:utf-8 -*-
import cv2 as cv
import sysif __name__ == '__main__':# 读取图像并判断是否读取成功image = cv.imread('E:/material/images/histogram_back_projection/lml-02.jpg')template = cv.imread('E:/material/images/histogram_back_projection/lml-template.jpg')if image is None or template is None:print('Failed to read matchTemplate.jpg or match_template.jpg.')sys.exit()cv.imshow('image', image)cv.imshow('template', template)# 计算模板图片的高和宽h, w = template.shape[:2]# 进行图像模式匹配result = cv.matchTemplate(image, template, method=cv.TM_CCOEFF_NORMED)min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)# 计算图像左上角、右下角坐标并画出匹配位置left_top = max_locright_bottom = (left_top[0] + w, left_top[1] + h)cv.rectangle(image, left_top, right_bottom, 255, 2)cv.imshow('Matching results', image)cv.waitKey(0)cv.destroyAllWindows()
代码运行结果如下:
从上面的运行结果可以看出,我们在原图中找到了待匹配图像,并且把它标注了出来。