본문 바로가기
AI-driven Methodology/CV (Computer Vision)

[Computer Vision] Morphological Operations

by goatlab 2021. 12. 13.
728x90
반응형
SMALL

형태학적 연산 (Morphological Operations)

 


kernel과 이미지를 convolution하여 수행할 수 있는 기본 연산이다. 변환은 binary 이미지에서 수행된다. 작업은 침식, 팽창, 열기, 닫기 및 기타 등이 있다.

 

이진화한 이미지에서는 같은 값을 가진 픽셀이 이웃하여 있으면 이를 형태학적 영역(morphological region)으로 생각할 수 있다. 이미지의 형태학적 변환(morphological transformation)은 이미지 필터링을 사용하여 영역을 변화시키는 방법이다.

 

변환에 적용할 커널은 getStructuringElement 함수로 생성한다.

 

getStructuringElement(shape, ksize)
  • shape : 커널 모양
    • cv2.MORPH_RECT: 사각형
    • cv2.MORPH_ELLIPSE: 타원형
    • cv2.TMORPH_CROSS: 십자
  • ksize : 커널 크기
cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]], dtype=uint8)
cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
array([[0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0]], dtype=uint8)
cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
array([[0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0]], dtype=uint8)

 

침식 (Erosion)


침식 기법은 각 픽셀에 커널을 적용하여 커널 영역 내의 최솟값으로 해당 픽셀을 대체한다. 이진화된 이미지에서는 0인 영역이 증가한다. erosion은 noise 제거에 도움이 된다. 이름에서 알 수 있듯이 이 작업은 전경 개체의 경계를 erosion하여 더 얇게 만든다. 이 아이디어는 kernel이 이미지와 convolution할 때 kernel 아래의 모든 pixel이 1인 경우에만 이미지의 특정 pixel value가 1로 유지된다는 것이다. 그렇지 않으면 픽셀 값이 0이 된다.

 

erode(src, kernel)

 

다음 코드는 이미지를 여러 커널 모양을 이용하여 침식 기법으로 변환한다. 검은색이 0인 영역, 흰색이 1인 영역이다. 검은색 (0)이 흰색을 침식해 들어가는 것을 알 수 있다.

 

from skimage.data import horse

img = horse().astype('uint8')
img = np.ones(img.shape) - img

ksize = (20, 20)
kernel = {}
kernel[0] = cv2.getStructuringElement(cv2.MORPH_RECT, ksize)
kernel[1] = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, ksize)
kernel[2] = cv2.getStructuringElement(cv2.MORPH_CROSS, ksize)
title = ["사각형 커널", "타원 커널", "십자가 커널"]

plt.subplot(2, 2, 1)
plt.imshow(img, cmap="gray")
plt.title("원본 이미지")
plt.axis('off')
for i in range(3):
    erosion = cv2.erode(img, kernel[i])
    plt.subplot(2, 2, i+2)
    plt.imshow(erosion, cmap='bone')
    plt.title(title[i])
    plt.axis('off')
plt.tight_layout()
plt.show()

 

팽창 (Dilation)


팽창 (Dilation)은 침식과 반대로 커널 영역내의 최댓값으로 해당 픽셀을 대체하는 것이다. OpenCV에서는 dilate 함수로 구현되어 있다. dilation은 일반적으로 물체 면적을 늘리기 위해 침식 후에 수행된다. erosion은 noise를 제거하고 경계를 얇게 만들것과는 반대이다. 즉, 객체 경계를 더 두껍게 만든다. convolution이 발생할 때 kernel 아래에 값이 1인 pixel이 하나 이상 있으면 원본 이미지의 pixel 값은 1이 된다.

 

plt.subplot(2, 2, 1)
plt.imshow(img, cmap="gray")
plt.title("원본 이미지")
plt.axis('off')
for i in range(3):
    erosion = cv2.dilate(img, kernel[i])
    plt.subplot(2, 2, i+2)
    plt.imshow(erosion, cmap='bone')
    plt.title(title[i])
    plt.axis('off')
plt.tight_layout()
plt.show()

 

그레디언트

 

그레디언트는 팽창으로 확장시킨 영역에서 침식으로 축소시킨 영역을 빼서 윤곽선을 파악하는 것이다.

 

이와 유사한 기법으로 오프닝 (Opening)과 클로징 (Closing)이 있다. 오프닝은 침식을 적용한 뒤 팽창을 적용하는 것으로 영역이 점점 둥글게 되므로 점 잡음, 작은 물체, 돌기 등을 제거하는데 적합하다. 클로징은 반대로 팽창을 적용한 뒤 침식을 적용하여 영역이 영역이 붙기 때문에 전체적인 윤곽을 파악하는데 적합하다.

 

Opening


이 작업은 단순히 erosion 후 dilation이다. 객체 경계를 유지하면서 noise를 제거하는 데 도움이 된다.

Closing


이 작업은 opening과 정반대이다. 즉, dilation 후 erosion이 뒤따른다. 이미지 개체의 작은 간격을 줄이는 데 도움된다.

 

그레디언트를 구하려면 OpenCV의 morphologyEx라는 함수를 사용한다.

 

morphologyEx(src, op, kernel)
  • src : 원본 이미지
  • op :
    • cv2.MORPH_OPEN: cv2.dilate(cv2.erode(image))
    • cv2.MORPH_CLOSE: cv2.erode(cv2.dilate(image))
    • cv2.MORPH_GRADIENT: cv2.dilate(image) - cv2.erode(image)
    • cv2.MORPH_TOPHAT: image - opening(image)
    • cv2.MORPH_BLACKHAT: image - closing(image)
  • kernel : 커널
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, 
    cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20, 20))
)

closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, 
    cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20, 20))
)

gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, 
    cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
)

images = [img, gradient, opening, closing]
titles = ["원본 이미지", 'Gradient', 'Opening', 'Closing']

for i in range(4):
    plt.subplot(2, 2, i+1)
    plt.imshow(images[i], cmap='gray')
    plt.title(titles[i])
    plt.axis('off')

plt.tight_layout()
plt.show()
728x90
반응형
LIST