동영상의 배경 이미지를 획득하고, 제거하는 다양한 방법을 설명할 수 있다.
영상 배경 제거
배경 인식
평균에 의한 배경 영상 : 가장 간단한 배경 인식 방법으로 카메러가 고정된 상화, 조명이 일정한 상황에서 잘 동작함(CCTV 등)
실습
import cv2 as cv
## [capture]
capture = cv.VideoCapture('vtest.avi')
# capture = cv.VideoCapture('AD_street_parking_santafe_set1_no1_20200818.mp4')
# capture = cv.VideoCapture('43653691_20200211-14h19m22s_R_P.mp4')
frame_index = 1
_, frame_prev = capture.read()
while True:
frame_index = frame_index + 1
_, frame = capture.read()
if frame is None:
break
beta = 98
frame_prev = cv.addWeighted(frame_prev, float(beta)/100, frame, float(100-beta)/100, 0)
#배경영상이미지 계산으로 동영상이 재생이 되면서 프레임 개수가 늘어가는데 과거 1번부터 마지막 프레임까지 전부다 평균을 내린다.
#frame_prev = cv.addWeighted(frame_prev, (frame_index-1)/frame_index, frame, 1/frame_index, 0)
## [display_frame_number]
#get the frame number and write it on the current frame
cv.rectangle(frame, (10, 2), (100,20), (255,255,255), -1)
cv.putText(frame, str(capture.get(cv.CAP_PROP_POS_FRAMES)), (15, 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5 , (0,0,0))
## [show]
cv.imshow('Frame', frame)
cv.imshow('Background', frame_prev)
keyboard = cv.waitKey(1) & 0xFF
if keyboard == 27:
break
capture.release()
cv.destroyAllWindows()
결과
beta의 값이 낮아질수록 잔상이 진해진다.
OpenCV 활용한 배경 제거
연속 frame 혹은 동영상의 배경 제거 알고리즘 지원
Opening/Closing 기법과 같이 활용하면 노이즈 개선 가능
종류
createBackgroundSubtractorMOG()
가우시안 Mixture을 기반으로 한 전경/배경 분할 알고리즘으로 가우시안 분산값(k=3~5) 지정
createBackgroundSubtractorGMG()
통계적 배경 이미지 제거와 픽셀 단위의 베이지안 분할을 결합
처음 몇 프레임을 배경 모델링으로 사용하여 초반에는 검정 창을 출력한다.
createBackgroundSubtractorMOG2()
기존 대비 조도 변화에 강인하여 각 픽셀별로 가우시안 분산값의 개수를 적절히 선택해 줌
createBackgroundSubtractorKNN()
KNN 기반으로 배경 여부를 판단하기 위한 threshold를 활용함
(그림자 인식 기능 활용할 경우 속도가 늦어짐 - >detectShadow = True/False)
실습
import cv2 as cv
import numpy as np
#### create Background Subtractor objects
backSub = cv.createBackgroundSubtractorMOG2()
# backSub = cv.bgsegm.createBackgroundSubtractorMOG()
# backSub = cv.bgsegm.createBackgroundSubtractorGMG()
# backSub = cv.createBackgroundSubtractorKNN()
## [capture]
capture = cv.VideoCapture('vtest.avi')
# capture = cv.VideoCapture('AD_street_parking_santafe_set1_no1_20200818.mp4')
# capture = cv.VideoCapture('43653691_20200211-14h19m22s_R_P.mp4')
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5,5))
# kernel = np.ones((3,3), np.uint8)
while True:
ret, frame = capture.read()
if frame is None:
break
## [apply]
#update the background model
#배경이 추출된 영상
fgMask = backSub.apply(frame)
# fgMask_filter = cv.fastNlMeansDenoising(fgMask,3,21,7)
# fgMask_blur = cv.GaussianBlur(fgMask, (5,5),0)
#배경에서 추출된 영상을 오프닝한 영상
fgMask_morph = cv.morphologyEx(fgMask, cv.MORPH_OPEN, kernel)
# fgMask_morph = cv.morphologyEx(fgMask_morph, cv.MORPH_CLOSE, kernel)
## [apply]
## [display_frame_number]
#get the frame number and write it on the current frame
cv.rectangle(frame, (10, 2), (100,20), (255,255,255), -1)
cv.putText(frame, str(capture.get(cv.CAP_PROP_POS_FRAMES)), (15, 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5 , (0,0,0))
## [display_frame_number]
## [show]
#show the current frame and the fg masks
cv.imshow('Frame', frame)
cv.imshow('FG Mask', fgMask)
# cv.imshow('FG Mask Filtered', fgMask_filter)
# cv.imshow('FG Mask Blur', fgMask_blur)
cv.imshow('FG Mask Blur', fgMask_morph)
## [show]
keyboard = cv.waitKey(1) & 0xFF
if keyboard == 27:
break
capture.release()
cv.destroyAllWindows()
결과
Cam Shift 함수
카메라에서 사람이 지정한 물체를 추적하는 기능
MeanShift : 데이터 집합의 밀도분포(특징점, 코너, 색상)를 기반으로 객체 추적
Cam Shift : 탐색창의 크기를 스스로 조정하여 Mean-Shift의 단점 보강.
실습
import cv2
import numpy as np
capture = cv2.VideoCapture("vtest.avi")
histogram = None
# 검색 중지 요건
terminal = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 15, 0.5)
while capture.isOpened():
ret, frame = capture.read()
if not ret:
break
draw = frame.copy()
if histogram is not None:
# HSV space로 변환
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 역투영
dst = cv2.calcBackProject([hsv], [0], histogram, [0,180], 1)
# 평균 이동 추적
ret, (x,y,w,h) = cv2.meanShift(dst, (x,y,w,h), terminal)
cv2.rectangle(draw,(x,y), (x+w, y+h), (0,255,0), 2)
# 추적 영상 및 역투영 영상 동시 출력
result = np.hstack((draw, cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)))
else :
cv2.putText(draw, "Target", (10,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 1, cv2.LINE_AA)
result = draw
cv2.imshow("MeanShift Tracking", result)
key = cv2.waitKey(10) & 0xff
if key == 27:
break
elif key == ord(' '):
# space bar를 누른 후 ROI 설정
x,y,w,h = cv2.selectROI("MeanShift Tracking", frame, False)
# histogram 계산 및 정규화
if w and h :
roi = frame[y:y+h, x:x+w]
roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
histogram = cv2.calcHist([roi], [0], None, [180], [0,180])
cv2.normalize(histogram, histogram, 0, 255, cv2.NORM_MINMAX)
else:
histogram = None
capture.release()
cv2.destroyAllWindows()
meanShift(추적하고자하는 물체의 이미지,초기위치, 추적종료조건)
결과
성능이 조금 구리다. 추적반응이 상당히 느리다. 참고로 스페이스바로 정지한후 마우스로 원하는 추적 영상을 드래그 한 후 스페이스바를 누르면 된다.
실습 2
import cv2
import numpy as np
cap = cv2.VideoCapture("vtest.avi")
# 옵션 설명 http://layer0.authentise.com/segment-background-using-computer-vision.html
fgbg = cv2.createBackgroundSubtractorMOG2(varThreshold=100)
while True:
ret, frame = cap.read()
if frame is None:
break
fgmask = fgbg.apply(frame)
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(fgmask)
for index, centroid in enumerate(centroids):
if stats[index][0] == 0 and stats[index][1] == 0:
continue
if np.any(np.isnan(centroid)):
continue
x, y, width, height, area = stats[index]
centerX, centerY = int(centroid[0]), int(centroid[1])
if area > 100:
cv2.circle(frame, (centerX, centerY), 1, (0, 255, 0), 2)
cv2.rectangle(frame, (x, y), (x + width, y + height), (0, 0, 255))
cv2.imshow('mask',fgmask)
cv2.imshow('frame',frame)
keyboard = cv2.waitKey(1) & 0xFF
if keyboard == 27:
break
cap.release()
cv2.destroyAllWindows()
결과
2명씩 붙어서 가는 경우 하나의 객체로 인식해준다.