조금씩 꾸준히 완성을 향해

[OpenCV/Python] 모폴로지(Morphology) 연산 - 침식(erode), 팽창(dilate), 열림(open), 닫힘(close) 본문

Python/OpenCV

[OpenCV/Python] 모폴로지(Morphology) 연산 - 침식(erode), 팽창(dilate), 열림(open), 닫힘(close)

all_sound 2022. 11. 13. 16:21

# 모폴로지 연산

  • Morphology : 형태 또는 모양에 관한 학문
  • 모폴로지 변환의 팽창(dilation) 침식(erosion)을 기본 연산으로 사용해 고급 형태학을 적용하는 변환 기법
  • 그레이 스케일 영상, 이진영상 모두 적용 가능
  • 구조 요소( Structuring Element)
    • 다양한 형태를 가지지만 주로 정방형을 사용
    • 구조 요소에 따라 형태가 달라짐

cv2.getStrucuringElement()

  • shape : element의 모양
    • morph_rect 사각형
    • morph_cross 십자 모양
    • morph_ellipse 타원형 모양
    • ksize : structuring element 사이즈
      => rectangle을 일반적으로 가장 많이 사용
# shape 확인 및 비교
kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT, ksize=(5,5))
kernel_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, ksize=(5,5))
kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, ksize=(5,5))

print('-kernel rect')
print(kernel_rect)
print('-kernel cross')
print(kernel_cross )
print('-kernel ellipse')
print(kernel_ellipse)

plt.style.use('grayscale')
plt.figure(figsize=(12, 4))
ax1 = plt.subplot(131)
ax2 = plt.subplot(132)
ax3 = plt.subplot(133)

ax1.imshow(kernel_rect)
ax2.imshow(kernel_cross)
ax3.imshow(kernel_ellipse)
ax1.set_title('rectangle')
ax2.set_title('cross')
ax3.set_title('ellipse')
ax1.axis('off')
ax2.axis('off')
ax3.axis('off')
plt.show()

# 침식 (erosion)

  • 객체 영역을 깎아냄 -> 축소
kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT, ksize=(3,3))
kernel_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, ksize=(3,3))
kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, ksize=(3,3))

img = np.zeros((12,12), dtype=np.uint8) 
img[3, 5:7]= 255
img[4:9, 2:10] = 255
img[8, 5:7]= 0

dst1 = cv2.erode(img, kernel_rect, iterations=1) # iterations: 횟수
dst2 = cv2.erode(img, kernel_cross, iterations=1)
dst3 = cv2.erode(img, kernel_ellipse, iterations=1)

imgs = [img, dst1, dst2, dst3]
titles = ['original', 'erode_rect', 'erode_cross', 'erode_ellipse']

plt.style.use('grayscale')
plt.figure(figsize=(15, 5))
plt.style.use('grayscale')
for i in range(4):
    ax = plt.subplot(1, 4, i+1)
    ax.imshow(imgs[i])
    ax.set_title(titles[i])
    ax.axis('off')
plt.show()

# 팽창 (dilate)

  • 객체 영역을 덧붙임 -> 확대
dst1 = cv2.dilate(img, kernel_rect, iterations=1)
dst2 = cv2.dilate(img, kernel_cross, iterations=1)
dst3 = cv2.dilate(img, kernel_ellipse, iterations=1)
dst4 = cv2.dilate(img, kernel_rect, iterations=2)
dst5 = cv2.dilate(img, kernel_cross, iterations=2)
dst6 = cv2.dilate(img, kernel_ellipse, iterations=2)

imgs = [img, dst1, dst2, dst3, dst4, dst5, dst6]
titles = ['original', 'rect_iter_1', 'cross_iter_1', 'ellipse_iter_1', 
              'rect_iter_2', 'cross_iter_2','ellipse_iter_2']

plt.style.use('grayscale')
plt.figure(figsize=(14, 7))
for i in range(7):
    ax = plt.subplot(2, 4, i+1)
    ax.imshow(imgs[i])
    ax.set_title(titles[i])
    ax.axis('off')

# 열림 (open)

  • 침식 + 팽창
dst1 = cv2.erode(img, kernel_rect, iterations=1)
dst2 = cv2.dilate(dst1, kernel_rect, iterations=1)

imgs = [img, dst1, dst2]
titles = ['original', 'erosion', 'dilate']

plt.style.use('grayscale')
plt.figure(figsize=(12, 4))
for i in range(3):
    ax = plt.subplot(1, 3, i+1)
    ax.axis('off')
    ax.set_title(titles[i])
    ax.imshow(imgs[i])
plt.show()

# 닫힘 (close)

  • 팽창 + 침식
dst1 = cv2.dilate(img, kernel_rect, iterations=1)
dst2 = cv2.erode(dst1, kernel_rect, iterations=1)

imgs = [img, dst1, dst2]
titles = ['original', 'dilate', 'erosion',]

plt.style.use('grayscale')
plt.figure(figsize=(12, 4))
for i in range(3):
    ax = plt.subplot(1, 3, i+1)
    ax.axis('off')
    ax.set_title(titles[i])
    ax.imshow(imgs[i])
plt.show()

# 기타 모폴로지 연산 - 빼기 연산

  • morph_gradient : 팽창-침식
  • morph_tophat : 원본-열기
  • morp_blackhat : 닫기-원본
erode1 = cv2.erode(img, kernel_rect, iterations=1)
dilate1 = cv2.dilate(img, kernel_rect, iterations=1)
open1 = cv2.dilate(erode1, kernel_rect, iterations=1)
close1 = cv2.erode(dilate1, kernel_rect, iterations=1)

gradient = dilate1 - erode1 #그라디언트
tophat = img - open1 #열기에서 사라진 부분 추출
blackhat = close1 - img #닫기에서 사라진 부분 추출

imgs = [gradient, tophat, blackhat]
titles = ['gradient', 'tophat', 'blackhat',]

plt.style.use('grayscale')
plt.figure(figsize=(12, 4))
for i in range(3):
    ax = plt.subplot(1, 3, i+1)
    ax.axis('off')
    ax.set_title(titles[i])
    ax.imshow(imgs[i])
plt.show()

# 함수로 구현
open1 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=1)
close1 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=1)
gradient1 = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel, iterations=1)
tophat1 = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel, iterations=1)
blackhat1 = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel, iterations=1)

# 실습

  • 다운받은 이미지를 열기, 닫기, 팽창, 침식을 적절히 활용하여 홀과 노이즈 제거
src = cv2.imread('morphology.jpg')

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, ksize=(3,3))

erode = cv2.erode(src, kernel, iterations=5) # iterations 5번 반복
# 노이즈들은 제거했으나 부작용으로 홀이 커짐 (침식됨)

dilate = cv2.dilate(src, kernel, iterations=5)
# 홀은 메꾸어주나 밖에 있는 노이즈들이 커지는 부작용

# dilation + erosion 
erode2 = cv2.erode(dilate, kernel, iterations=8) # iterations 8번 반복
                   
# dilation + erosion + dilation 
dilate2 = cv2.dilate(erode2, kernel, iterations = 2) # iterations 2번 반복

# 결과 확인
imgs = [src, erode, dilate, erode2, dilate2]
titles = ['src', 'erode', 'dilate', 'erode2', 'dilate2']
plt.style.use('grayscale')
plt.figure(figsize=(20, 10))
for i in range(5):
    ax = plt.subplot(1, 5, i+1)
    ax.axis('off')
    ax.set_title(titles [i])
    ax.imshow(imgs[i])
plt.show()

  • 노이즈만 추출
kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(3,3))
closing = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel, iterations = 5)
opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel, iterations = 5)
tophat = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel, iterations = 5)
blackhat = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel, iterations = 5)

imgs = [closing, opening, tophat, blackhat, tophat+blackhat]
titles = ['closing', 'opening', 'tophat', 'blackhat', 'tophat+blackhat']

plt.style.use('grayscale')
plt.figure(figsize=(20, 10))
for i in range(5):
    ax = plt.subplot(1, 5, i+1)
    ax.axis('off')
    ax.set_title(titles [i])
    ax.imshow(imgs[i])
plt.show()

  • 모폴로지 테두리만 추출
gradient = cv2.morphologyEx(opening, cv2.MORPH_GRADIENT, kernel)
plt.style.use('grayscale')
plt.figure(figsize=(5, 5))
plt.axis('off')
plt.imshow(gradient)