[OpenCV_Python]模板匹配

1.Template Matching(模板匹配)

模板匹配是一種在較大圖像中搜索和查找模板圖像位置的方法。OpenCV提供了一個函數cv2.matchTemplate()。它只是在輸入圖像上滑動模板圖像(如在2D卷積中),并比較模板圖像下的輸入圖像的模板和補丁。在OpenCV中實現了幾種比較方法。它返回一個灰度圖像,其中每個像素表示該像素的鄰域與模板匹配的程度。

假設輸入圖像的大小(WxH)且模板圖像的大小(wxh),則輸出圖像的大小為(W-w + 1,H-h + 1)。獲得結果后,可以使用cv2.minMaxLoc()函數查找最大/最小值的位置。將其作為矩形的左上角,并將(w,h)作為矩形的寬度和高度。那個矩形是你的模板區域匹配后得到的區域。

2.匹配實例

找出一張電路的圖像中,指定的芯片,并標記出來

import cv2
import numpy as np

# 讀取名稱為 p20.png 的圖片,并轉成黑白
img = cv2.imread("/home/yhch/Pictures/P20.png",1)
gray = cv2.imread("/home/yhch/Pictures/P20.png",0)
cv2.imshow('pic',gray)

# 讀取需要檢測的模板圖片(黑白)
img_template = cv2.imread("/home/yhch/Pictures/P20_temp.png",0)
# 得到圖片的高和寬
w, h = img_template.shape[::-1]
print(w,h)


# 模板匹配操作
res = cv2.matchTemplate(gray,img_template,cv2.TM_SQDIFF)

# 得到最大和最小值得位置

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = min_loc #左上角的位置
bottom_right = (top_left[0] + w, top_left[1] + h) #右下角的位

# 在原圖上畫矩形
cv2.rectangle(img,top_left, bottom_right, (0,0,255), 2)

# 顯示原圖和處理后的圖像
cv2.imshow("img_template",img_template)
cv2.imshow("processed",img)
cv2.waitKey(0
運行效果

3. API

  • 用法
cv2.matchTemplata(img_big,img_temp,cv2.method)

img_big:在該圖上查找圖像
img_temp:待查找的圖像,模板圖像
method: 模板匹配的方法
  • 關于參數 method:
method introduce
CV_TM_SQDIFF 平方差匹配法 該方法采用平方差來進行匹配;最好的匹配值為0;匹配越差,匹配值越大
CV_TM_CCORR 相關匹配法 該方法采用乘法操作;數值越大表明匹配程度越好。
CV_TM_CCOEFF 相關系數匹配法 1表示完美的匹配;-1表示最差的匹配。
CV_TM_SQDIFF_NORMED 歸一化平方差匹配法
CV_TM_CCORR_NORMED 歸一化相關匹配法
CV_TM_CCOEFF_NORMED 歸一化相關系數匹配法

4.與多個對象匹配的模板

在上一實例,搜索了芯片的圖像,該圖像僅在圖像中出現一次。如果正在搜索的圖像中有多個對象出現,cv2.minMaxLoc()就不會為提供模板圖像所有位置。在這種情況下,可以使用閾值來匹配多個對象。在這個例子中,使用了游戲Mario的截圖,會在其中找到硬幣并標記出來。

import cv2
import numpy as np

im_rgb = cv2.imread('/home/yhch/Pictures/mario.png')

cv2.imshow('im',im_rgb)
im_gray = cv2.cvtColor(im_rgb,cv2.COLOR_BGR2GRAY)

template = cv2.imread('/home/yhch/Pictures/mario_coin.png',0)
w,h = template.shape[::-1]

res = cv2.matchTemplate(im_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where(res>=threshold)


for pt in zip(*loc[::-1]):
    cv2.rectangle(im_rgb,pt,(pt[0]+w,pt[1]+h),(0,0,255),1)

cv2.imshow('res.png',im_rgb)
cv2.waitKey(0
運行效果
img
說明
  • python3數組的倒序 a[::-1]

    list = [1,2,3,4,5,6]
    print(list[::-1])
    [6, 5, 4, 3, 2, 1]
    arry = ([1,2,3],[4,5,6])
    print(arry[::-1])
    ([4, 5, 6], [1, 2, 3])

  • python3 zip() 函數

zip() 函數用于將可迭代的對象作為參數,將對象中對應的元素打包成一個個元組,然后返回由這些元組組成的對象,這樣做的好處是節約了不少的內存。

我們可以使用 list() 轉換來輸出列表。

如果各個迭代器的元素個數不一致,則返回列表長度與最短的對象相同,利用 * 號操作符,可以將元組解壓為列表。

>>> a = [1,2,3]
>>> b = [4,5,6]
>>> c = ['yhch','apple']
>>> obj_ziped = zip(a,b,c) #返回值為一個對象
>>> print(obj_ziped)
    <zip object at 0x7f35ac530b88> # 對象

>>> for i in obj_ziped:
    ...     print(i)
    ... 
    (1, 4, 'yhch')
    (2, 5, 'apple')
>>> 
  • 不同閥值下匹配出的數

識別網易易盾滑動驗證碼

threshould = 0.4

103個,好多位置并不準確,當然有一個最準確的位置就在其中

img

threshould = 0.57

只有1個,并且準確的找出位置


img

threshould = 0.6

一個都沒有


img

網易易盾驗證碼的識別

寫這篇教程不是空穴來風,是我在學習爬蟲的過程中,遇到滑動驗證碼的識別,遇到了問題
可以看到,已經能夠識別出準確的位置,配和selenium滑動滑塊,就能破解滑動驗證碼了。
但在實際過程中,是不知道會出現什么畫面的驗證碼,不同的圖像,顏色,透明度是不一樣,閥值也就不一樣。而只有找到準確的閥值才能得到準確的位置。

如何動態的分析不同圖片的閥值
  • 從上面的實例可以發現閥值越小,結果就越多,閥值越大,結果越少,甚至沒有結果。閥值介于[0,1],因此通過循環用二分法去試一試,當結果有且只有一個的時候,得到的threshould便是我們想要的,再通過threshold獲取位置信息
* 閾值始終為區間左端和右端的均值,即 threshhold = (R+L)/2;
* 如果當前閾值查找結果數量大于1,則說明閾值太小,需要往右端靠近,即左端就增大,即L += (R - L) / 2;
* 如果結果數量為0,則說明閾值太大,右端應該減小,即R -= (R - L) / 2;
* 當結果數量為1時,說明閾值剛
代碼實現
img = cv2.imread("/home/yhch/Pictures/target.jpg",1)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread('/home/yhch/Pictures/template.png', 0)

w, h = template.shape[::-1]

res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)

L = 0
R = 1
count = 0
while count < 20:
    threshold = (L+R)/2
    count += 1
    loc = np.where(res >= threshold)
    if len(loc[0]) > 1:
        L += (R-L) /2
    elif len(loc[0]) == 1:
        pt = loc[::-1]
        print('目標區域的左上角坐標:',pt[0],pt[1])
        print('次數:',count)
        print('閥值',threshold)
        break
    elif len(loc[0]) < 1:
        R -= (R-L) / 2


cv2.rectangle(img,pt,(pt[0]+w,pt[1]+h),(34,139,34),2)

cv2.imshow("img_template",template)
cv2.imshow("processed",img)

cv2.waitKey(0)
img

小結

通過OPencv的模板識別功能,并且用二分法對針實際場景進行二次開發,后面會利用這里的知識點,對網易易盾滑動驗證碼進行破解。代碼已上傳到github(點這里)

關于作者

個人博客: https://yhch.xyz

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。