【計算機視覺(五)】模板匹配

前請提要

前三期(【計算機視覺(二)】常用顏色空間及其轉換)、【計算機視覺(三)】形態學處理【計算機視覺(四)】輪廓檢測)在介紹基本知識的同時穿插了一個很naive的檢測車牌位置的方法,對參數設置有很強的依賴。后續會慢慢涉及到高級的方法。

本期內容

本期作為一個引子介紹模板匹配法,引出后來的Cascade檢測算法。

一、全等模板

car_test.jpg

假如我們要在上面這張固定的圖上找出左邊這輛車的位置,一個很簡單的方法就是我們可以先人工把左邊這輛車用“截圖”工具裁剪出來。
template.jpg

然后拿著這張圖在大圖上找,一開始,我們把模板圖放到大圖的左上角,看它們是不是所有像素值都相等,然后往右移一個像素,再看是不是所有值相等,如此遍歷整幅圖,直到找到全等的位置。
sw1.jpg

sw2.jpg

這樣的搜尋策略叫做滑動窗口
template_match_result.jpg

實現代碼如下:

# coding: utf-8
import cv2
import numpy as np

'''
函數名:template_match
輸入:
template    模板
img   原圖
輸出:
(x,y)    匹配位置的左上角坐標,找不到返回None
'''
def template_match(template, img):
    tpl_h, tpl_w = template.shape[:2]
    img_h, img_w = img.shape[:2]
    for i in xrange(img_h - tpl_h):
        for j in xrange(img_w - tpl_w):
            roi = img[i:i+tpl_h, j:j+tpl_w]
            if (template == roi).all():
                return (j,i)
    return None

# 程序入口
def main():
    # 整圖
    cars = cv2.imread('car_test.jpg')
    # 車模板
    car_tpl = cars[117:199, 24:246].copy()

    pos = template_match(car_tpl, cars)
    tpl_h, tpl_w = car_tpl.shape[:2]

    if pos is not None:
        x,y = pos
        cv2.rectangle(cars, (x,y), (x+tpl_w,y+tpl_h), (0,255,0), 2)

    cv2.imshow('template', car_tpl)
    cv2.imshow('result', cars)
    cv2.imwrite('template_match_result.jpg', cars)
    cv2.waitKey(0)

if __name__ == '__main__':
    main()

這樣做的缺點很明顯,只要有一個像素點跟模板不相等就找不到了,如果只是要找些非常固定的東西那用這種方法是可以的,比如以前做游戲腳本的時候要實現鼠標點到某個固定的技能或道具上。

二、相關系數

這時候要用更靠譜的測度——例如相關系數(除此之外還有平方差等)。
圖像的相關系數的計算方法參考這個公式,是從Matlab的文檔截下來的。

相關系數公式

分子是圖像與模板的協方差,分母是它們的標準差的乘積。
具體原理參考知乎的這個回答
它會返回一個數值表示圖像的相關程度,越相關,值越靠近1,實現代碼如下:

# 輸入灰度圖
def norm_corr(template, img):
    tpl_h, tpl_w = template.shape[:2]
    img_h, img_w = img.shape[:2]
    expand_img = np.zeros((tpl_h+img_h, tpl_w+img_w), dtype=np.uint8)
    expand_img[:img_h, :img_w] = img
    img_h, img_w = expand_img.shape[:2]

    # 圖像均值
    tpl_mean = np.mean(template)
    # 減均值
    tpl_sub_mean = template - tpl_mean
    # 標準差
    sigma_tpl = np.sum(tpl_sub_mean**2)
    # 相關系數圖
    corr = np.zeros(img.shape, dtype=np.float32)

    for i in xrange(img_h - tpl_h):
        for j in xrange(img_w - tpl_w):
            roi = expand_img[i:i+tpl_h, j:j+tpl_w]
            # 圖像均值
            roi_mean = np.mean(roi)
            # 減均值
            roi_sub_mean = roi - roi_mean
            # 標準差
            sigma_roi = np.sum(roi_sub_mean**2)
            # 協方差
            cov = np.sum(tpl_sub_mean * roi_sub_mean)
            # 相關系數
            corr[i,j] = cov / np.sqrt(sigma_tpl * sigma_roi)

    # 歸一化到0-255
    corr_max = corr.max()
    corr_min = corr.min()
    print 'max = ', corr_max
    print 'min = ', corr_min
    if corr_max != corr_min:
        corr = 255 * (corr - corr_min) / (corr_max - corr_min)
    return corr.astype(np.uint8)

要注意輸入是灰度圖,最后輸出是一張表示每一個位置上的相關系數的圖,歸一化到0-255就可以顯示出來了。再次使用上面的模板圖和大圖,得到的相關系數圖如下:

相關系數圖

圖中越亮的地方表示把模板的左上角在大圖的對應位置的可能性越大。
我們可以把相關圖上值超過127(最大255,可以自行設置這個閾值)的地方用矩形框標出來。
代碼如下:

idx = np.where(corr > 0.5 * 255)
rows = idx[0]
cols = idx[1]
rects = []
for r,c in zip(rows, cols):
    if c+tpl_w < img_w and r+tpl_h < img_h:
        rects.append((c,r,c+tpl_w,r+tpl_h))

for rect in rects:
    tx,ty,bx,by = rect
    cv2.rectangle(cars, (tx,ty), (bx,by), (0,255,0), 2)

效果如下:


所有框

可以看到雖然用的模板是左邊的車,但由于兩臺車很相似,所以右邊的車也被檢出來了,但是框非常的多,我們可以想辦法做去重,把頂點靠近的矩形框當作一個子集,最后分別給出各個矩形集的平均位置,代碼如下,可以根據需要子集改進:

def group_rects(rects, diff=20):
    groups = [[rects[0]]]
    for rect in rects[1:]:
        tx,ty,bx,by = rect
        found = False
        for gr in groups:
            for gre in gr:
                if abs(gre[0]-tx) < diff and abs(gre[1]-ty) < diff and abs(gre[2]-bx) < diff and abs(gre[3]-by) < diff:
                    gr.append(rect)
                    found = True
                    break
            if found:
                break
        if not found:
            groups.append([rect])
    result = []
    for group in groups:
        result.append(np.array(group).mean(axis=0).astype(np.int32).tolist())
    return result

這樣就得到了很好的兩個框。


框融合結果

總結

即使改變了使用的距離函數,我們也只使用了一個數據(圖像)就希望能檢測到其他的同類物體,的確是很以偏概全的想法。以檢測車子的例子來說,我們是把一臺特定的車當作模板,而沒有從更接近本質的角度去解構這個問題,假設我們能人為的定義車子該長什么樣也許就能很好的解決這個問題,比如說車有輪子、車窗、車燈、整體是流線型的,等等。這些性質可以稱為車子的特征(feature)。計算機視覺有很大部分研究的問題都在圍繞著如何更好的描述物體,也就是如何得到物體更好的特征。特征可能是級聯的,意思是說有輪子是車子的特征,輪子也有自己的特征(圓的,黑的),特征總是從低層的只包含結構、形狀等信息往高層的更不可描述的信息走。發展到現在,特征可以分為人為設計和通過機器學習的方法學習出來兩種,人為設計的特征如HOG、Haar等,有著固定的計算過程,可以手工計算出來,在深度學習大熱的當今已經慢慢被新晉行業的人視為“傳統的技術”,但在我看來這兩者并沒有那么大的不同,沒有必要放棄這些“old-school”的技術,這樣只會造成“拿起錘子,看什么都是釘子”的想法。

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

推薦閱讀更多精彩內容

  • 不同圖像灰度不同,邊界處一般會有明顯的邊緣,利用此特征可以分割圖像。需要說明的是:邊緣和物體間的邊界并不等同,邊緣...
    大川無敵閱讀 13,923評論 0 29
  • 這些年計算機視覺識別和搜索這個領域非常熱鬧,后期出現了很多的創業公司,大公司也在這方面也花了很多力氣在做。做視覺搜...
    方弟閱讀 6,556評論 6 24
  • 前天晚上被頭條上關于電影《無問西東》的評論吸引,也到電影院觀影,可以說,這個冬天最溫暖、最文藝的兩部電影,一部是《...
    傍晚的早晨閱讀 228評論 0 0
  • 一周的踐行又到了尾聲,每周寫踐行記錄似乎也變的麻木起來,重復次數的增加都給我們帶來了哪方面的進步呢? 英語 最近一...
    穆建園閱讀 230評論 0 0
  • 是一個月以前的一次旅行了,我在臺灣交換學習,周末有時間就出去走走。前一天已經到了花蓮,不過沒玩什么地方,第二天與朋...
    雙木中堂閱讀 503評論 5 10