배경 제거 코드
평균 배경 이미지 생성 코드를 알아보자
배경이 명확해야 그 배경과 비교해서 물체의 움직임을 더 잘 감지할 수 있으니 안정적이고 깨끗한 배경을 만들어야 한다.
그래서 다음과 같이 코드를 쓴다.
일종의 준비 작업이라고 생각하면 될 것 같다.
int main() {
VideoCapture capture("background.mp4"); // 비디오 파일 열기
Mat image, sum, avg; // 이미지, 합계, 평균을 저장할 변수
int cnt = 2; // 프레임 수를 세기 위한 변수
capture >> avg; // 첫 번째 프레임을 평균 변수에 저장
while (true) {
if (!capture.read(image)) break; // 더 이상 읽을 이미지가 없으면 종료
add(image / cnt, avg*(cnt - 1) / cnt, avg); // 현재 프레임과 이전 평균을 사용해 새로운 평균 계산
imshow("avg", avg); // 평균 이미지를 보여줌
cnt++; // 프레임 카운트 증가
waitKey(33); // 33ms 대기 (대략 30fps)
}
}
처음에 cnt를 2로 설정한 이유는 첫 번째 프레임을 이미 평균으로 사용하고 있기 때문이다.
그래서 cnt를 2로 시작함으로써, 첫 번째 프레임은 이미 평균의 일부로 간주하고, 그 이후 프레임들과의 평균 계산을 올바르게 할 수 있게 되는 거다.
피자를 처음부터 하나 먹었는데, 나중에 다른 사람이랑 나눠 먹으려고 하니까 이미 먹은 피자까지 나누는 건 말이 안되지 않는가 ㅋㅋ
그래서 처음부터 하나 먹은 걸 제외하고 나머지를 나누는 것처럼, 여기서도 첫 번째 프레임은 이미 평균에 들어갔으니까 cnt를 2로 시작하는 거다.
image / cnt는 현재 프레임을 전체 프레임 수로 나눈 값이다.
현재 프레임이 평균에 얼마나 작게 반영될지를 결정하는 거다.
avg * (cnt - 1) / cnt는 이전 평균을 약간 줄여서 새로운 프레임에 맞추는 부분이다.
이 두 가지를 더해서 새로운 평균을 계산해 나가는 과정이다.
천천히 조율하면서 점점 완벽한 배경을 만들어가는 거다.
절대 차이를 이용한 배경 차감 코드에 대해 알아보자
이 코드는 비디오 파일에서 첫 번째 프레임을 배경으로 설정하고, 그 뒤 프레임들과의 절대 차이를 구해 물체의 움직임을 감지한다.
int main() {
VideoCapture capture("background.mp4"); // 비디오 파일 열기
Mat background, image, gray, result, foregroundMask, foregroundImg; // 필요한 변수들 선언
// 첫 번째 프레임을 배경으로 설정
capture >> background;
cvtColor(background, background, CV_BGR2GRAY); // 배경을 그레이스케일로 변환
while (true) {
if (capture.grab() == 0) break; // 프레임 읽기 실패하면 종료
capture.retrieve(image); // 새로운 프레임을 가져옴
cvtColor(image, gray, CV_BGR2GRAY); // 현재 프레임을 그레이스케일로 변환
absdiff(background, gray, foregroundMask); // 배경과 현재 프레임의 차이 구함
threshold(foregroundMask, foregroundMask, 50, 255, CV_THRESH_BINARY); // 임계값을 적용해 전경 마스크 생성
foregroundMask.copyTo(foregroundImg); // 전경 마스크를 전경 이미지로 복사
imshow("foregroundImg", foregroundImg); // 전경 이미지 보여주기
imshow("foregroundMask", foregroundMask); // 전경 마스크 보여주기
imshow("background", background); // 배경 이미지 보여주기
waitKey(33); // 33ms 대기
}
}
absdiff(background, gray, foregroundMask); 이 부분이 핵심인데 배경과 현재 프레임의 절대 차이를 계산해서 움직임이 있는 부분을 검출한다.
threshold(foregroundMask, foregroundMask, 50, 255, CV_THRESH_BINARY); 차이를 50 이상인 값만 흰색으로, 나머지는 검은색으로 이진화한다.
OpenCV의 MoG2를 사용한 배경 차감 코드에 대해서 알아보자
MoG2(Mixture of Gaussians)는 OpenCV에서 제공하는 배경 차감 알고리즘이다.
가우시안 혼합 모델을 사용해 배경을 업데이트하고, 전경을 검출한다.
int main() {
Ptr<BackgroundSubtractor> bg_model = createBackgroundSubtractorMOG2(); // MoG2 배경 차감 모델 생성
Mat image, foregroundMask, backgroundImg, foregroundImg; // 필요한 변수 선언
VideoCapture cap("background.mp4"); // 비디오 파일 열기
while (true) {
cap >> image; // 프레임 읽기
resize(image, image, Size(640, 480)); // 이미지를 640x480 크기로 조정
if (foregroundMask.empty()) // 전경 마스크가 비어 있으면 크기를 맞춰서 생성
foregroundMask.create(image.size(), image.type());
// 전경 마스크 생성
bg_model->apply(image, foregroundMask); // 프레임을 전경 마스크로 변환, 움직이는 물체만 마스크로 검출
GaussianBlur(foregroundMask, foregroundMask, Size(11, 11), 3.5, 3.5); // 마스크를 부드럽게 처리
threshold(foregroundMask, foregroundMask, 10, 255, THRESH_BINARY); // 이진화
foregroundImg = Scalar::all(0); // 전경 이미지 초기화
image.copyTo(foregroundImg, foregroundMask); // 전경 마스크를 적용해 전경 이미지 생성
bg_model->getBackgroundImage(backgroundImg); // 배경 이미지 추출
imshow("foregroundImg", foregroundImg); // 전경 이미지 보여주기
imshow("backgroundImg", backgroundImg); // 배경 이미지 보여주기
imshow("foregroundMask", foregroundMask); // 전경 마스크 보여주기
waitKey(33); // 33ms 대기
}
}
객체의 윤곽선 검출 및 개수 세기 코드도 한 번 알아보자
int main() {
Mat gray = imread("contours.png", 0); // 이미지를 그레이스케일로 읽기
Mat result;
threshold(gray, result, 230, 255, THRESH_BINARY_INV); // 이진화 처리
vector<vector<Point>> contours; // 윤곽선을 저장할 벡터
vector<Vec4i> hierarchy; // 계층 정보를 저장할 벡터
findContours(result, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); // 윤곽선 검출
putText(result, format("contour count: %d", contours.size()), Point(50, 80), FONT_HERSHEY_SIMPLEX, 1, Scalar(128), 4); // 윤곽선 개수 표시
imshow("contours", result); // 결과 보여주기
waitKey(0); // 키 입력 대기
}