目标:使用模板匹配方法在图片里寻找目标对象,学会使用cv.matchTemplate(), cv.minMaxLoc()函数
模板匹配算法是从一个大图像里找到感兴趣的目标对象,OpenCV提供函数cv.matchTemplate()来实现相关的功能。模板匹配的原理其实很简单,就是不断地在原图中移动模板图像去比较,有6种不同的比较方法:
平方差匹配CV_TM_SQDIFF:用两者的平方差来匹配,最好的匹配值为0
归一化平方差匹配CV_TM_SQDIFF_NORMED
相关匹配CV_TM_CCORR:用两者的乘积匹配,数值越大表明匹配程度越好
归一化相关匹配CV_TM_CCORR_NORMED
相关系数匹配CV_TM_CCOEFF:用两者的相关系数匹配,1表示完美的匹配,-1表示最差的匹配
归一化相关系数匹配CV_TM_CCOEFF_NORMED
这些方法的对比代码可到源码处查看。模板匹配也是应用卷积来实现的:假设原图大小为W×H,模板图大小为w×h,那么生成图大小是(W-w+1)×(H-h+1),生成图中的每个像素值表示原图与模板的匹配程度。
由于这个模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。所以一般用来相同目标物品在不同图片里寻找,或者同一类型图片的查找。比如下面的例子,就是用来寻找不同人的身份证的有效日期的位置:
#python 3.7.4,opencv4.1
#蔡军生 https://blog.csdn.net/caimouse/article/details/51749579
#
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('cnid.png',0)
img2 = img.copy()
template = cv2.imread('cnid1.png',0)
w, h = template.shape[::-1]
#6种比较方法
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
for meth in methods:
img = img2.copy()
method = eval(meth)#这里把字符串表达式求值,返回宏表达的数值
#匹配比较
res = cv2.matchTemplate(img,template,method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
#TM_SQDIFF 或 TM_SQDIFF_NORMED取最小值有效
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img,top_left, bottom_right, 255, 3)#画白色矩形作为标记
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()
结果输出如下:
输入搜索的目标
最后输出的结果和在原图上标记为白色
通过这样的方式,就可以定位有效日期所在位置,然后再根据位置进行有效日期的截图,再进入后一阶段进行处理和识别。
如果在一个图片里有多个相同的目标,就需要使用下面的代码来解释搜索结果:
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
这里使用阈值进行分割,然后zip函数遍历所有值。