前請(qǐng)?zhí)嵋?/h2>
前三期(【計(jì)算機(jī)視覺(二)】常用顏色空間及其轉(zhuǎn)換)揩尸、【計(jì)算機(jī)視覺(三)】形態(tài)學(xué)處理侥袜、【計(jì)算機(jī)視覺(四)】輪廓檢測(cè))在介紹基本知識(shí)的同時(shí)穿插了一個(gè)很naive的檢測(cè)車牌位置的方法稠项,對(duì)參數(shù)設(shè)置有很強(qiáng)的依賴。后續(xù)會(huì)慢慢涉及到高級(jí)的方法窃躲。
本期內(nèi)容
本期作為一個(gè)引子介紹模板匹配法嫂伞,引出后來的Cascade檢測(cè)算法。
一聂抢、全等模板
假如我們要在上面這張固定的圖上找出左邊這輛車的位置钧嘶,一個(gè)很簡單的方法就是我們可以先人工把左邊這輛車用“截圖”工具裁剪出來。
然后拿著這張圖在大圖上找琳疏,一開始有决,我們把模板圖放到大圖的左上角,看它們是不是所有像素值都相等空盼,然后往右移一個(gè)像素书幕,再看是不是所有值相等,如此遍歷整幅圖揽趾,直到找到全等的位置台汇。
這樣的搜尋策略叫做滑動(dòng)窗口。
實(shí)現(xiàn)代碼如下:
# coding: utf-8
import cv2
import numpy as np
'''
函數(shù)名:template_match
輸入:
template 模板
img 原圖
輸出:
(x,y) 匹配位置的左上角坐標(biāo)篱瞎,找不到返回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()
這樣做的缺點(diǎn)很明顯苟呐,只要有一個(gè)像素點(diǎn)跟模板不相等就找不到了,如果只是要找些非常固定的東西那用這種方法是可以的俐筋,比如以前做游戲腳本的時(shí)候要實(shí)現(xiàn)鼠標(biāo)點(diǎn)到某個(gè)固定的技能或道具上牵素。
二、相關(guān)系數(shù)
這時(shí)候要用更靠譜的測(cè)度——例如相關(guān)系數(shù)(除此之外還有平方差等)澄者。
圖像的相關(guān)系數(shù)的計(jì)算方法參考這個(gè)公式两波,是從Matlab的文檔截下來的。
分子是圖像與模板的協(xié)方差闷哆,分母是它們的標(biāo)準(zhǔn)差的乘積腰奋。
具體原理參考知乎的這個(gè)回答。
它會(huì)返回一個(gè)數(shù)值表示圖像的相關(guān)程度抱怔,越相關(guān)劣坊,值越靠近1,實(shí)現(xiàn)代碼如下:
# 輸入灰度圖
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
# 標(biāo)準(zhǔn)差
sigma_tpl = np.sum(tpl_sub_mean**2)
# 相關(guān)系數(shù)圖
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
# 標(biāo)準(zhǔn)差
sigma_roi = np.sum(roi_sub_mean**2)
# 協(xié)方差
cov = np.sum(tpl_sub_mean * roi_sub_mean)
# 相關(guān)系數(shù)
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)
要注意輸入是灰度圖屈留,最后輸出是一張表示每一個(gè)位置上的相關(guān)系數(shù)的圖局冰,歸一化到0-255就可以顯示出來了。再次使用上面的模板圖和大圖灌危,得到的相關(guān)系數(shù)圖如下:
圖中越亮的地方表示把模板的左上角在大圖的對(duì)應(yīng)位置的可能性越大康二。
我們可以把相關(guān)圖上值超過127(最大255,可以自行設(shè)置這個(gè)閾值)的地方用矩形框標(biāo)出來勇蝙。
代碼如下:
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)
效果如下:
可以看到雖然用的模板是左邊的車沫勿,但由于兩臺(tái)車很相似,所以右邊的車也被檢出來了,但是框非常的多产雹,我們可以想辦法做去重诫惭,把頂點(diǎn)靠近的矩形框當(dāng)作一個(gè)子集,最后分別給出各個(gè)矩形集的平均位置蔓挖,代碼如下夕土,可以根據(jù)需要子集改進(jìn):
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
這樣就得到了很好的兩個(gè)框。
總結(jié)
即使改變了使用的距離函數(shù)瘟判,我們也只使用了一個(gè)數(shù)據(jù)(圖像)就希望能檢測(cè)到其他的同類物體怨绣,的確是很以偏概全的想法。以檢測(cè)車子的例子來說拷获,我們是把一臺(tái)特定的車當(dāng)作模板梨熙,而沒有從更接近本質(zhì)的角度去解構(gòu)這個(gè)問題,假設(shè)我們能人為的定義車子該長什么樣也許就能很好的解決這個(gè)問題刀诬,比如說車有輪子、車窗邪财、車燈陕壹、整體是流線型的,等等树埠。這些性質(zhì)可以稱為車子的特征(feature)糠馆。計(jì)算機(jī)視覺有很大部分研究的問題都在圍繞著如何更好的描述物體,也就是如何得到物體更好的特征怎憋。特征可能是級(jí)聯(lián)的又碌,意思是說有輪子是車子的特征咬崔,輪子也有自己的特征(圓的领跛,黑的)芽世,特征總是從低層的只包含結(jié)構(gòu)艾恼、形狀等信息往高層的更不可描述的信息走曙旭。發(fā)展到現(xiàn)在荸镊,特征可以分為人為設(shè)計(jì)和通過機(jī)器學(xué)習(xí)的方法學(xué)習(xí)出來兩種拜马,人為設(shè)計(jì)的特征如HOG单芜、Haar等展姐,有著固定的計(jì)算過程躁垛,可以手工計(jì)算出來,在深度學(xué)習(xí)大熱的當(dāng)今已經(jīng)慢慢被新晉行業(yè)的人視為“傳統(tǒng)的技術(shù)”圾笨,但在我看來這兩者并沒有那么大的不同教馆,沒有必要放棄這些“old-school”的技術(shù),這樣只會(huì)造成“拿起錘子擂达,看什么都是釘子”的想法土铺。