배경 제거를 위한 GrabCut 알고리즘

김성빈·2024년 5월 21일
0

Modern Computer Vision

목록 보기
33/117

정보처리기사 필기를 시험보고 일주일을 쉬었다.

저번에 이어서 다시 시작하겠다.

GrabCut 알고리즘

이미지 분할(Image Segmentation) 알고리즘으로

이미지에서 전경(foreground)과 배경(background)을 분리하는 것이다.


grabcut 알고리즘 docs

이론

  1. 우선 이미지의 관심영역을 포함하는 사각형을 이미지에 그린다.
  2. 사각형 내부는 전경으로 외부는 배경으로 초기 레이블링표시
  3. GMM(가우시한 혼합모델) 각 픽셀의 확률 계산
    사각형을 기준으로 전경, 배경의 각 색상 분포를 모델링 후 픽셀이 속할 확률을 계산한다.
  4. 픽셀 분포 학습
    GMM 이 데이터를 기반으로 픽셀 분포를 학습하고, 알 수 없는 픽셀을 전경 또는 배경으로 분류
  5. 그래프 구축
    각 픽셀을 노드로 하는 그래프를 만들고, 소스(전경) 노드와 싱크(배경) 노드를 추가
    전경 픽셀은 소스 노드에, 배경 픽셀은 싱크 노드에 연결
  6. 엣지 가중치 정의
    픽셀과 소스/싱크 노드 간의 엣지 가중치는 픽셀이 전경/배경일 확률로 정의
  7. 최소 컷 알고리즘 적용
    최소 컷 알고리즘을 사용하여 그래프를 소스 노드와 싱크 노드를 분리하는 최소 비용 함수로 분할
    소스 노드에 연결된 모든 픽셀은 전경이 되고, 싱크 노드에 연결된 픽셀은 배경이 된다.

위 순서를 정확한 전경과 배경 분리를 이룰때까지 반복한다.

실습

간단하게 배경제거를 하고싶은 부분에서 배경과 전경을 구별하고 알고리즘을 적용해서 분류해나가는 작업인데 직접 실습을 진행해보자.

우선 이미지의 원하는 부분의 박스를 표시하는 예제이다.

image = cv2.imread('woman.jpeg')
copy = image.copy()

# 동일한 마스크(0 suint8 data type) 생성size (width, height) as our original image 
mask = np.zeros(image.shape[:2], np.uint8)

bgdModel = np.zeros((1,65), np.float64)
fgdModel = np.zeros((1,65), np.float64)

# 수동으로 설정하거나 cv2.selectROI()로 선택
x1, y1, x2, y2 = 190, 70, 350, 310
start = (x1, y1)
end = (x2, y2)

# Format is X,Y,W,H
rect = (x1,y1,x2-x1,y2-y1)

# Show Rectangle
cv2.rectangle(copy, start, end, (0,0,255), 3)
imshow("Input Image", copy)

원본 이미지를 수정하지 않기 위해 복사본을 만들고,

copy = image.copy()

# 동일한 마스크(0 uint8 data type) 생성 (원본 이미지와 동일한 크기)
mask = np.zeros(image.shape[:2], np.uint8)

원본 이미지와 동일한 크기를 갖는 mask 배열을 생성하고

이 mask는 Grabcut 알고리즘에서 전경과 배경을 구분하는데 사용된다.

bgdModel과 fgdModel

bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)

GrabCut 알고리즘에서 내부적으로 사용하는 배열로
각각 배경과 전경의 가우시안 혼합 모델을 저장 한다.

관심의 영역(빨간 박스)를 수동으로 직접 설정했다.

x1, y1, x2, y2

rect = (x1, y1, x2 - x1, y2 - y1):

GrabCut 알고리즘에서 사용할 사각형을 정의

형식은 (x, y, width, height)

해당 박스를 원본이미지를 건들지 않기위해 copy 이미지에 그린다.

cv2.rectangle(copy, start, end, (0,0,255), 3)
imshow("Input Image", copy)

이 작업은 GrabCut 알고리즘을 실행하기 전에 사용자에게 선택된 영역을 시각적으로 확인할수 있게 하는 부분으로.

이론의 1,2 단계에 해당한다.

실습 2

GrabCut 알고리즘을 사용하여 주어진 사각형 영역을 기준으로 전경과 배경을 분리하는 작업을 수행한다.

GrabCut 함수 사용

GrabCut 알고리즘을 5회 반복 실행한다는 의미의 코드로 인자는 아래와 같다.

  • image: 원본 이미지
  • mask: 전경과 배경을 구분하는 마스크
  • rect: 사용자가 지정한 관심 영역
  • bgdModel과 fgdModel: GrabCut 알고리즘에서 사용되는 배경과 전경의 가우시안 혼합 모델
  • 5: 반복 횟수
  • cv2.GC_INIT_WITH_RECT: 사각형을 초기화 모드로 사용한다는 것을 지정.

Mask

cv2.grabCut(image, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)

cv2.grabCut을 실행하면서 Mask에 대한 결과이다.

Mask 의 결과를 보면 박스 안에 전경(여자) 배경(여자를 제외한 박스 내부) 로 구성돼있는것을 확인할 수 있다.

Mask2

Mask를 이진화 한 이미지로, 인자를 보면 mask==2, mask==0 에대한 내용이 나오는데 아래 내용을 알아야한다.

cv2.GC_BGD (0): 확실한 배경
cv2.GC_FGD (1): 확실한 전경
cv2.GC_PR_BGD (2): 잠재적인 배경
cv2.GC_PR_FGD (3): 잠재적인 전경

확실한 배경, 잠재적인 배경을 0(검은색)으로 설정하고 그 외(확실한, 잠재적인 전경)의 것들을 1(흰색)로 설정해 이진화를 진행하여

전경과 배경을 명확하게 구분한 Mask2

# 마스크를 수정하여 모든 0픽셀과 2픽셀을 배경(0)으로, 모든 1픽셀과 3픽셀을 전경(1)으로 설정합니다.
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')

Image

최종적으로 배경이 제거된 이미지

image = image * mask2[:,:,np.newaxis]

기존 원본의 이미지에서 mask2[전경(여자) = 1, 배경(박스 내부 전경 제외 부분) = 0] 을 곱하면 이미지에서 여자를 제외한 나머지 부분은 0으로 출력된다는 뜻이므로

image는 여자(전경)를 제외한 나머지 부분은 검은색으로 출력된다 = 배경제거

3차원(이미지), 2차원(Mask2) 곱셈연산 방법

여기서 mask2에다 다른 작업을 해줬는데

mask2[:,:,np.newaxis]

image는 3차원이고 mask2는 2차원 (height, width) 이므로 둘이 곱셈 연산을 수행하려면

mask2도 3차원(height, width, channels) 이 돼야한다

그래서 배열의 새로운 축을 추가하는 np.newaxis을 이용했는데,

mask2[:, :, np.newaxis]의 shape은 (height, width, 1)

height, width는 이미 있으므로 :,:, 으로 표현했고, 그 뒤는 추가를 해야하는 축이므로 newaxis가 들어간것

코드

cv2.grabCut(image, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)

mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
image = image * mask2[:,:,np.newaxis]

imshow("Mask", mask * 80)
imshow("Mask2", mask2 * 255)
imshow("Image", image)
profile
감사합니다. https://www.youtube.com/channel/UCxlkiu9_aWijoD7BannNM7w

0개의 댓글