去除影片背景與移動物體偵測
在固定攝影機場景的環境下,我們可以用cv2.absdiff()函數將前後兩個影格(frame)相減,得到的殘影即為移動物體的位置,但是若影格中的物體中間材質與顏色相近則殘影為零,因此這種方法得到的會像是物體的輪廓在移動.
另一種作法為,先取一張沒有物體的靜態影像,將之後的每個影格隊這個靜態影像將減,這樣我們就可以得到完整的物體移動,但是這種方阿有以下幾個缺點.
- 必須使用兩張圖片。
- 除非能夠隨時update用於diff計算的背景圖片。
- 相當容易受環境變動(如光線、週遭物件、物體與背景顏色)的干擾。
- 若移動中的物體與背景顏色相同則無法偵測。
- 若物體靜止或移動得很緩慢,則無法偵測。
為了解決這些問題,後續開發出了以下幾種演算法用來做影片去背與偵測物體移動,而這些函數都內建在openCV內:
- K-Nearest(KNN)
- Mixture of Gaussians的MOG及MOG2
- Geometric Multigid(GMG)
這些方法中MOG效果較好,因此我們在這裏只介紹MOG.
BackgroundSubtractorMOG
2001年由P. KadewTraKuPong和R. Bowden提出的論文“An improved adaptive background mixture model for real-time tracking with shadow detection”發展而成的演算法,是一種以高斯混合(Gaussian Mixture-base)為基礎的前景/背景分離技術.它利用一種指定K值(K=3~5)的高斯分佈來計算背景像素。
指令:cv2.createBackgroundSubtractorMOG([history[, nmixtures[, backgroundRatio[, noiseSigma]]]]) → retval</br> 參數:int history=200, int nmixtures=5, double backgroundRatio=0.7, double noiseSigma=0</br>
BackgroundSubtractorMOG2
BackgroundSubtractorMOG的改良版,依據Z.Zivkovic,的兩篇論文:2004年「Improved adaptive Gausian mixture model for background subtraction」以及2006年「Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction」發展而來。理論上,MOG2比起MOG有更好的光照差異處理,此外,MOG2支援multi-threads,因此速度快近一倍。MOG2預設的detectShadows = True,預設為偵測影子,影子以灰階呈現.也可以選擇不要偵測影子detectShadows = False.
指令:cv2.createBackgroundSubtractorMOG2()</br> 參數:int history=500, double varThreshold=16, bool detectShadows=true
import cv2
cap = cv2.VideoCapture('car.mp4')
fgbg = cv2.createBackgroundSubtractorMOG2(detectShadows=True, varThreshold=16)
i=0
while(1):
ret, frame = cap.read()
frame=cv2.resize(frame,(240,180))
fgmask = fgbg.apply(frame)
# 把影子過濾掉
th = cv2.threshold(fgmask.copy(), 250, 255, cv2.THRESH_BINARY)[1]
#針對不同的case我們需要調整morphology膨漲(dolate)或腐蝕的(erode)參數,讓要偵測的物體變得更明顯,同時消除雜訊
# 參考文章: http://monkeycoding.com/?p=577
dilated = cv2.dilate(th, cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3)), iterations = 0)
image, contours, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
#只把輪廓面積大於10的外誆矩形畫出
if cv2.contourArea(c) > 10:
(x,y,w,h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x,y), (x+w, y+h), (255, 255, 0), 2)
# cv2.imshow("mog", fgmask)
# cv2.imshow("thresh", th)
cv2.imshow("detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
#waitKey()--这个函数是在一个给定的时间内(单位ms)等待用户按键触发;如果用户没有按下键,则接续循环
break
cap.release()
cv2.destroyAllWindows()