meanshift
meanshift 背后的直覺很簡(jiǎn)單吁断,設(shè)想你有一個(gè)點(diǎn)集。(可以是一個(gè)像素分布庶溶,像直方圖向后投影)宅此。你得到一個(gè)小窗口(可能是個(gè)圓)并且你得移動(dòng)窗口到最大像素強(qiáng)度的地方(或者最多點(diǎn)的數(shù)量)
初始化的窗口顯示成藍(lán)色圓,名字叫“C1”褐捻,它的圓心是藍(lán)方塊“C1_o”掸茅。但是如果你找窗口里的點(diǎn)的質(zhì)心,那就在點(diǎn)"C1_r"(用藍(lán)色小圓標(biāo)記)柠逞,這是窗口的實(shí)際質(zhì)心昧狮,所以它們不匹配。所以移動(dòng)你的窗口讓新窗口的圓圈和前一個(gè)的質(zhì)心匹配板壮。再次逗鸣,找到新的質(zhì)心,很可能是還是不匹配绰精,然后再次移動(dòng)撒璧,不斷迭代知道窗口的中心和質(zhì)心落在同一個(gè)位置(或者誤差可接受)。最后你獲得的窗口有最多的像素分布笨使,用綠色的圓標(biāo)記卿樱。叫C2,在圖里可以看到他有最多的點(diǎn)硫椰。下圖展示了整個(gè)過程殿如。
所以我們一般傳入直方圖向后投影圖像和初始化的目標(biāo)位置。當(dāng)目標(biāo)移動(dòng)時(shí)最爬,移動(dòng)被反應(yīng)在直方圖向后投影圖像里涉馁,作為結(jié)果,均值平移算法吧我們的窗口移到有最大亮度的位置爱致。
OpenCV里的均值平移
要在OpenCV里使用均值平移烤送,首先我們需要設(shè)置目標(biāo),找到他的直方圖糠悯,這樣我們可以為了計(jì)算均值平移向后投影目標(biāo)到每一幀上帮坚。我們也需要提供窗口的初始位置。對(duì)于直方圖互艾,只考慮色調(diào)试和。要避免低光線帶來(lái)的錯(cuò)誤值,低光線的值使用cv2.inRange()函數(shù)來(lái)丟棄掉纫普。
import numpy as np
import cv2cap = cv2.VideoCapture('slow.flv')
# take first frame of the video
ret,frame = cap.read()# setup initial location of window
r,h,c,w = 250,90,400,125? # simply hardcoded the values
track_window = (c,r,w,h)# set up the ROI for tracking
roi = frame[r:r+h, c:c+w]
hsv_roi =? cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )while(1):
? ? ret ,frame = cap.read()
? ?
? ? if ret == True:
? ? ? ? hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
? ? ? ? dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)? ? ? ? # apply meanshift to get the new location
? ? ? ? ret, track_window = cv2.meanShift(dst, track_window, term_crit)? ? ? ? # Draw it on image
? ? ? ? x,y,w,h = track_window
? ? ? ? img2 = cv2.rectangle(frame, (x,y), (x+w,y+h), 255,2)
? ? ? ? cv2.imshow('img2',img2)
? ? ? ? k = cv2.waitKey(60) & 0xff
? ? ? ? if k == 27:
? ? ? ? ? ? break
? ? ? ? else:
? ? ? ? ? ? cv2.imwrite(chr(k)+".jpg",img2)? ? ? ? else:
? ? ? ? ? ? breakcv2.destroyAllWindows()
cap.release()
我使用的視頻里的三幀如下:
camshift
你仔細(xì)看之前的結(jié)果沒阅悍?有一個(gè)問題,我們的窗口總是同樣的大小,當(dāng)汽車從遠(yuǎn)處走得離攝像頭越來(lái)越近节视,這并不好拳锚。我們需要讓窗口的大小和目標(biāo)的大小以及旋轉(zhuǎn)相適應(yīng)。這個(gè)解決方案也是來(lái)自O(shè)penCV實(shí)驗(yàn)室寻行,叫做CAMshift(連續(xù)的適應(yīng)性均值平移)
它首先應(yīng)用均值平移霍掺,當(dāng)均值平移覆蓋后,它更新窗口的大小拌蜘,
它也計(jì)算最適合的橢圓的方向杆烁,然后它在使用新的大小的窗口在之前的位置開始進(jìn)行均值平移,過程不斷繼續(xù)直到到達(dá)指定的準(zhǔn)確率简卧。
OpenCV里的camshift
和meanshift幾乎一樣兔魂,但是它返回旋轉(zhuǎn)的舉行和盒子參數(shù)
import numpy as np
import cv2cap = cv2.VideoCapture('slow.flv')
# take first frame of the video
ret,frame = cap.read()# setup initial location of window
r,h,c,w = 250,90,400,125? # simply hardcoded the values
track_window = (c,r,w,h)# set up the ROI for tracking
roi = frame[r:r+h, c:c+w]
hsv_roi =? cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )while(1):
? ? ret ,frame = cap.read()? ? if ret == True:
? ? ? ? hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
? ? ? ? dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)? ? ? ? # apply meanshift to get the new location
? ? ? ? ret, track_window = cv2.CamShift(dst, track_window, term_crit)? ? ? ? # Draw it on image
? ? ? ? pts = cv2.boxPoints(ret)
? ? ? ? pts = np.int0(pts)
? ? ? ? img2 = cv2.polylines(frame,[pts],True, 255,2)
? ? ? ? cv2.imshow('img2',img2)? ? ? ? k = cv2.waitKey(60) & 0xff
? ? ? ? if k == 27:
? ? ? ? ? ? break
? ? ? ? else:
? ? ? ? ? ? cv2.imwrite(chr(k)+".jpg",img2)
? ? else:
? ? ? ? breakcv2.destroyAllWindows()
cap.release()
END