喜歡使用Arduino或樹莓派的Maker們,應該都很清楚PIR這種模組,圓圓的白色蓋子下藏的是對紅外線變化特別敏感的sensor,由於人或動物均會輻射出微量的紅外線,因此,透過PIR對於週遭紅外線的變化感應,我們就能判斷是否有人在移動。
來源:
https://howtomechatronics.com/tutorials/arduino/how-pir-sensor-works-and-how-to-use-it-with-arduino/
透過OpenCV進行影像處理,我們也能達到類似的功能:當影像中有物體移動時便能把該物體抓取並顯示出來,方法非常的簡單,下面我們來看看怎麼作到的。
cv2.absdiff
此指令依其字面意思是差異的絕對值,的確,它的功能就是將兩張相片相減後取絕對值。例如,我們將影片中兩張連續兩個frame進行cv2.absdiff,單憑肉眼似乎感覺不出前後兩張frame的差異,但使用cv2.absdiff之後便會得到如上的結果
套用到影片則效果如下:
影片中前後frame的差異形成了較淺的顏色,因此,我們發現cv2.absdiff的好用之處在於它不但能取得移動中的物體,還能過濾掉相片中相同的雜物背景!不過,由於物體移動是漸進的,因此明顯的差異僅在於物體邊緣,中間部份由於材質顏色相同因此差異不明顯,這使得移動中的物體像是由線條組成的的輪廓。
因此,如果我們將其中一張相片換成無人的背景圖,兩者進行cv2.absdiff後就能得到較為完整的物件形狀。
同樣的,若能在影片中套用此背景圖針對每個frame進行計算,其效果會比使用前一張frame作為背景圖好很多。
用二值化強調物件圖形
如上透過diffabs所取得的物體圖形為灰階且有深有淺,為了明確取得該物體的範圍,我們將cv2.absdiff的結果進行二值化(Binary)以及Dilate(擴張)、Erode(侵蝕)的處理。
(T, img) = cv2.threshold(img, 30, 255, cv2.THRESH_BINARY)
img = cv2.dilate(img, None, iterations=36)
img = cv2.erode(img, None, iterations=36)
img = cv2.dilate(img, None, iterations=12)
先使用cv2.findContours找到所有物體輪廓之後,先過濾太小的物體再使用cv2.boundingRect,就能取得物體的bounding box了。
_, cnts, _ = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
if cv2.contourArea(c) < minArea:
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(binary, (x, y), (x + w, y + h), (0,0,255), 2)
示範影片:
BackgroundSubtractor
雖然cv2.absdiff能夠有效的去除背景取得移動中的物體,但卻有很多的限制讓它不是那麼好用:
必須使用兩張圖片。
除非能夠隨時update用於diff計算的背景圖片。
相當容易受環境變動(如光線、週遭物件、物體與背景顏色)的干擾。
若移動中的物體與背景顏色相同則無法偵測。
若物體靜止或移動得很緩慢,則無法偵測。
除了cv2.absdiff,其實OpenCV還另外提供了四種針對影片的背景分割器可用以分離影像中的前景與背景,它們分別是K-Nearest(KNN)、Mixture of Gaussians的MOG及MOG2、Geometric Multigid(GMG)等。它們的用法非常簡單,先使用cv2.createBackgroundSubtractor建立物件(可在此階段輸入參數),接著便可傳入影像使用apply命令去學習並取得去除背景後的結果。
四種前/背景分離指令
OpenCV內建四種背景分離技術,可用以學習影片中的背景以分離出移動的前景。下方為四種方法的指令及支援參數,紅色粗體字表示該參數比較重要且常用。
BackgroundSubtractorMOG
這是一種以高斯混合(Gaussian Mixture-base)為基礎的前景/背景分離技術,由P. KadewTraKuPong和R. Bowden於2001年發表的論文「An improved adaptive background mixture model for real-time tracking with shadow detection」發展而來。它利用一種指定K值(K=3~5)的高斯分佈來計算背景像素。
指令:cv2.bgsegm.createBackgroundSubtractorMOG()
參數:int history=200, int nmixtures=5, double backgroundRatio=0.7, double noiseSigma=0
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,因此速度快近一倍。
指令:cv2.createBackgroundSubtractorMOG2()
參數:int history=500, double varThreshold=16, bool detectShadows=true
BackgroundSubtractorGMG
此技術合併了固定的背景圖像預測(statistical background image estimation)以及像素貝葉氏分割(per-pixel Bayesian segmentation)理論,是來自於三位學者(Andrew B. Godbehere, Akihiro Matsukawa, Ken Goldberg)於2012年的paper: Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive Audio Art Installation。
指令:cv2.bgsegm.createBackgroundSubtractorGMG ()
參數:int initializationFrames=120, double decisionThreshold=0.8
BackgroundSubtractorKNN
K近鄰(K-nearest neigbours)為基礎的前景/背景分離技術,來自於由Zoran Zivkovic 和Ferdinand van der Heijden的論文「Efficient adaptive density estimation per image pixel for the task of background subtraction.」
指令:cv2.createBackgroundSubtractorKNN ()
參數:int history=500, double dist2Threshold=400.0, bool detectShadows=true
我將這四種前/後景分離技術與之前介紹的cv2.diffabs實際用幾個影片來作比較,程式碼放在
https://github.com/ch-tseng/report_BackgroundSubtractor
,其執行結果如下。
cv2.diffabs的作法是前後frame之間的差異,因此邊緣部份會比較明顯。
其它MOG、MOG2、KNN及GMG四種都是使用預設參數。
MOG2在陰影處理上比起MOG好很多,但是在無陰影狀況下取出的物件形態似乎又沒有MOG來得好。GMG由於需要先學習,因此一開始不會顯示。