logo

모폴로지 연산

구조화 요소를 이용한 침식·팽창·열림·닫힘

실습 준비

import cv2 as cv
from yeom import to_pil
image = cv.imread("shapes.webp")
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

_, binary = cv.threshold(
    gray,
    60,   # 임계값
    255,  # 임계값보다 밝은 픽셀에 넣을 값
    cv.THRESH_BINARY,
)
to_pil(binary)

임계값 60보다 밝은 픽셀은 흰색 처리. 흰색 영역에는 도형 17개와 배경의 작은 밝은 점이 남는다.

도형 원본과 임계값 60으로 만든 잡음이 포함된 이진 이미지

구조화 요소

모폴로지 연산(morphological operation)은 구조화 요소(structuring element)를 주변 픽셀에 적용하여 이진 객체의 형태를 바꾸는 처리. OpenCV에서는 0이 아닌 픽셀을 흰색 전경으로 취급.

kernel = cv.getStructuringElement(
    cv.MORPH_ELLIPSE,  # 모양. MORPH_RECT, MORPH_CROSS도 사용 가능
    (3, 3),            # 크기
)

kernel
실행 결과
array([[0, 1, 0],
       [1, 1, 1],
       [0, 1, 0]], dtype=uint8)

커널에서 값이 1인 중심과 상하좌우 5개 픽셀이 연산에 참여. 커널이 클수록 한 번의 연산 범위도 확대.

침식과 팽창

침식(erosion)은 커널 영역의 최솟값을 사용. 위의 십자형 커널에서는 5개 픽셀 중 하나라도 검은색(0)이면 결과도 검은색(0). 따라서 흰색 전경 축소.

  • 객체의 바깥 경계가 안쪽으로 이동
  • 작은 흰색 점과 가는 돌출부 제거
  • 가는 연결 부분의 단절 가능

팽창(dilation)은 커널 영역의 최댓값을 사용. 위의 십자형 커널에서는 5개 픽셀 중 하나라도 흰색(1)이면 결과도 흰색(1). 따라서 흰색 전경 확대.

  • 객체의 바깥 경계가 바깥쪽으로 이동
  • 작은 검은색 구멍과 끊어진 간격 축소
  • 가까운 객체의 결합 가능
eroded = cv.erode(binary, kernel)
dilated = cv.dilate(binary, kernel)

to_pil(cv.hconcat([eroded, dilated]))

이진 이미지와 3×3 침식·팽창 결과 비교

침식으로 도형과 흰색 잡음이 함께 축소. 팽창으로 도형이 굵어지고 작은 흰색 잡음도 확대.

전경과 배경을 먼저 확인

침식과 팽창의 설명은 흰색을 전경으로 보는 경우에 해당. 검은색 물체는 먼저 이진 이미지를 반전하여 관심 물체를 흰색으로 설정.

열림과 닫힘

열림(opening)은 침식 후 팽창. 큰 객체의 크기를 대체로 복원하면서 커널보다 작은 흰색 잡음을 제거.

닫힘(closing)은 팽창 후 침식. 객체의 크기를 대체로 복원하면서 작은 검은색 구멍과 끊긴 부분을 메우는 연산.

opened = cv.morphologyEx(
    binary,
    cv.MORPH_OPEN,
    kernel,
)
cleaned = cv.morphologyEx(
    opened,
    cv.MORPH_CLOSE,
    kernel,
)

to_pil(cv.hconcat([opened, cleaned]))

각 단계의 검출 객체 수 비교. cv.findContours()는 이진 이미지에서 객체의 경계를 찾는 함수.

raw_contours, _ = cv.findContours(
    binary,
    cv.RETR_EXTERNAL,
    cv.CHAIN_APPROX_SIMPLE,
)
opened_contours, _ = cv.findContours(
    opened,
    cv.RETR_EXTERNAL,
    cv.CHAIN_APPROX_SIMPLE,
)
cleaned_contours, _ = cv.findContours(
    cleaned,
    cv.RETR_EXTERNAL,
    cv.CHAIN_APPROX_SIMPLE,
)
len(raw_contours) # before
실행 결과
176
len(opened_contours) # after opening
실행 결과
17
len(cleaned_contours) # after closing
실행 결과
17

잡음이 포함된 이진 이미지와 열림·닫힘 결과의 윤곽선 수 비교

이진화 직후에는 배경의 작은 밝은 점까지 객체로 잡혀 176개 검출. 열림 후 실제 도형 수인 17개로 감소. 닫힘은 도형 내부의 작은 틈을 정리하면서 17개 객체를 유지.

Previous
Sobel과 Canny 경계 검출