单模板匹配
import cv2 #opencv读取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline
def cvshow(name, ndarray):
img = cv2.imshow(name, ndarray)
cv2.waitKey(0)
cv2.destroyAllWindows()
模板匹配是指在当前图像 A 内寻找与图像 B 最相似的部分,一般将图像 A 称为输入图像,
将图像 B 称为模板图像。模板匹配的操作方法是将模板图像 B 在图像 A 上滑动,遍历所有像
素以完成匹配。
API
result = cv2.matchTemplate(image, templ, method[, mask ] )
- image 为原始图像,必须是 8 位或者 32 位的浮点型图像。
- templ 为模板图像。它的尺寸必须小于或等于原始图像,并且与原始图像具有同样的类型。
- method 为匹配方法。有六种方法,如下图
- mask 为模板图像掩模。它必须和模板图像 templ 具有相同的类型和大小。通常情况下该值使用默认值即可。当前,该参数仅支持 TM_SQDIFF 和 TM_CCORR_NORMED 两个值。
- 返回值 result 是由每个位置的比较结果组合所构成的一个结果集,类型是单通道 32 位浮点型。如果输入图像(原始图像)尺寸是 WH,模板的尺寸是 wh,则返回值的大小为(W-w+1)*(H-h+1)。
下图是计算公式:
method 的值为 cv2.TM_SQDIFF 和 cv2.TM_SQDIFF_NORMED 时,result 值为 0 表示匹配度最好,值越大,表示匹配度越差;
method 的值为 cv2.TM_CCORR、cv2.TM_CCORR_NORMED、cv2.TM_CCOEFF和cv2.TM_CCOEFF_NORMED 时,result 的值越小表示匹配度越差,值越大表示匹配度越好
在查找最佳匹配时,首先要确定使用的是何种 method,然后再确定到底是查找最大值,还是查找最小值。查找最值(极值)与最值所在的位置,可以使用 cv2.minMaxLoc()函数实现。
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc( src [, mask] )
- src 为单通道数组。
- minVal 为返回的最小值,如果没有最小值,则可以是 NULL(空值)。
- maxVal 为返回的最大值,如果没有最小值,则可以是 NULL。
- minLoc 为最大值的位置,如果没有最大值,则可以是 NULL。
- maxLoc 为最大值的位置,如果没有最大值,则可以是 NULL。
- mask 为用来选取掩模的子集,可选项。
lena = cv2.imread("lena.jpg")
template = cv2.imread("template.png")
# 模板匹配
res = cv2.matchTemplate(lena, template, cv2.TM_CCOEFF_NORMED)
# 相关系数则是值越大越匹配
minVal, maxVal, minLoc, maxloc = cv2.minMaxLoc(res)
img = cv2.rectangle(lena, (maxloc[0], maxloc[1]), (maxloc[0]+template.shape[1], maxloc[1]+template.shape[0]), (255, 0, 0), 2)
cvshow("lena", img)
Img = cv.rectangle( img, pt1, pt2, color[, thickness])
- img 表示要标记的目标图像
- pt1 是矩形的顶点
- pt2 是 pt1 的对角顶点
- color 是要绘制矩形的颜色或灰度级(灰度图像)
- thickness 是矩形边线的宽度
多模板匹配
有些情况下,要搜索的模板图像很可能在输入图像内出现了多次,这时就需要想匹配多个结果,使用函数 cv2.minMaxLoc()是无法实现的,需要利用np.where()进行处理。要找出多个匹配结果。
a = np.arange(12).reshape(3, 4)
b = np.where(a>2)
print(a)
print(b)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
(array([0, 1, 1, 1, 1, 2, 2, 2, 2], dtype=int64), array([3, 0, 1, 2, 3, 0, 1, 2, 3], dtype=int64))
可以看出,np.where()返回值为一个元组,每个元素为一个array,array中的元素代表索引。因为a为二维矩阵,所以返回两个数组,一个代表行索引,一个代表列索引,行列索引一一匹配成一个坐标
函数 zip()用可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
# 将原始图像扩充为4份进行多模板匹配
lena = cv2.imread("lena.jpg")
mid = np.hstack((lena, lena))
res = np.vstack((mid, mid))
cvshow("res", res)
# 读入模板
template = cv2.imread("template.png")
# 模板匹配
result = cv2.matchTemplate(res, template, cv2.TM_CCOEFF_NORMED)
width, height = template.shape[1], template.shape[0]
# 阈值筛选
filter = np.where(result>0.95)
# 打包行列索引对并遍历
for item in zip(*filter[::-1]): #filter[::-1]表示行列数组互换位置,原来是第一个数组为行索引,第二个数组为列索引,但是列索引才表示x,行索引表示y
print(item)
cv2.rectangle(res, (item[0], item[1]), (item[0]+width, item[1]+height), (0, 255, 0), 1)
cvshow("res", res)
(127, 111)
(390, 111)
(127, 374)
(390, 374)
其实按这个道理来说,应该会有很多区域可以匹配成功,但是每个图象里怎么就一个框呢?其实是都画出来了,尤其是最相似的区域里有很多框,只不过都重合在一起了,框的线条粗细才1,可以把阈值调低看框是不是会变很粗