그랩컷(GrabCut)

그랩컷 알고리즘은 영국 캠브릿지 마이크로소프트 연구소의 Carsten Rother, Vladimir Kolmogorov 및 Andrew Blake가 만들었다. 그들의 논문인  “GrabCut”: interactive foreground extraction using iterated graph cuts 에서 확인할 수 있다. 이 알고리즘은 최소한의 상호 작용으로 전경(객체) 추출을 돕는다.

처음에 사용자가 전경 영역 주변에 사각형(Rectangle)을 그린다. 그러면 알고리즘은 최상의 결과를 얻기 위해 반복적으로 사각형 영역을 분할하여 전경을 추출한다. 그러나 완벽하게 분할이 되진 않는다. 예를 들어 일부 전경 영역을 배경으로 표시하거나 그 반대로 표시하기도 한다.

그랩컷 알고리즘은 이런 경우 전경/배경에 대한 정보를 사용자로부터 추가적으로 받아서 좀 더 정밀하게 이를 수행하여 더 나은 결과를 만들어 낸다.

그랩컷 내부에서는 어떤일이?

  • 입력된 사각형 외부의 모든 항목은 배경으로 간주되고, 사각형 내부에 대해서는 아직은 알지 못한다. 그랩컷 수행시 사용자가 정의한 전경/배경 정보는 그대로 적용되며 변경하지 않는다.
  • 우리가 제공한 데이터에 따라 초기 레이블링을 수행한다. 레이블링은 개별픽셀을 전경 및 배경으로 구분짓는 작업을 의미한다.
  • 이제 GMM(Gaussian Mmixture Model)을 사용하여 전경과 배경을 모델링한다.
  • 우리가 준 데이터에 따라, GMM은 새로운 픽셀 분포를 학습하고 생성한다. 즉, 알 수 없는 픽셀은 색상 통계 측면에서 레이블이 지정된 다른 픽셀과의 관계에 따라 PR(Probable) 전경 또는 PR(Probable) 배경으로 레이블이 지정된다.
  • 이 픽셀 분포로부터 그래프가 작성되고, 픽셀은 그래프의 노드가 된다. ‘소스 노드’와 ‘싱크 노드’라는 두 개의 노드가 부가적으로 추가된다. 모든 전경 픽셀은 소스 노드에 연결되고 모든 배경 픽셀은 싱크 노드에 연결된다.
  • 픽셀을 소스 노드 또는 끝 노드에 연결하는 엣지의 가중치는 픽셀이 전경 또는 배경일 확률에 의해 정해진다. 픽셀 간의 가중치는 엣지 정보 또는 픽셀 유사도로 정의된다. 픽셀 색상의 차이가 크면 픽셀 사이의 가장자리가 낮은 가중치를 갖게 된다.
  • 그런 다음 mincut 알고리즘을 사용하여 그래프를 분할한다. 이는 최소비용 함수(minimum cost function)를 사용하여 그래프를 두 개의 분리된 소스 노드와 싱크 노드로 자른다. 비용 함수는 절단된 모서리의 모든 가중치의 합이다. 절단 후 소스 노드에 연결된 모든 픽셀은 전경이 되고 싱크 노드에 연결된 픽셀은 배경이 된다.
  • 분류(classfication)가 수렴할 때까지 프로세스가 계속된다.

위 과정을 아래의 이미지로 확인하자.

grabcut_scheme.jpg

GrabCut 함수

OpenCV에서 제공하는 grabCut() 함수 매개변수에 대해서 알아보자.

Imgproc.grabCut(
    Mat img, 
    Mat mask, 
    Rect rect, 
    Mat bgdModel, 
    Mat fgdModel, 
    int iterCount, 
    int mode
)
  • img – 입력 이미지
  • mask – 확실한 배경(GC_BGD=0), 확실한 전경(GC_FGD=1) 또는 배경 가능성이 있는(GC_PR_BGD=3)/ 전경 가능성이 있는(GC_PR_FGD=4) 등의 영역을 지정하는 마스크 이미지다.
  • rect – 전경 객체 좌표
  • bgdModel, fgdModel – 임시 (전경/배경) 모델, 알고리즘에서 내부적으로 사용하는 행렬. 같은 영상 처리 시에는 변경 금지.
  • iterCount – 결과 생성을 위한 반복 횟수.
  • mode: 특별히 입력 마스크 없고 rect로만 그랩컷 하는 경우 GC_INIT_WITH_RECT. 사용자가 지정한 마스크가 있는 경우 GC_INIT_WITH_MASK로 초기화.

GrabCut 예제

class GrabCutRGBUseCase @Inject constructor(){

operator fun invoke(rgbSrc:Mat, mask:Mat, rect: Rect) :Mat{
val bgdModel = Mat()
val fgdModel = Mat()

Imgproc.grabCut(
rgbSrc,
mask,
rect,
bgdModel,
fgdModel,
1,
Imgproc.GC_INIT_WITH_MASK
)

val source = Mat(
1,
1,
CvType.CV_8U,
Scalar(Imgproc.GC_PR_FGD.toDouble())
)
val foregroundMask = Mat()
Core.compare(
mask,
source,
foregroundMask,
Core.CMP_EQ
)
val foreground = Mat(
rgbSrc.size(),
CvType.CV_8UC3,
BLACK
)

rgbSrc.copyTo(foreground, foregroundMask)

return foreground
}
}

카테고리: OpenCV

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.