OpenCV의 GrabCut을 사용해 보았습니다.
이것은 무엇인가
n번 달인데, 이미지의 전경 영역을 추출하기 위해 OpenCV의 GrabCut을 사용해 본 기사입니다.
배경
저의 프로그래밍 이외의 유일한 취미는 기타입니다만, 기타를 외형으로부터 간단하게 검색할 수 있는 서비스를 만들려고 합니다. 그 과정에서 기타 단체를 뽑아낸 이미지가 필요할 것 같아서, 배경을 삭제할 수 있는 수법 뭔가 없을까-라고 찾고 있었습니다.
특히 좋은 것을 찾지 못하면 세그멘테이션용 모델로 어떻게든 할 수 없을까라고 생각하고 있을 때 OpenCV의 GrabCut 간단하게 할 수 있는 것 같아서 시도해 보았습니다.
구현
구조에 대해서는 파이썬 튜토리얼 가 상세합니다.
수행하는 작업은 다음과 같습니다.
이번에는 기타 이미지를 무료 소재에서 가져 왔습니다.
이미지 로드
위의 3개의 화상은 화상 사이즈가 크기 때문에 미리 작게 해 둡니다.
def resize_image(filepath, width, height):
img = Image.open(filepath)
img.thumbnail((width, height),resample=Image.BICUBIC)
return img
IMG_BASE_PATH = './img/'
filename = 'guitar.jpeg'
WIDTH = 1000
HEIGHT = 1000
file_path = IMG_BASE_PATH + filename
pillow_img = resize_image(file_path, WIDTH, HEIGHT)
resized_image_path = f'{IMG_BASE_PATH}resized_{filename}'
pillow_img.save(resized_image_path)
크기 조정이 끝나면 OpenCV를 사용하여 다시 로드합니다. 사실은 Pillow로 불러온 이미지를 OpenCV의 이미지로 변환하는 방법이 있다고 생각합니다만, 이번은 이것으로 좋게 해 주세요.
img = cv2.imread(resized_image_path)
원하는 전경 둘러싸는 구형을 지정
GrabCut은 대화식 방식으로 전경을 추출하는 기술입니다. 그래서 사용하는 인간이 어디에 원하는 전경이 포함되어 있는지를 지정해 줄 필요가 있습니다. 이번은 귀찮아서 이미지의 거의 전부를 직사각형으로 둘러싸겠습니다.
# 引数はx座標, y座標, 幅, 高さ
rect = (1,1, pillow_img.size[0],pillow_img.size[1])
여기서 x 좌표와 y 좌표에서 0을 지정하면 GrabCut 실행시 다음 오류가 발생했습니다.
---------------------------------------------------------------------------
error Traceback (most recent call last)
<ipython-input-46-73e975b54ddd> in <module>
1 rect = (0,0, pillow_img.size[0],pillow_img.size[1])
----> 2 cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
error: OpenCV(4.1.2) /io/opencv/modules/imgproc/src/grabcut.cpp:386: error: (-215:Assertion failed) !bgdSamples.empty() && !fgdSamples.empty() in function 'initGMMs'
튜토리얼을 보면
사용자는 먼저 초기 값으로 전경 영역 주위에 직사각형을 그립니다 (전경 물체는이 직사각형에서 튀어 나와서는 안됩니다).
이번 경우는 튀어 나오지는 않지만 모든 영역을 둘러싸고 있기 때문에 안 되는 것 같아요.
GrabCut 실행
mask에 대한 numpy 배열과 GrabCut에 필요한 배열을 만듭니다.
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
반복 수는 튜토리얼 그대로 5에서 실행됩니다.
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
GrabCut 결과를 원본 이미지에 반영
반영하기 전에 GrabCut의 결과를 살펴 보겠습니다.
GrabCut은 이미지의 픽셀별로 네 가지 분류를 수행합니다. 튜토리얼에 따르면 이 네 가지 분류는 다음과 같은 의미를
마스크 화상 중의 화소치가 0의 화소는 배경, 1의 화소는 전경, 2의 화소는 배경다운, 3의 화소는 전경다운 화소를 의미합니다
마스크의 0,2 부분을 검은 색으로 채우면 전경 이미지 만 얻을 수 있습니다.
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
결과
처리 전
처리 후
대체로 좋은 느낌으로 잘라내고 있습니다만, 좌하의 배경과 바닥의 광택이 남아 버렸습니다. 진짜는 수정하는 것도 가능합니다만, 이번은 이 정밀도로 문제 없기 때문에 여기까지로 끝납니다.
마지막으로
OpenCV의 GrabCut을 사용해 보았습니다. 신경망을 사용하지 않고 여기까지 할 수있는 것에 놀랐습니다.
이미지 사이즈에 따라서는 속도가 비정상적으로 느리거나 하기 때문에 앞으로는 거기를 어떻게든 해결할 수 없는지를 찾아 가고 싶습니다.
참고로 한 사이트
Reference
이 문제에 관하여(OpenCV의 GrabCut을 사용해 보았습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/marutaku0131/items/657c32358cfad7817648텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)