본문 바로가기
Image Processing/OpenCV

[OpenCV] 모폴로지 (morphology)

by AteN 2022. 11. 25.

모폴로지(morphology)

모폴로지는 형태학이라는 뜻으로 다양한 학문 분야에서 사용되는 용어이다 

영상처리에서는 모폴로지는 영상의 객체들의 형태 (shape)를 분석하고 처리하는 기법이다. 이 형태학적 처리를 활용하여 영상의 경계, 골격, 블록 등의 형태를 표현하는데 필요한 요소를 추출한다. 

간단히 말하면, 영상 내에 존재하는 객체의 형태를 조금씩 변형시킴으로써 영상 내에서 불필요한 잡음을 제거하거나 객체를 두드러지게 하여 필요한 요소를 추출한다 

 

침식 연산 (erosion operation)

침식 연산은 객체를 침식시키는 연산이다. 따라서 객체의 크기가 축소되고, 배경은 확장된다 

객체의 크기가 축소되기 때문에 영상 내에 존재하는 잡은 같은 작은 크기의 객체들은 사라질 수 있다 

이러한 연산을 이용하여 소금-후추 (salt & papper) 잡음과 같은 임펄스 (impulse) 잡음 들을 제거할 수 있다. 또한 영상 내에서 객체의 돌출부를 감소시키기 때문에 서로 닿은 물체를 분리할 때도 유용하게 사용할 수 있다 

입력 영상의 화소와 마스크 원소가 한개의 화소라도 일치하지 않으면 출력 화소는 검은색 0이 된다. 반면 입력 영상의 화소와 마스크 원소와 비교해서 모두가 일치하면 출력 화소는 흰색인 1이 지정된다. 여기서 마스크 원소가 1인 위치만을 비교 대상으로 한다. 또한 마스크의 크기와 원소의 구성은 입력 영상의 형태에 따라서 사용자가 조정하여 더 나은 결과 영상을 생성할 수 있다. 

 

import numpy as np, cv2
 
def erode(img, mask=None):
    dst = np.zeros(img.shape, np.uint8)
    if mask is None: mask = np.ones((3, 3), np.uint8)
    ycenter, xcenter = np.divmod(mask.shape[:2], 2)[0]

    mcnt = cv2.countNonZero(mask)
    for i in range(ycenter, img.shape[0] - ycenter):           # 입력 행렬 반복 순회
        for j in range(xcenter, img.shape[1] - xcenter):
            y1, y2 = i - ycenter, i + ycenter + 1              # 마스크 높이 범위
            x1, x2 = j - xcenter, j + xcenter + 1              # 마스크 너비 범위
            roi = img[y1:y2, x1:x2]                            # 마스크 영역
            temp = cv2.bitwise_and(roi, mask)
            cnt  =  cv2.countNonZero(temp)                     # 일치한 화소수 계산
            dst[i, j] = 255 if (cnt == mcnt) else 0            # 출력 화소에 저장
    return dst
 
image = cv2.imread("img/morph.jpg", cv2.IMREAD_GRAYSCALE)
if image is None: raise Exception("영상파일 읽기 오류")
data = [0, 1, 0,                                               # 마스크 선언 및 초기화
        1, 1, 1,
        0, 1, 0]
mask = np.array(data, np.uint8).reshape(3, 3)
th_img = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY)[1]  # 영상 이진화
 
dst1 = erode(th_img, mask)                                     # 사용자 정의 침식 함수
dst2 = cv2.erode(th_img, mask)
# dst2 = cv2.morphologyEx(th_img, cv2.MORPH_ERODE, mask)         # OpenCV의 침식 함수
 
cv2.imshow("image", image)
cv2.imshow("binary image", th_img)
cv2.imshow("User erode", dst1)
cv2.imshow("OpenCV erode", dst2)
cv2.waitKey(0)
 
  • binary imgae은 왼쪽의 입력 영상을 이진 영상으로 만들어 모폴로지 처리를 준비한 영상이다.
  • User erode는 침식 연산을 구현해서 수행한 결과
  • OpenCV erode는 opencv에서 제공하는 함수를 사용하여 수행한 결과
  • 연산의 결과로 배경 부분에 있는 잡음들(흰색)이 제거 된것을 볼수 있다. 그런데 객체가 침식되므로 객체(글자)의 내부의 작은 공간(검은색)은 더 넓어진다.

팽창 연산 (dilation operation)

팽창 연산은 객체를 팽창시키는 연산이다 

객체의 가장 외곽 화소를 확장시키기 떄문에 객체의 크기는 확대되고, 배경은 축소된다. 또한 객체의 팽창으로 인해서 객체 내부에 있는 빈 공간도 매워지게 된다.

이진 영상에서 팽창 연산을 수행하는 과정을 나타낸 것이며, 마스크 범위의 입력 화소와 팽창 마크크의 원소 간 (element-wise)에 일치하는지를 비교한다. 팽창 마스크가 1인 원소와 해당 입력 화소가 모두 불일치하면 출력 화소로 0을 지저안다. 그리고 1개 화소라도 일치하게 되면 1이 출력 화소로 결정된다.

 

import numpy as np, cv2
 
def dilate(img, mask):
    dst = np.zeros(img.shape, np.uint8)
    if mask is None: mask = np.ones((3, 3), np.uint8)
    ycenter, xcenter = np.divmod(mask.shape[:2], 2)[0]

    for i in range(ycenter, img.shape[0] - ycenter):    # 입력 행렬 반복 순회
        for j in range(xcenter, img.shape[1] - xcenter):
            y1, y2 = i - ycenter, i + ycenter + 1       # 마스크 높이 범위
            x1, x2 = j - xcenter, j + xcenter + 1       # 마스크 너비 범위
            roi = img[y1:y2, x1:x2]                     # 마스크 영역
            temp = cv2.bitwise_and(roi, mask)
            cnt  = cv2.countNonZero(temp)
            dst[i, j] = 0 if (cnt == 0) else 255  # 출력 화소에 저장
    return dst
 
image = cv2.imread("img/morph.jpg", cv2.IMREAD_GRAYSCALE)
if image is None: raise Exception("영상파일 읽기 오류")
mask = np.array([[0, 1, 0],                         # 마스크 초기화
                 [1, 1, 1],
                 [0, 1, 0]]).astype("uint8")
th_img = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY)[1]  # 영상 이진화
dst1 = dilate(th_img, mask)                              # 사용자 정의 팽창 함수
dst2 = cv2.morphologyEx(th_img, cv2.MORPH_DILATE, mask)  # OpenCV의 팽창 함수
# dst2 = cv2.dilate(th_img, mask)
 
cv2.imshow("User dilate", dst1)
cv2.imshow("OpenCV dilate", dst2)
cv2.waitKey(0)
 
  • 팽창 연산의 수행 결과로 객체들이 전반적으로 확장되어 뚜력하게 나타나고, 객체 내부의 빈 공간이 매워지게 된다. 다만 배경 부분의 잡음들도 확장된 것을 볼 수 있다. 

 

댓글