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
運行效果
說明
-
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個,好多位置并不準確,當然有一個最準確的位置就在其中
threshould = 0.57
只有1個,并且準確的找出位置
threshould = 0.6
一個都沒有
網易易盾驗證碼的識別
寫這篇教程不是空穴來風,是我在學習爬蟲的過程中,遇到滑動驗證碼的識別,遇到了問題
可以看到,已經能夠識別出準確的位置,配和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)
小結
通過OPencv的模板識別功能,并且用二分法對針實際場景進行二次開發,后面會利用這里的知識點,對網易易盾滑動驗證碼進行破解。代碼已上傳到github(點這里)
關于作者
個人博客: https://yhch.xyz