정보처리기사 필기를 시험보고 일주일을 쉬었다.
저번에 이어서 다시 시작하겠다.
GrabCut 알고리즘
이미지 분할(Image Segmentation) 알고리즘으로
이미지에서 전경(foreground)과 배경(background)을 분리하는 것이다.
위 순서를 정확한 전경과 배경 분리를 이룰때까지 반복한다.
간단하게 배경제거를 하고싶은 부분에서 배경과 전경을 구별하고 알고리즘을 적용해서 분류해나가는 작업인데 직접 실습을 진행해보자.
우선 이미지의 원하는 부분의 박스를 표시하는 예제이다.
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 = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
GrabCut 알고리즘에서 내부적으로 사용하는 배열로
각각 배경과 전경의 가우시안 혼합 모델을 저장 한다.
관심의 영역(빨간 박스)를 수동으로 직접 설정했다.
x1, y1, x2, y2
GrabCut 알고리즘에서 사용할 사각형을 정의
형식은 (x, y, width, height)
해당 박스를 원본이미지를 건들지 않기위해 copy 이미지에 그린다.
cv2.rectangle(copy, start, end, (0,0,255), 3)
imshow("Input Image", copy)
이 작업은 GrabCut 알고리즘을 실행하기 전에 사용자에게 선택된 영역을 시각적으로 확인할수 있게 하는 부분으로.
이론의 1,2 단계에 해당한다.
GrabCut 알고리즘을 사용하여 주어진 사각형 영역을 기준으로 전경과 배경을 분리하는 작업을 수행한다.
GrabCut 알고리즘을 5회 반복 실행한다는 의미의 코드로 인자는 아래와 같다.
cv2.grabCut(image, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
cv2.grabCut을 실행하면서 Mask에 대한 결과이다.
Mask 의 결과를 보면 박스 안에 전경(여자) 배경(여자를 제외한 박스 내부) 로 구성돼있는것을 확인할 수 있다.
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 * mask2[:,:,np.newaxis]
기존 원본의 이미지에서 mask2[전경(여자) = 1, 배경(박스 내부 전경 제외 부분) = 0] 을 곱하면 이미지에서 여자를 제외한 나머지 부분은 0으로 출력된다는 뜻이므로
image는 여자(전경)를 제외한 나머지 부분은 검은색으로 출력된다 = 배경제거
여기서 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)