天天看點

Opencv實戰之混合高斯前景背景分割算法

-不管三七二十一,先上代碼

-讀取需要掌握的函數

# 相關函數
# cv.VideoCapture() 初始化攝像頭,0開啟第一個攝像頭,1開啟第2個攝像頭,傳回攝像頭對象,一般會自動打開攝像頭
# cap.read()  讀取攝像頭幀,傳回值1表示是否成功讀取幀,傳回值2表示該幀
# cv.cvtColor(frame,mode) 轉換圖檔的色彩空間
# cap.release() 關閉攝像頭
# cap.isOpened()  檢查攝像頭是否打開
# cap.open()  打開攝像頭
# cap.get(propld) 獲得該幀的大小
# cap.set(propld,value) 設定該幀的大小      
  • 基于混合高斯的原理圖

    我們可以認為時域中的同一個點的像素值看做是一個像素的變化過程,由一組像素組成,如果這裡隻考慮灰階圖的話,對于點 , 處于時間的像素曆史值為

    其中代表着圖像序列,在生活中,我們會遇到很多幹擾因素,例如光照亮度,風吹動樹葉,行走中的汽車。環境中的背景對于我們來說往往不是完全不變的。由于這種變化的産生,我們經常會發現,随着時間的推移,在圖像中,也就是即随着幀數的增加,像素點的值的分布較為分散,尤其是前景與背景交叉的區域。由于像素點的變化是多個的,适應單個像素變化的高斯模型,針對二維圖檔來說,那将是一個多元的變化,一般高斯模型變化無法适應。那麼我們将針對每一個模型進行高斯的模組化,這樣多個像素得到的機率密度函數為:

    是該點對應的高斯模型的最大數目, 是時間時的第個高斯模型的權重,即目前高斯模型對于該像素點對應的所有模型占得權重, 是時間t時的第i個高斯模型的期望, 是時間 t時的第 i個高斯模型的協方差矩陣, 代表高斯機率密度方程:

  • 一般考慮到考慮到程式的空間與時間複雜度, 的取值一般為3-5.而且協方差矩陣被簡化為

    判斷一個像素點是否服從高斯分布條件:像素值是否位于高斯分布的2.5倍标準差内。對于每個像素點對應的高斯模型,如果該像素值比對到了某個高斯模型,則更新公式如下:

    對于沒有比對到的高斯模型,其方差與期望不變,權重的更新按照上述公式。其中當像素點比對了某個高斯模型的時候設定值為1,否則值為0。

    每個像素值對應的各個高斯分布的參數會一直發生改變,當一個物體趨于靜止的時候,對應的像素高斯分布的方差會趨于更小,其對應的權重會趨于變得更大,相對于一個運動的物體,他一般不會比對到像素對應的高斯模型,運動的物體會讓高斯模型的方差變得更大,權重變得更低。

我們按照高斯模型的權重與方差的比值進行降序排列,由門檻值T(程式中取值為0.7)選出B個高斯分布作為目前像素判斷是否屬于背景的模型:

如果一個新的像素值服從該B 個高斯模型中的某一個,我們就認為該像素屬于背景。但是如果該像素服從某個高斯分布,但是該高斯分布位于該B 個高斯模型之後,該像素仍然會被判定為前景。T的取值不宜過大,否則會産生一個多峰分布模型,背景模型如果是單峰值的話,會選出一個可能性最高的高斯分布。

效果圖

上代碼

import cv2 as cv
import numpy as np

video = cv.VideoCapture(0,cv.CAP_DSHOW)
# 設定編碼格式
# MP4
fourcc = cv.VideoWriter_fourcc(*"mp4v")
# avi
fourcc_2 = cv.VideoWriter_fourcc(*'XVID')
out_video = cv.VideoWriter('output.mp4',fourcc, 20.0, (640,480))
out_video_2 = cv.VideoWriter('ori.avi',fourcc, 20.0, (640,480))


# 背景減法器 基于自适應混合高斯背景模組化的背景減除法
# history:用于訓練背景的幀數,預設為500幀,如果不手動設定learningRate,history就被用于計算目前的learningRate,此時history越大,learningRate越小,背景更新越慢;
# varThreshold:方差門檻值,用于判斷目前像素是前景還是背景。一般預設16,如果光照變化明顯,如陽光下的水面,建議設為25,36,具體去試一下也不是很麻煩,值越大,靈敏度越低;
# detectShadows:是否檢測影子,設為true為檢測,false為不檢測,檢測影子會增加程式時間複雜度,如無特殊要求,建議設為false
backsub = cv.createBackgroundSubtractorMOG2(history=500,varThreshold=16,detectShadows=False)



while True:
    # ret 讀取狀态,frame image data
    ret,frame = video.read()
    # 擷取掩碼
    if ret:
        mask = backsub.apply(frame)
        # print(frame.shape)
        # print(mask.shape)
        # 擴充次元
        mask = np.expand_dims(mask,axis=2).repeat(3,axis=2)
        out_video.write(mask)
        out_video_2.write(frame)
        cv.imshow("frame",mask)
    if cv.waitKey(30) & 0xFF ==ord('q'):
        break
#     釋放資源
video.release()
out_video.release()
out_video_2.release()
cv.destroyAllWindows()
```      

繼續閱讀