程式語言:Python
Package:opencv-python
官方網址
OpenCV-Python Tutorials
功能:找出圖片中類似的東西
表示 image, 表示 template, 表示 result
Template Matching
Package:opencv-python
官方網址
OpenCV-Python Tutorials
功能:找出圖片中類似的東西
演算法
- 輸入兩張影像,分別為 image、template
- 不斷滑動 template,得到 image 上各個位置的比較值,比較值代表相似程度
然後將 image 左上角位置,作為 result 比較值的存放位置 - 完成後可得到 result
可用 minMaxLoc() 函式,找出結果圖的最大或最小值,定位出搜尋位置
限制
- 物體有旋轉時,會找不到
- 物體大小改變時,會找不到
result = cv2.matchTemplate(image, templ, method[, result])
- 參數
- image
- 被尋找的圖片
- 必須為 8-bit or 32-bit
- templ
- 尋找的物品圖片
- size 不能大於 image,且格式需一致
- method
- 比對的方法
- result
- 比較的結果,格式為 numpy.ndarray (dtype=float32)
- 可傳入想儲存結果的 array
- 因 image 大小為
且 templ 為 ,所以大小為
比對方法
- CV_TM_SQDIFF
- 平方差,越小越相似
- CV_TM_SQDIFF_NORMED
- 正規化平方差,越小越相似
- 保證當 pixel 亮度都乘上同一係數時,相似度不變
- CV_TM_CCORR
- 相關係數,越大越相似
- CV_TM_CCORR_NORMED
- 正規化相關係數,越大越相似
- 保證當 pixel 亮度都乘上同一係數時,相似度不變
- CV_TM_CCOEFF
- 去掉直流成份的相關係數,越大越相似
where- CV_TM_CCOEFF_NORMED
- 正規化 去掉直流成份的相關係數
- 保證當 pixel 亮度都乘上同一係數時,相似度不變
- 計算出的相關係數被限制在了 -1 到 1 之間
- 1 表示完全相同
- -1 表示亮度正好相反
- 0 表示没有線性相關
where
範例
程式碼
結果
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
- img = cv2.imread('image.png')
- img2 = img.copy()
- template = cv2.imread('template.jpg')
- w = template.shape[1]
- h = template.shape[0]
- # 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']
- for meth in 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(121),plt.imshow(res,cmap = 'gray')
- plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
- plt.subplot(122),plt.imshow(img)
- plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
- plt.suptitle(meth)
- plt.show()
程式碼
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
- def getPoints(img, template, threshold, method=cv2.TM_CCOEFF_NORMED):
- result = cv2.matchTemplate(img, template, method)
- if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
- loc = np.where(result <= threshold) #回傳為 y, x
- else:
- loc = np.where(result >= threshold) #回傳為 y, x
- pts = zip(*loc[::-1])
- return removeSame(pts, min(template.shape[0], template.shape[1]))
- # 去掉太過接近的座標
- def removeSame(pts, threshold):
- elements = []
- for x,y in pts:
- for ele in elements:
- if ((x-ele[0])**2 + (y-ele[1])**2) < threshold**2:
- break
- else:
- elements.append((x,y))
- return elements
- def drawRectangle(img, pts, w, h, color=(0,0,255), lineW=2):
- for pt in pts:
- cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), color, lineW) #因後面會反向顏色順序,所以這邊顯示紅色要反向
- if __name__ == '__main__':
- img = cv2.imread('image.png')
- img_result = img.copy()
- template = cv2.imread('template.png')
- w = template.shape[1]
- h = template.shape[0]
- threshold = 0.4
- elements = getPoints(img, template, threshold)
- total_n = len(elements)
- drawRectangle(img_result, elements, w=template.shape[1], h=template.shape[0])
- # 逆時針旋轉 90
- template_r90 = cv2.transpose(template)
- cv2.flip(template_r90, 0)
- pts1 = getPoints(img, template_r90, threshold)
- # 順時針旋轉 90
- template_r90 = cv2.transpose(template)
- cv2.flip(template_r90, 1)
- pts2 = getPoints(img, template_r90, threshold)
- elements = removeSame(pts1+pts2, w)
- total_90 = len(elements)
- drawRectangle(img_result, elements, w=template_r90.shape[1], h=template_r90.shape[0], color=(0,255,0))
- plt.subplot(121)
- plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) #因 openCV 記錄的顏色順序是反過來的
- plt.title('Original'), plt.xticks([]), plt.yticks([])
- plt.subplot(122)
- plt.imshow(cv2.cvtColor(img_result, cv2.COLOR_BGR2RGB)) #因 openCV 記錄的顏色順序是反過來的
- plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
- plt.suptitle('Total:{}\nnomral:{}, 90:{}'.format(total_n+total_90, total_n, total_90))
- plt.show()
- # cv2.imwrite('result.png', img_result)
參考
Template MatchingTemplate Matching
留言
張貼留言