그랩컷(GrabCut)이란?
- 그래프 컷(graph cut) 기반 영역 분할 알고리즘
- 영상의 픽셀을 그래프 정점으로 간주하고, 픽셀들을 두 개의 그룹(객체, 배경)으로 나누는 최적의 컷(Max Flow Minimum Cut)을 찾는 방식
이 알고리즘으로 객체와 배경을 구분할 수 있다. 객체를 가운데, 배경은 바깥 부분으로 간주한다.
그랩컷 영상 분할 동작 방식
- 객체 위치를 러프하게 사각형 형태로 주는 방식
- 객체 부분과 배경 부분을 마우스로 지정해주고 정보를 제공하고 다시 업데이트 하는 방식
✔︎ 그랩컷 함수 - cv2.grabCut
mask, bgdModel, fgdModel = cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode=None)
- img : 입력 영상. 8비트 3채널.
- mask : 입출력 마스크.
cv2.GC_BGD(0), cv2.GC_FGD(1), cv2.GC_PR_BGD(2), cv2.GC_PR_FGD(3) 네 개의 값으로 구성됨.
cv2.GC_INIT_WITH_RECT 모드로 초기화. - rect : ROI 영역. cv2.GC_INIT_WITH_RECT 모드에서만 사용됨.
- bgdModel : 임시 배경 모델 행렬. 같은 영상 처리시에는 변경 금지.
- fgdModel : 임시 전경 모델 행렬. 같은 영상 처리시에는 변경 금지.
- iterCount : 결과 생성을 위한 반복 횟수
- mode : cv2.GC_로 시작하는 모드 상수.
보통 cv2.GC_INIT_WITH_RECT 모드로 초기화하고, cv2.GC_INIT_WITH_MASK 모드로 업데이트함.
💬 그랩컷 영상 분할 예제
src = cv2.imread('nemo.jpg')
rc = cv2.selectROI(src)
mask = np.zeros(src.shape[:2], np.uint8)
cv2.grabCut(src, mask, rc, None, None, 5, cv2.GC_INIT_WITH_RECT)
# 0: cv2.GC_BGD, 2: cv2.GC_PR_BGD
mask2 = np.where((mask == 0) | (mask == 2), 0, 1).astype('uint8')
dst = src * mask2[:, :, np.newaxis]
mask = mask * 64
cv2.imshow('mask', mask)
cv2.imshow('dst', dst)
💬 마우스를 활용한 그랩컷 영상 분할 예제
src = cv2.imread('messi5.jpg')
mask = np.zeros(src.shape[:2], np.uint8) # 마스크
bgdModel = np.zeros((1, 65), np.float64) # 배경 모델
fgdModel = np.zeros((1, 65), np.float64) # 전경 모델
rc = cv2.selectROI(src)
cv2.grabCut(src, mask, rc, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask == 0) | (mask == 2), 0, 1).astype('uint8')
dst = src * mask2[:, :, np.newaxis]
# 초기 분할 결과 출력
cv2.imshow('dst', dst)
# 마우스 이벤트 처리 함수 등록
def on_mouse(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(dst, (x, y), 3, (255, 0, 0), -1)
cv2.circle(mask, (x, y), 3, cv2.GC_FGD, -1)
cv2.imshow('dst', dst)
elif event == cv2.EVENT_RBUTTONDOWN:
cv2.circle(dst, (x, y), 3, (0, 0, 255), -1)
cv2.circle(mask, (x, y), 3, cv2.GC_BGD, -1)
cv2.imshow('dst', dst)
elif event == cv2.EVENT_MOUSEMOVE:
if flags & cv2.EVENT_FLAG_LBUTTON:
cv2.circle(dst, (x, y), 3, (255, 0, 0), -1)
cv2.circle(mask, (x, y), 3, cv2.GC_FGD, -1)
cv2.imshow('dst', dst)
elif flags & cv2.EVENT_FLAG_RBUTTON:
cv2.circle(dst, (x, y), 3, (0, 0, 255), -1)
cv2.circle(mask, (x, y), 3, cv2.GC_BGD, -1)
cv2.imshow('dst', dst)
cv2.setMouseCallback('dst', on_mouse)
while True:
key = cv2.waitKey()
if key == 13: # ENTER
# 사용자가 지정한 전경/배경 정보를 활용하여 영상 분할
cv2.grabCut(src, mask, rc, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_MASK)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
dst = src * mask2[:, :, np.newaxis]
cv2.imshow('dst', dst)
elif key == 27:
break
⬇︎
객체 검출을 잘 못하였을 때 직접 배경과 전경을 마우스로 지정하고 업데이트 할 수 있다.
'OpenCV' 카테고리의 다른 글
[OpenCV with Python] 영상 분할과 객체 검출 : 템플릿 매칭 1 (0) | 2022.02.04 |
---|---|
[OpenCV with Python] 영상 분할과 객체 검출 : 모멘트 기반 객체 검출 (0) | 2022.02.04 |
[OpenCV with Python] 이진 영상 처리 : 다양한 외곽선 함수 (0) | 2022.02.04 |
[OpenCV with Python] 이진 영상 처리 : 외곽선 검출 (0) | 2022.02.04 |
[OpenCV with Python] 이진 영상 처리 : 레이블링 (0) | 2022.02.04 |