2013년 1월 20일 일요일

OpenCV #5-5 Example (그랩컷 알고리즘으로 전경 객체 추출)

 - 그랩컷 알고리즘 : 영상 분할의 다른 인기있는 알고리즘.
 - 수학적 형태학에 기반을 두진 않지만 워터쉐드 분할 알고리즘 사용과 몇가지 유사함.
 - 정지 영상에서 전경 객체를 추출하기 원할 때 좋은 알고리즘.

 - cv::grabCut 함수를 사용.
 - 영상과 배경 또는 전경에 속하는 화소의 일부인 레이블이 필요.
 - 부분 레이블링에 기반을 두며, 이 알고리즘은 영상 전체에 걸쳐 전경/배경 분할을 결정.
 - 입력 영상에 대한 부분 전경/배경 레이블링을 지정하는 한 가지 방법은 전경 객체 내부를 포함하는 내부 직사각형을 정의하는 방법.


  • Example
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

int main()
{
cv::Mat image= cv::imread("group.jpg");
cv::namedWindow("Original Image");
cv::imshow("Original Image",image);

// 입력 영상에 대한 부분 전경/배경 레이블을 지정하는 방법
// 전경 객체 내부를 포함하는 내부 직사각형을 정의
cv::Rect rectangle(10, 100, 380, 180);
// 경계 직사각형 정의
// 직사각형 밖의 화소는 배경으로 레이블링

// 입력 영상과 자체 분할 영상 외에 cv::grabCut 함수를 호출할 때
// 이 알고리즘에 의해 만든 모델을 포함하는 두 행렬의 정의가 필요
cv::Mat result; // 분할 (4자기 가능한 값)
cv::Mat bgModel, fgModel; // 모델 (초기 사용)
cv::grabCut (image, // 입력 영상
result, // 분할 결과
rectangle, // 전경을 포함하는 직사각형
bgModel, fgModel, // 모델
5, // 반복 횟수
cv::GC_INIT_WITH_RECT); // 직사각형 사용
// cv::CC_INT_WITH_RECT 플래그를 이용한 경계 직사각형 모드를 사용하도록 지정

// cv::GC_PR_FGD 전경에 속할 수도 있는 화소(직사각형 내부의 화소 초기값)
// cv::GC_PR_FGD와 동일한 값을 갖는 화소를 추출해 분할한 이진 영상을 얻음
cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);
// 전경일 가능성이 있는 화소를 마크한 것을 가져오기
cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255));
// 결과 영상 생성
image.copyTo(foreground, result);
// 배경 화소는 복사되지 않음

cv::namedWindow("Result");
cv::imshow("Result", result);

cv::namedWindow("Foreground");
cv::imshow("Foreground", foreground);

cv::waitKey(0);

return 0;
}

  • Result

 - 입력/결과 분할 영상은 4가지 값 중 하나가 될 수 있음.
 - cv::GC_BGD 확실히 배경에 속하는 화소 (예제의 직사각형 밖에 있는 화소)
 - cv::GC_FGC 확실히 전경에 속하는 화소
 - cv::GC_PR_BGD 배경에 속할 수도 있는 화소
 - cv::GC_PR_FGD 전경에 속할 수도 있는 화소 (예제에서 직사각형 내부에 화소 초기값)

 - 모든 전경 화소를 추출하기 위해 cv::GC_PR_FGD나 cv::GC_FGD와 동일 한 값으로 단순히 첫 번째 비트 값을 확인할 수 있음.

// 비트별 연산으로 첫 번째 비트 확인
result = result&1 // FG이면 1임

 - 상수를 1과 3인 값으로 정의하기 때문에 가능.
 - 다른 두 가지 상수가 있는데 0과 2로 정의.
 - 예제에서 분할 영상이 cv::GC_FGD 화소를 포함하지 않기 때문에 동일한 결과를 얻음.
 - 마지막으로 마스크로 복사함으로 써 전경 객체(흰색 배경 위)인 영상을 얻음.

// 결과 영상 생성
cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255)) // 모든 흰색 영상
image.copyTo(foreground, result); // 배경 화소는 복사되지 않음.

  • 예제 분석
 - 그랩 컷 알고리즘은 간단히 객체가 포함된 직사각형 내부를 지정해 전격 객체를 추출할 수 있음.
 - cv::grabCut 함수의 두 번째 인자로 분할 영상의 특정 일부 화소인 cv::GC_BGD와 cv::FGD 값을 할당할 수 있음.
 - 그 후 입력 모드 플래그인 GC_INIT_WITH_MASK를 지정.
 - 입력 레이블을 얻을 수 있는데, 사용자가 대화식으로 입력 영상의 몇 가지 요소를 마크.
 - 또한 두 가지 입력 모드를 조합하는 것도 가능.

 - 입력 정보를 사용하면 그랩컷은 배경/전경 분할을 생성.
 - 처음에 전경 레이블(cv::GC_PR_FGD)에 잠정적으로 마크하지 않은 모든 화소를 할당.
 - 현재 분류에 기반을 두고, 이 알고리즘은 화소를 유사 컬러의 클러스터로 묶음.
 - 다음 단계는 전경과 배경 화소간의 경계를 이용해 배경/전경 분할을 결정.
 - 유사한 레이블을 갖는 화소의 연결은 최적화 과정을 통해 수행하고, 상대적으로 균등한 명암도를 갖는 영역 내의 경계 위치에 대해서는 패널티로 봉쇄.
 - 최적화 문제는 그랩컷 알고리즘을 사용해 효율적으로 해결할 수 있고, 이 방법은 최적 구성을 조합하기 위해 컷이 적용된 연결 그래프를 대표해 문제에 대한 최적 해결책을 찾을 수 있음.

 - 취득한 분할에는 화소에 대한 새로운 레이블이 있음.
 - 클러스터링 과정을 반복하며 다시 새롭게 최적화를 거친 분할을 찾는 과정 등을 반복할 수 있음.
 - 즉, 그랩컷은 분할 결과를 점진적으로 개선할 때까지의 과정을 되풀이.
 - 장면이 복잡한지 여부에 달려 있다는 전제하에서 좋은 해결 방법은 반복을 많이 하거나 반복을 적게 함.

 - cv::grabCut 함수의 다섯 번째 인자는 사용자가 반복 횟수를 지정하는 것.
 - 알고리즘이 관리하는 두 내부 모델은 함수의 인자로 넘겨지고 추가 반복으로 수행한 분할 결과를 개선하기 원하면 마지막에 실행했던 모델 함수를 다시 호출.

  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook