파이썬 강의/openCV

파이썬 openCV 27. 형태학적 처리 : 침식/팽창(Erosion/Dilation)

마리사라 2021. 1. 12. 13:30
반응형

파이썬 openCV 27번째 강의는 형태학적 처리의 침식과 팽창(Erosion/Dilation)입니다.


0. 형태학적 처리?

 형태학적 처리는 특정한 모양의 형태소(structuring element)를 이진 영상에 적용해서 출력 영상을 만드는 연산입니다. 이러한 처리를 통해 영상을 필터링하거나 잡음을 제거하고, 세선화와 골격화를 할 수 있습니다

 이번 시간에는 형태학적 처리 중, 잡음을 제거하는 침식 연산과 영상을 뚜렷하게 하는 팽창 연산에 대해서 알아볼 예정입니다.

 

(1) 침식

침식 연산은 말 그대로 객체를 깎아내는 연산입니다. 이때 깎아내는 부분은 객체의 경계를 깎아내며, 이러한 연산을 통해 물체의 크기는 축소시키고, 배경은 확장시키는 역할을 합니다. 침식 연산은 두 가지의 대표적인 특징을 갖습니다.

  • 영상 내에서 잡음이나 불필요한 작은 물체를 제거
  • 영상에서 서로 닿은 물체를 분리

침식은 수학적으로도 표현할 수 있으며, 다음과 같은 식으로 나타납니다.

A = 원본 영상, B = 형태소

이진 영상에서 침식은 형태소의 형태와 원본 화소 주변의 형태가 같은 경우에 1을, 그 외에는 0을 출력합니다. 

 

(2) 팽창

 팽창 연산은 침식과 반대로 객체의 모양을 팽창시키는 연산입니다. 그렇기 때문에 침식과는 반대로 배경을 축소시키고 객체를 확장하는 역할을 합니다. 팽창 연사도 두 가지의 대표적인 특징을 갖습니다.

  • 객체 내분의 홀을 채움
  • 객체 간 연결을 강하게 함

팽창 역시 수학적으로 표현할 수 있으며, 다음과 같은 식으로 나타납니다.

A = 원본 영상, B = 형태소

이진 영상에서 팽창은 형태소의 형태와 원본 화소 주변의 형태를 비교했을 때, 하나라도 일치하는 것이 있으면 1을, 모두 불일치하면 0을 출력합니다.


1. openCV에서의 침식/팽창

openCV에서의 침식/팽창 연산을 다룰 때, 이진 영상에서 위에서 설명한 방법을 통해 할 수도 있습니다. 하지만 이번 시간에는 이진 영상이 아닌 그레이 스케일에서의 침식/팽창 연산을 알려 드리겠습니다.

img = cv2.imread('lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
height, width = gray.shape

우선 이미지를 그레이 스케일로 만들고, 그 크기를 구해줍니다.

 

out = np.zeros((height + 2, width + 2), dtype=np.uint8)
out[1:1 + height, 1:1+width] = gray.copy()
erode = np.zeros_like(gray)
dilate = np.zeros_like(gray)

주변에 +1만큼 제로 패딩을 해주고, 침식 연산과 팽창 연산의 결과가 들어갈 빈 영상을 만들어 줍니다.

 

for i in range(height):
    for j in range(width):
        temp = out[i:i+3, j:j+3]
        erode[i][j] = np.min(temp)
        dilate[i][j] = np.max(temp)

이제부터 중첩 for문을 사용하여 출력 화소를 계산합니다. 우선 3x3의 크기의 범위를 잡습니다. 이후 침식 연산일 경우 범위의 최솟값을, 팽창 연산일 경우 범위의 최댓값을 출력 화소로 합니다.

 

하지만 이렇게 한다면 이것이 진짜 침식 연산이나 팽창 연산인지 헷갈리시는 분들도 계실 겁니다.

array = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]])
erosion = cv2.erode(gray, array)
dilation = cv2.dilate(gray, array)

위 연산은 모든 값이 1인 3x3의 형태소를 가지고 침식 연산과 팽창 연산을 한 것과 같습니다. 그래서 해당 형태소를 가지고 openCV에서 지원하는 침식 함수와 팽창 함수에 각각 넣으면 같은 결과를 얻을 수 있습니다.

 

원본 영상
좌 : 중첩 for문을 통한 침식 연산 / 우 : 함수를 통한 침식 연산
좌 : 중첩 for문을 통한 팽창 연산 / 우 : 함수를 통한 팽창 연산

결과를 보시면 침식 연산은 선이 굻고 어둡게, 팽창 연산은 선이 가늘고 밝게 나오는 것을 보실 수 있습니다.


2. 마치며

침식과 팽창은 실제로도 많이 사용되는 연산입니다. 그렇기에 이진 영상에서의 사용법, 그레이 스케일에서의 사용법, openCV의 함수의 사용법을 모두 알아 가셨으면 합니다.

def erosion_dilation():
    img = cv2.imread('lenna.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    height, width = gray.shape

    out = np.zeros((height + 2, width + 2), dtype=np.uint8)
    out[1:1 + height, 1:1+width] = gray.copy()
    erode = np.zeros_like(gray)
    dilate = np.zeros_like(gray)

    for i in range(height):
        for j in range(width):
            temp = out[i:i+3, j:j+3]
            erode[i][j] = np.min(temp)
            dilate[i][j] = np.max(temp)

    array = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]])
    erosion = cv2.erode(gray, array)
    dilation = cv2.dilate(gray, array)

    cv2.imshow('original', gray)
    cv2.imshow('erosion', erode)
    cv2.imshow('dilation', dilate)
    cv2.imshow('erosion2', erosion)
    cv2.imshow('dilation2', dilation)
    cv2.waitKey(0)
반응형