영상의 객체 추적 - 배경 차분 - 이동 평균 배경
이전 포스팅에서 공부하였던 정적 배경 모델 사용시 문제점은 새로 나타난 객체가 고정되었을 때 이것을 지속적으로 객체로 인식한다는 것입니다.
고정된 객체가 일정 시간 지나면 배경으로 등록되는 방법에 대해 알아보겠습니다.
1. 이동 평균 배경 - Moving average
이동 평균 배경 방법은 현재 프레임과 이전 프레임까지의 배경 영상에 가중치를 곱하여 영상을 갱신합니다.
수백장의 영상을 저장하는 대신 매 프레임이 들어올 때마다 평균 영상을 갱신하는 방법입니다.
$$B(x, y, t) = \alpha \cdot I(x,y,t) + (1-\alpha)\cdot B(x, y, t-1)$$
B(x,y,t) : 갱신된 배경 영상
$\alpha$ : 현재 프레임에 대한 가중치
I(x,y,t) : 현재 프레임
B(x,y,t-1) : 이전 프레임까지의 배경 영상
현재 프래임과 배경 영상의 가중치 합을 이용해서 배경 영상을 업데이트 합니다.
이 방법을 이용하면 배경 영상을 조금조금씩 업데이트 할 수 있습니다.
주의할 점은 현재 프래임은 컬러영상 unit8 데이터 타입인데 가중치 합 과정에서 소수점 미세한 정보를 보존해야 하므로 입력 영상은 실수형 자료를 이용해줘야 합니다.
영상을 갱신하는 방법이므로 대용량 버퍼 메모리가 필요하지 않습니다.
2. 이동 평균 계산을 위한 가중치 누적 함수 - cv2.accumulateWeighted
OpenCV에서는 이동 평균 함수를 따로 제공해주지 않고 cv2.accumulateWeighted 함수를 이용하여 이동 평균 배경을 구현할 수 있습니다.
cv2.accumulateWeighted(src, dst, alpha, mask=None) -> dst
• src: 입력 영상. 1 또는 3채널. 8비트 또는 32비트 실수형
• dst: 축적 영상. 입력 영상과 동일 채널 개수. 32비트 또는 64비트 실수형
• alpha: (입력 영상에 대한) 가중치
• mask: 마스크 영상
alpha 값은 보통 0.01을 줍니다.
0.01은 작은 값이 아닙니다.
초당 30프레임으로 영상이 동작하기 때문에 0.01은 3~4초만에 100개의 프레임이 입력되어 1이 됩니다.
dst가 입력인자, 반환값 두 가지가 있습니다.
accumulate 내부 구현 식은 dst가 입력, 출력 두 곳에 있으므로 입력 인자에도 dst를 입력해야 합니다.
3. 이동 평균에 의한 배경 차분 예제 코드
예제 코드 출처 : 황선규 박사님 github홈페이지 sunkyoo.github.io/opencv4cvml/
# 비디오 파일 열기
cap = cv2.VideoCapture('PETS2000.avi')
if not cap.isOpened():
print('Video open failed!')
sys.exit()
# 배경 영상 등록
ret, back = cap.read()
if not ret:
print('Background image registration failed!')
sys.exit()
# back: uint8 배경, fback: float32 배경
# 흑백으로 변환
back = cv2.cvtColor(back, cv2.COLOR_BGR2GRAY)
# 노이즈 제거
back = cv2.GaussianBlur(back, (0, 0), 1.0)
# float32로 변경
fback = back.astype(np.float32)
# 비디오 매 프레임 처리
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (0, 0), 1.0)
# fback: float32, back: uint8 배경
# gray는 현재 프레임의 영상
cv2.accumulateWeighted(gray, fback, 0.01)
# absdiff 함수를 이용하기 위해 unit8로 변경
back = fback.astype(np.uint8)
# 인자의 타입이 같아야 한다.
diff = cv2.absdiff(gray, back)
_, diff = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
# 레이블링을 이용하여 바운딩 박스 표시
cnt, _, stats, _ = cv2.connectedComponentsWithStats(diff)
for i in range(1, cnt):
x, y, w, h, s = stats[i]
if s < 100:
continue
cv2.rectangle(frame, (x, y, w, h), (0, 0, 255), 2)
cv2.imshow('frame', frame)
cv2.imshow('diff', diff)
cv2.imshow('back', back)
if cv2.waitKey(30) == 27:
break
cap.release()
cv2.destroyAllWindows()
이처럼 고정 객체가 발생하였을 때 배경 영상을 업데이트하여 고정 객체를 배경 객체로 인식합니다.
OpenCV 튜토리얼과 황선규 박사님의 'OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝' 을 공부하면서 정리해 보았습니다.