aircv是網易放出的小開源專案,應該也是現在做簡單影像匹配被引用最多的專案了。上一篇做了如何使用aircv之find_template的描述,然而,它並不算是一個成熟的專案,裡面的小坑不少,有待改進。今天先做個程式碼邏輯的解析。
核心函式find_template 與 find_all_template
find_template函式是返回第一個最匹配的結果(位置未必在最上面),而find_all_template是返回所有大於指定置信度的結果。
比如要在思否頁面截圖中找
結果如如下圖所示:
我們深入進去看一下程式碼,就會發現find_template是這樣寫的:
def find_template(im_source, im_search, threshold=0.5, rgb=False, bgremove=False):
'''
@return find location
if not found; return None
'''
result = find_all_template(im_source, im_search, threshold, 1, rgb, bgremove)
return result[0] if result else None
好傢伙! 直接呼叫find_all_template,然後取返回值的第一個。。。
所以find_all_template才是真正的核心,我們排除掉無關程式碼,來看一下最關鍵的部分:
def find_all_template(im_source, im_search, threshold=0.5, maxcnt=0, rgb=False, bgremove=False):
# 匹配演算法,aircv實際上在程式碼裡寫死了用CCOEFF_NORMED,大部分測試的效果,也確實是這個演算法更好
method = cv2.TM_CCOEFF_NORMED
# 獲取匹配矩陣
res = cv2.matchTemplate(im_source, im_search, method)
w, h = im_search.shape[1], im_search.shape[0]
result = []
while True:
# 找到匹配最大最小值
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
if max_val < threshold:
break
# 計算中心點
middle_point = (top_left[0]+w/2, top_left[1]+h/2)
# 計算四個角的點,存入結果集
result.append(dict(
result=middle_point,
rectangle=(top_left, (top_left[0], top_left[1] + h), (top_left[0] + w, top_left[1]), (top_left[0] + w, top_left[1] + h)),
confidence=max_val
))
# 把最匹配區域填充掉,再繼續查詢下一個
cv2.floodFill(res, None, max_loc, (-1000,), max_val-threshold+0.1, 1, flags=cv2.FLOODFILL_FIXED_RANGE)
return result
其中cv2.matchTemplate
是opencv的方法,它的返回值是個矩陣,相當於用小圖在大圖上滑動,從左上角開始,每次移動一個畫素,然後計算一個匹配結果,最終形成結果矩陣。
結果矩陣大小應該是: (W - w + 1) x (H - h + 1),其中W,H是大圖的寬高, w和h是小圖的寬高。
這個矩陣中最大值的那個點,就表示小圖的左上角對在這個位置時,匹配度最高,由此得到第一個匹配結果。
最後cv2.floodFill
的作用,是把結果矩陣最大值這塊區域用別的數字填充掉,這樣就可以查詢下一個最大值了,而且也避免了區域重疊的現象(要不然下一個最大值,有可能在剛剛找到的區域裡面)。
幾個小問題
- 不支援灰度圖和帶透明通道的圖
其實opencv的matchTemplate本來只支援灰度圖,但大多數情況下,我們都是查詢彩色圖,所以aircv封裝的時候,把bgr三個彩色通道做了分離,分別呼叫matchTemplate,然後再合併結果。
但這個封裝沒有相容本來就是灰度圖的情況,竟然會出錯,而且如果源圖帶透明通道也會出錯,為此,我專門提交了一個PR,但一直沒有處理,看來專案已經沒人維護了。
- floodFill貌似有點興師動眾了,numpy的切片應該可以實現
這個回頭寫段小程式碼驗證一下 - 不能處理圖片的縮放
模板圖片和原圖中的內容,並不一定大小嚴格一致的,那麼就需要做一些縮放處理,重複嘗試。 - 影像匹配效果不理想
如果是完全一致的圖,find_template的效果非常好,但當需要模糊匹配時,某些人眼一看就相同的影像,是無法被識別出來的,有點時候又會誤識別,這可能需要從兩方面改進:
-- 調整演算法:採用SIFT之類的特徵點檢測演算法,可以解決大小和角度都不一致的影像匹配問題;Halcon等商業軟體,採用了shape based matching,匹配效果更佳。
-- 加入資訊:有時候除了模板影像本身,我們也許知道更多的資訊,比如影像可能出現的位置範圍,影像周邊區域的樣式等等,來幫助提升識別準確度,減少誤識和漏識。