1. 简述
为了让大家不至于看到后面的公式就退却,我先简单描述一下模板匹配的原理:
- 简单来说,模板匹配就是你拿一个模板(图片)在目标图片上依次滑动,每次计算模板与模板下方的子图的相似度,最后我们就计算出了非常多的相似度;
- 如果你只是单个目标的匹配,那你只需要取相似度最大值所在的位置就可以得出匹配位置;
- 如果你要匹配多个目标,那就设定一个阈值,就是说,只要咋俩的相似度大于比如0.8,我就认为你是我要匹配的目标。
2. 原理
2.1 计算步骤
- 你有一张模板图像和一张较大的待搜索图像,模板匹配是一种用于在较大图像中搜索和查找模板图像位置的方法。
- 具体就是将模板图像滑动到输入图像上(就像在卷积操作一样),然后在模板图像下比较模板和输入图像的子图的相似度(以下会介绍几种不同的相似度度量方法)。
- 它返回一个灰度图像,其中每个像素表示该像素的邻域与模板匹配的相似度。如果输入图像的大小
(WxH)
和模板图像的大小(wxh)
,则输出图像的大小将为(W-w + 1,H-h + 1)
。 - 获得相似度图像之后,在其上查找最大相似度所在的像素。将其作为匹配区域矩形的左上角,并以
(w,h)
作为矩形的宽度和高度。该矩形是与模板匹配的区域。 - 下面放da张图,免得大家看得太枯燥。大概用省略号的方式表示滑动操作:
2.2 相似度度量指标
- 差值平方和匹配
CV_TM_SQDIFF
- 标准化差值平方和匹配
CV_TM_SQDIFF_NORMED
- 相关匹配
CV_TM_CCORR
- 标准相关匹配
CV_TM_CCORR_NORMED
- 相关匹配
CV_TM_CCOEFF
- 标准相关匹配
CV_TM_CCOEFF_NORMED
下面的描述多数是公式,其中表示模板图像,表示目标图像$, 表示相似度矩阵
为了简化表示,下述公式仅表示模板与图像中某个位置的相似度函数
2.2.1 差值平方和匹配 CV_TM_SQDIFF
- 原理: 计算模板与某个子图的对应像素的差值平方和。
- 越相似该值越小
其中,是相对于目标大图I左上角的坐标,是相对于模板或者当前子图的左上角的坐标,那么就是当前子图上的某个点相对于目标大图的绝对坐标。
2.2.2 标准化差值平方和匹配 CV_TM_SQDIFF_NORMED
- 标准化的差值平方和
- 越相似该值越小
这种标准化操作可以保证当模板和图像各个像素的亮度都乘上了同一个系数时,相关度不发生变化。也就是说当和变为和时,不发生变化。
2.2.3 相关匹配 CV_TM_CCORR
- 模板与子图对应位置相乘,可以将其看作是差值平方和平方项展开之后中间的那个项
- 越相似该值越大
2.2.4 标准相关匹配 CV_TM_CCORR_NORMED
- 标准化的相关匹配,
- 和标准化差值平方和匹配类似,都是去除了亮度线性变化对相似度计算的影响。可以保证图像和模板同时变亮或变暗k倍时结果不变。
- 越相似该值越大
2.2.5 相关匹配 CV_TM_CCOEFF
- 这种方法也叫做相关匹配,但是,这里是把图像和模板都减去了各自的平均值,使得这两幅图像都没有直流分量。
- 越相似该值越大
2.2.6 标准相关匹配 CV_TM_CCOEFF_NORMED
- 具体的说,就是在减去了各自的平均值之外,还要各自除以各自的方差。
- 经过减去平均值和除以方差这么两步操作之后,无论是我们的目标图像还是模板都被标准化了,这样可以保证图像和模板分别改变光照不影响计算结果。
- 越相似该值越大
- 计算出的相关系数被限制在了 -1 到 1 之间,1 表示完全相同,-1 表示两幅图像的亮度正好相反,0 表示两幅图像之间没有线性关系。
3. python实战
3.1 单模板匹配单个目标(同时测试不同的相似度度量指标)
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('image3.png', 0)
img2 = img.copy()
template = cv2.imread('roi3.png', 0)
w, h = template.shape[::-1]
# All the 6 methods for comparison in a list
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
plt.subplot(4, 2, 1)
plt.imshow(template, cmap='gray')
plt.title('Template Image'), plt.xticks([]), plt.yticks([])
plt.subplot(4, 2, 2)
plt.imshow(img, cmap='gray')
plt.title('Target Image'), plt.xticks([]), plt.yticks([])
for i, meth in enumerate(methods):
img = img2.copy()
method = eval(meth)
# Apply template Matching
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
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, 2)
plt.subplot(4, 2, i+3)
plt.imshow(img, cmap='gray')
plt.title('Matching Result by {}'.format(meth)), plt.xticks([]), plt.yticks([])
plt.show()
3.2 单模板匹配多个目标
import cv2
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv2.imread('image1.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('roi1.png', 0)
w, h = template.shape[::-1]
plt.subplot(131)
plt.imshow(template)
plt.subplot(132)
plt.imshow(img_rgb)
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (255, 255, 255), 2)
plt.subplot(133)
plt.imshow(img_rgb)
plt.show()
3.3 多模板匹配多个目标
import cv2
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv2.imread('image4.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template1 = cv2.imread('roi4_1.png', 0)
template2 = cv2.imread('roi4_2.png', 0)
template3 = cv2.imread('roi4_3.png', 0)
template_list = ['roi4_1.png', 'roi4_2.png', 'roi4_3.png']
w1, h1 = template1.shape[::-1]
w2, h2 = template1.shape[::-1]
w3, h3 = template1.shape[::-1]
plt.subplot(131)
plt.imshow(template1)
plt.subplot(132)
plt.imshow(template2)
plt.subplot(133)
plt.imshow(template3)
plt.show()
for i in range(len(template_list)):
template = cv2.imread(template_list[i], 0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.65
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
gray = int(255/(i+1))
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (gray, gray, gray), 2)
plt.imshow(img_rgb)
plt.show()
4. 算法优化
4.1 速度优化
这一块我没有看过opencv的优化方法,但是可想而知,直接滑动窗口效率是极其低下的,他们一定不会用这种方法。
- 我看到的一些相关文章,我没仔细看,放个链接
- 另外我自己的一点想法,因为之前看过卷积网络的卷积优化,而卷积操作的原理就是滑动窗口,那么,就可以将滑动窗口操作改成矩阵运算,具体可以看我之前写过的github博客:各种卷积操作及其矩阵运算。
4.2 精度优化
- 多尺度模板匹配
- 旋转目标模板匹配
- 非极大值抑制