< Background Subtraction >
이번 장에서는
- OpenCV에서 가능한 배경 제거 방법에 대해서 알아볼 것이다.
Basics
배경 제거는 많은 비전 기반의 응용의 전처리 단계에서 주로 이뤄진다. 예를 들어, 정적인 카메라로 방을 들어오는 / 나가는 방문자들의 수를 세거나, 차량으로 부터 교통 정보를 추출하는 등의 경우가 존재한다. 이 모든 경우에, 사람이나 차량만들 추출할 필요가 있다. 기술적으로, 정적인 배경으로부터 움직이는 전경(foreground)을 추출할 필요가 있다.
만약 방문자 없는 방, 차량 없는 도로같이, 배경만 있는 이미지라면, 매우 쉬운 일이 된다. 배경으로 부터 새로운 이미지를 빼기만 하면 된다. 그러면 전경만 얻을 수 있다. 하지만 많은 경우에, 이런 이미지와 같지 않기에, 가지고있는 이미지로부터 배경을 추출해야한다. 이는 차량에 그림자가 생기면 더 복잡해진다. 그림자도 움직이기 때문에, 간단한 제거로는 그림자도 같이 전경으로 쳐질 것이다. 이는 복잡한 일이다.
여러 알고리즘이 이러한 목표를 위해있다. OpenCV는 3가지 알고리즘을 소개하고 있다. 하나하나 봐보자!
BackgroundSubtractorMOG
이는 가우시안 혼합(Gaussian Mixture)을 기반으로한 전경/배경 분할 알고리즘이다. 이는 모델에 각 K값이 3 또는 5인 가우시안 분포의 혼합(mixture)을 배경 픽셀에 적용함으로써 배경 제거를 하는 방법을 사용한다. 믹스쳐에 대한 가중치는 색상이 장면에 머무는 시간 비율을 나타낸다. 배경으로 생각되는 색은 더 오래 머물거나 정적인 것이다.
코딩을 할 때에, cv2.createBackgroundSubtractorMOG()함수를 사용하기 위해 우리는 배경 객체를 생성해야한다. 과거의 길이, 가우시안 믹스쳐의 수, 임계값 등과 같은 선택적인 파라미터가 있다. 모두 기본값으로 설정한다. 그리고 비디오 루프에서 backgroundsubtractor.apply()를 사용해서 전경 마스크(foreground mask)를 얻는다.
아래 코드를 보자~
## createBackgroundSubtractorMOG
import numpy as np
import cv2
cap = cv2.VideoCapture('./videos/walking.avi')
fgbg = cv2.bgsegm.createBackgroundSubtractorMOG()
while (1):
ret, frame = cap.read()
frame = cv2.resize(frame,(400,400))
fgmask = fgbg.apply(frame)
# _,fgmask = cv2.threshold(fgmask,127,255,cv2.THRESH_BINARY_INV)
cv2.imshow('original',frame)
cv2.imshow('frame',fgmask)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
좌측이 기존 영상이고, 오른쪽이 MOG함수를 적용한 결과이다.
BackgroundSubtractorMOG2
이 또한 가우시안 믹스쳐 기반의 배경/전경 분할 알고리즘이다. 이 알고리즘의 한 가지 중요한 특성은 각 픽셀에 대해 알맞는 가우시안 분포의 수를 선택한다는 것이다. (기억해라, 이전 케이스에서는 K 가우시안 분포를 가져갔다) 이는 조명 변화로 인한 다양한 장면에 대해서 더 나은 적응력을 보인다.
이전의 경우에서처럼, 배경 제거 객체를 생성해야한다. 여기서는 그림자를 탐지할지 말지를 선택할 수 있다. 만약detectShadows = True (초기값)이라면, 이는 탐지하고 그림자를 따로 나타내지만 속도가 줄어든다. 그림자는 회색으로 표시된다.
## createBackgroundSubtractorMOG2
import numpy as np
import cv2
cap = cv2.VideoCapture('./videos/walking.avi')
fgbg = cv2.createBackgroundSubtractorMOG2(detectShadows=False)
while (1):
ret, frame = cap.read()
frame = cv2.resize(frame,(400,400))
fgmask = fgbg.apply(frame)
# _,fgmask = cv2.threshold(fgmask,127,255,cv2.THRESH_BINARY_INV)
cv2.imshow('original',frame)
cv2.imshow('frame',fgmask)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
**detectShadows = False
**detectShadows = True
회색지역이 그림자이다.
BackgroundSubtractorGMG
이 알고리즘은 통계적 배경 이미지 제거와 픽셀 단위의 베이지안 분할을 결합한 것이다.
이는 처음의 몇(120개) 프레임을 배경 모델링하는데 사용한다. 이는 베이지안 추론을 사용하여 전경 객체일 확률을 구하는 확률적 전경 분할 알고리즘에 이용된다.이 추측은 적응적이다 ; 변덕스러운 조명에 적응하기 위해 새로운 관측들은 기존의 관측보다 더 높은 가중치를 가진다. closing과 opening과 같은 몇몇 형태학적인(morphological) 필터링으로 원하지 않는 노이즈를 제거할 수 있다. 처음 몇 프레임동안은 검정 창을 보게 된다.
노이즈 제거를 위해 cv2.morphologyEx의 opening을 사용하는 것이 더 좋다.
## BackgroundSubtractorGMG
import numpy as np
import cv2
cap = cv2.VideoCapture('./videos/walking.avi')
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
fgbf = cv2.bgsegm.createBackgroundSubtractorGMG()
while(1):
ret, frame = cap.read()
frame = cv2.resize(frame,(400,400))
fgmask = fgbg.apply(frame)
fgmask = cv2.morphologyEx(fgmask,cv2.MORPH_OPEN,kernel)
cv2.imshow('orig',frame)
cv2.imshow('frame',fgmask)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
opening으로 인해 노이즈들이 지워진 모습을 볼 수 있다.