作者:曾成訓
喜歡使用 Arduino 或樹莓派的 Maker 們,應該都很清楚 PIR 這種模組,圓圓的白色蓋子下藏的是對紅外線變化特別敏感的 sensor。由於人或動物均會輻射出微量的紅外線,因此透過 PIR 對於週遭紅外線的變化感應,我們就能判斷是否有人在移動。
透過 OpenCV 進行影像處理,我們也能達到類似的功能:當影像中有物體移動時,能把該物體抓取並顯示出來,該方法非常的簡單,我們來看看怎麼做到的。
cv2.absdiff
此指令依其字面意思是「差異的絕對值」,的確,它的功能就是將兩張相片相減後取絕對值。例如,我們將影片中連續兩個 frame 進行 cv2.absdiff,單憑肉眼似乎感覺不出前後兩張 frame 的差異,但使用 cv2.absdiff 之後便會得到如上的結果。

憑肉眼分辨不出差別的兩張 frame,使用 cv2.absdiff 後可以看出其些微差異
前後 frame 的差異形成了較淺的顏色,因此,我們發現 cv2.absdiff 的好用之處在於它不但能取得移動中的物體,還能過濾掉相片中相同的雜物背景;然而,物體移動是漸進的,因此明顯的差異僅在於「物體邊緣」,中間部份由於材質顏色相同因此差異不明顯,這使得移動中的物體看起來像是由「線條」組成的的輪廓。
如果我們將其中一張相片換成無人的背景圖,兩者進行 cv2.absdiff 後就能得到較為完整的物件形狀。

將其中一張相片換成無人的背景圖,兩者進行 cv2.absdiff 後便可以得到較為完整的物件形狀
同樣的,若能在影片中套用此背景圖,針對每個 frame 進行計算,其效果會比使用前一張 frame 作為背景圖好很多。
用二值化強調物件圖形
如上透過 diffabs 所取得的物體圖形為灰階且有深有淺,為了明確取得該物體的範圍,我們將 cv2.absdiff 的結果進行二值化(Binary)以及 Dilate(擴張)、Erode(侵蝕)的處理。
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 了。
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)

使用 cv2.findContours 找到物體輪廓後,過濾太小的物體,再使用 cv2.boundingRect,就能取得物體的邊框
Background Subtractor
雖然 cv2.absdiff 能夠有效的去除背景取得移動中的物體,但卻有很多的限制讓它不是那麼好用:
- 必須使用兩張圖片
- 除非能夠隨時 update 用於 diff 計算的背景圖片
- 相當容易受環境變動(如光線、週遭物件、物體與背景顏色)的干擾
- 若移動中的物體與背景顏色相同,則無法偵測
- 若物體靜止或移動得很緩慢,則無法偵測
除了 cv2.absdiff,其實 OpenCV 還另外提供了四種針對影片的背景分割器可用以分離影像中的前景與背景,它們分別是 K-Nearest(KNN)、Mixture of Gaussians 的 MOG 及 MOG 2、Geometric Multigid(GMG)等。它們的用法非常簡單,先使用 cv2.create Background Subtractor 建立物件(可在此階段輸入參數),接著便可傳入影像,使用 apply 命令去學習並取得去除背景後的結果。
四種前/背景分離指令
OpenCV 內建四種背景分離技術,可用以學習影片中的背景以分離出移動的前景。下方為四種方法的指令及支援參數。
1. Background Subtractor MOG
這是一種以高斯混合(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
2. Background Subtractor MOG 2
這是 Background Subtractor MOG 的改良版,依據 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」發展而來。理論上,MOG 2 比起MOG 有更好的光照差異處理;此外,MOG 2 也支援 multi-threads,因此速度快近一倍。
- 指令:cv2.createBackgroundSubtractorMOG2()
- 參數:int history=500, double varThreshold=16, bool detectShadows=true
3. Background Subtractor GMG
此技術合併了固定的背景圖像預測(statistical background image estimation)以及像素貝葉氏分割(per-pixel Bayesian segmentation)理論,是來自於三位學者(Andrew B. Godbehere、Akihiro Matsukawa、Ken Goldberg)於 2012 年的論文「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
4. Background Subtractor KNN
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 實際用幾個影片來作比較,可以參見程式碼,其執行結果如下。
cv2.diffabs 的作法是前後 frame 之間的差異,因此邊緣部份會比較明顯。 MOG、MOG 2、KNN 及 GMG 四種都是使用預設參數;MOG 2 在陰影處理上比起 MOG 好很多,但是在無陰影狀況下取出的物件形態似乎又沒有 MOG 來得好;GMG 由於需要先學習,因此一開始不會顯示。
- 【模型訓練】訓練馬賽克消除器 - 2020/04/27
- 【AI模型訓練】真假分不清!訓練假臉產生器 - 2020/04/13
- 【AI防疫DIY】臉部辨識+口罩偵測+紅外線測溫 - 2020/03/23
訂閱MakerPRO知識充電報
與40000位開發者一同掌握科技創新的技術資訊!