모폴로지 연산
구조화 요소를 이용한 침식·팽창·열림·닫힘
실습 준비
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개와 배경의 작은 밝은 점이 남는다.

구조화 요소
모폴로지 연산(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]))

침식으로 도형과 흰색 잡음이 함께 축소. 팽창으로 도형이 굵어지고 작은 흰색 잡음도 확대.
전경과 배경을 먼저 확인
침식과 팽창의 설명은 흰색을 전경으로 보는 경우에 해당. 검은색 물체는 먼저 이진 이미지를 반전하여 관심 물체를 흰색으로 설정.
열림과 닫힘
열림(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개 객체를 유지.