형태학적 처리(mathematical morphology)

Seungpil Choi·2022년 7월 25일
0

형태학적 처리(mathematical morphology)

형태학적 처리란?

형태학적 처리란 특정한 모양의 형태소(structuring element)를 영상에 적용하여 출력 영상을 생성하는 연산이다. 모폴로지 연산이라고도 번역된다. 가장 기본적인 형태학적 처리 연산은 팽창과 침식 연산이다. 형태학(morphology)은 물체의 형태와 구조를 다루는 학문 분야이다. 형태학적 처리는 물체의 내재된 구조를 명확히 하는 데 이용된다. 예를 들어서 물체의 외곽선을 세션화한다거나 골격선(skeleton)을 추출하는 데 사용된다. 형태학적 처리의 대표적인 연산은 침식과 팽창 연산이고, 이 2개를 결합한 닫힘 연산과 열림 연산이 있다. 왜 침식이나 팽창 연산을 하는 것일까? 영상에서 잡음은 보통 크기가 작다. 영상에 존재하는 모든 객체를 침식시키면 잡음은 없어질 수 있다. 또 객체 내부에 조그마한 홀이 있다면 팽창 연산에 의하여 채워질 것이다. 만약 붙어 있는 객체를 때려는 경우에도 침식 연산을 사용할 수 있다. 객체를 연속하여 침식시키면 골격선을 구할 수 있다.

침식과 팽창 연산

형태학적 처리에는 이진처리그레이스케일 처리가 있다. 이진 처리는 이진화 연산을 통해 생성된 이진 영상에 대해 적용된다. 형태학적 처리에서는 영상안에 있는 화소들을 AND와 OR같은 논리적인 연산을 사용하여 화소들을 순차적으로 처리한다. 각 화소 위치에서 인접 화소들은 형태소(structuring element)와 비교된다. 침식 연산에서는 모든 화소들이 형태소와 일치하면 출력 화소값은 1이 되고 그렇지 않으면 0이 된다. 팽창 연산에서는 1개 이상만 일치하면 1이 되고 그렇지 않으면 0이 된다. 형태소는 공간 필터링에서의 마스크(mask)와 유사한 개념이다. 형태소의 값들은 0또는 1이된다. 일반적으로 3X3, 5X5크기의 정사각형이며 형태소의 값은 0이나 1을 가질 수 있으며 또한 don't care상태도 가질 수 있다.

침식 연산

침식 연산은 물체의 크기를 축소한다. 침식과 팽창은 주로 스파이크 잡음이나 끊어진 에지같은 작은 크기의 물체를 제거하는 데 쓰인다.

  • getStructuringElement() 매개변수

    • shape : 연산에 사용되는 형태소의 종류. 3가지 형태 중 하나를 선택할 수 있다.
      • MORPH_RECT : 사각형
      • MORPH_CROSS : 십자가
      • MORPH_ELLIPSE : 타원형
    • ksize : 형태소의 크기
    • anchor : 형태소의 기준 위치. 만약 (-1,-1)로 지정하면 기준점은 형태소의 중심이 된다.
  • erode() 매개변수

    • src : 소스 이미지
    • kernel : 우리가 침식을 수행하는 데 사용할 형태소. 형태소를 생성하기 위하여 getStucturingElement()함수를 사용함.
img = cv2.imread("../Downloads/lena.png", 0)
ret, threshold_img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
cv2.imshow("img", threshold_img)
cv2.waitKey(0)

element = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3), (-1,-1))

erosion_img = cv2.erode(threshold_img, element)
cv2.imshow("erosion", erosion_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

팽창 연산

팽창은 침식의 역연산이다. 팽창은 영상 내의 밝은 영역을 성장시킨다.

  • dilate() 매개변수
    • src : 소스 이미지
    • kernel : 형태소
dilate_img = cv2.dilate(threshold_img, element)

cv2.imshow("dilate", dilate_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

열림 연산과 닫힘 연산

제거 연산 후에 팽창 연산을 수행하면 잡음은 효과적으로 제거되면서 물체의 크기는 변하지 않을 것이다. 열림 연산과 닫힘연산은 2차적인 연산으로 침식과 팽창 연산을 이용하여 구현된다.

  • morphologyEx() 매개변수
    • src : 소스 영상
    • operation : 형태학적 연산의 종류
      • 열림(opening) : MORPH_OPEN : 2
      • 닫힘(closing) : MORPH_CLOSE : 3
      • 그라디언트(gradient) : MORPH_GRADIENT : 4
      • 톱햇(top hat) : MORPH_TOPHAT : 5
      • 블랙햇(black hat) : MORPH_BLACKHAT : 6
    • element : 사용되는 형태소

열림 연산(Opening)

열림 연산은 침식 연산 다음에 팽창 연산이 이어지는 것이다. 그 효과는 한 화소 크기의 파편 같은 잡음을 없애는 것이다. 결과적으로 물체의 외곽선이 부드러워진다. 침식 연산과 달리 열림 연산은 원래의 모양과 크기를 유지하는 특성을 지닌다.

kernel = np.array([[1,1,1],[1,1,1],[1,1,1]], dtype = np.uint8)
opening_img = cv2.morphologyEx(threshold_img, cv2.MORPH_OPEN, kernel)

cv2.imshow("opening", opening_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

닫힘 연산(closing)

닫힘 연산은 한 화소 크기의 틈새를 메우는 데 이용된다. 닫힘 연산도 여러 번 적용 될 수 있으며, 이럴 경우 비교적 큰 틈새도 메워질 수 있다.

closing_img = cv2.morphologyEx(threshold_img, cv2.MORPH_CLOSE, element)
cv2.imshow("closing", closing_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

형태학적 그라디언트(morphological gradient)

형태학적 그라디언트는 팽창과 침식의 차이를 계산하여 객체의 윤곽선을 찾는 방법이다.

gradient_img = cv2.morphologyEx(threshold_img, cv2.MORPH_GRADIENT, element)
cv2.imshow("gradient", gradient_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

골격화(skeletonization)

골격화는 골격선을 구하는 연산으로 중심축(medial axis) 또는 세션화(thinning)라고도 불리며 어떤 물체의 중심을 지나는 직선이나 곡선을 말한다.

skel = np.zeros((threshold_img.shape[0],threshold_img.shape[1]), dtype=np.uint8)

while((cv2.countNonZero(threshold_img)!=0)):
    eroded = cv2.erode(threshold_img, element)
    temp = cv2.dilate(eroded, element)
    temp = cv2.subtract(threshold_img, temp)
    skel = cv2.bitwise_or(skel, temp)
    threshold_img = eroded.copy()
    
cv2.imshow("result", skel)
cv2.waitKey(0)
cv2.destroyAllWindows()

Hit-or-Miss 변환

Hit-or-Miss 변환은 영상에서 특정한 패턴을 찾는 데 사용할 수 있는 이진 형태학적 연산이다. 이변환은 고급 형태학적 연산인 세선화(thinning)나 가지치기(pruning) 연산의 기초가 된다.
앞서 보았듯이 2가지 기본적인 형태학적인 연산은 침식과 팽창이다. 이 두 연산을 결합하면 열림, 닫힘과 같은 고급 연산을 만들 수 있었다. Hit-or-Miss 변환은 이진 영상의 특정한 패턴을 찾는 데 유용하다. 특히, 이웃한 화소가 첫 번째 형태소 B1의 모양과 일치하는지 확인하면서 동시에 두 번째 형태서 B2의 모양과 일치하지 않는 곳을 찾는다.

단계

  1. 입력 영상 A를 형태소 B1으로 침식한다.
  2. 영상 A의 역영상 A여집합을 형태소 B2로 침식한다.
  3. 1단계와 2단계의 결과를 AND 연산한다.

형태소

우리는 중심 화소가 배경에 해당되고 동서남북 방향의 화소는 전경에 해당되는 패턴을 찾고 있다. 나머지 화소들은 don't care이다 = -1.

  • Hit 형태소
    • [[0,1,0],
      [1,0,1],
      [0,1,0]]
  • Miss 형태소
    • [[0,0,0],
      [0,1,0],
      [0,0,0]]
  • 둘을 결합한 형태소
    • [[0,1,0],
      [1,-1,1],
      [0,1,0]]
kernel = np.array([[0,1,0],[1,-1,1],[0,1,0]], dtype = np.uint8)
hitmiss_img = cv2.morphologyEx(threshold_img, cv2.MORPH_HITMISS, kernel)

cv2.imshow("hitmiss", hitmiss_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

코너를 찾는 형태소

corners = np.array(
    [[[-1,1,-1],
     [0,1,1],
     [0,0,-1]],
    [[-1,1,-1],
     [1,1,0],
     [-1,0,0]],
    [[-1,0,0],
     [1,1,0],
     [-1,1,-1]],
    [[0,0,-1],
     [0,1,1],
     [-1,1,-1]]]
)

corner_img = np.zeros((threshold_img.shape[0],threshold_img.shape[1]), dtype=np.uint8)

for corner in corners:
    temp = cv2.morphologyEx(threshold_img, cv2.MORPH_HITMISS, corner)
    corner_img = cv2.bitwise_or(corner_img, temp)

cv2.imshow("corner_img", corner_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

0개의 댓글