9장. Morphology: 침식, 팽창, 열기, 닫기
팽창
흰 종이에 물감 한 방울을 떨어뜨리면 둘레로 조금씩 번집니다. 번진 물감이 옆의 다른 방울과 닿으면 두 방울이 하나로 이어지고, 방울 사이의 작은 틈은 물감으로 메워집니다. 팽창은 흰 덩어리를 한 겹 부풀립니다. 덩어리가 커지고, 가까운 덩어리끼리 붙고, 작은 검은 구멍은 흰색으로 메워집니다.
그림의 세 단계는 침식과 둘째 칸만 다릅니다. 도장의 1 자리에 흰 칸이 하나라도 있으면 흰색을 씁니다. 여기서 물감 방울이 흰 덩어리이고, 번지는 너비가 도장의 크기입니다.
손으로 계산해 보기
침식과 같은 8×8 그림을 같은 3×3 도장으로 팽창합니다.
왼쪽의 다홍 테두리 두 개는 원래 0 이던 칸에 댄 도장입니다. (2, 6) 에 댄 도장 안에는 튀어나온 칸 하나가 들어 있어 1 이 되고, (5, 5) 에 댄 도장 안에는 네모 귀퉁이와 떨어진 점이 함께 들어 있어 1 이 됩니다. 오른쪽 결과에서는 네모가 사방으로 한 겹 커졌고, 떨어져 있던 점도 3×3 으로 부풀어 네모와 이어졌습니다.
| 도장을 댄 칸(세로, 가로) | 원래 값 | 도장 안의 1 | 하나라도 1 인가 | 결과 |
|---|---|---|---|---|
| (2, 6) | 0 | 튀어나온 칸 1개 | 그렇다 | 1 |
| (5, 5) | 0 | 네모 귀퉁이, 떨어진 점 | 그렇다 | 1 |
| (7, 1) | 0 | 없음 | 아니다 | 0 |
흰 칸은 18개에서 47개로 늘었고, 흰 덩어리는 두 개(네모, 점)에서 하나로 줄었습니다.
짧은 예시
import cv2
import numpy as np
# 침식 절과 같은 8×8 그림과 3×3 도장
img = np.zeros((8, 8), np.uint8)
img[1:5, 1:5] = 255
img[3, 5] = 255
img[6, 6] = 255
se = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# dilate = 넓히다, 부풀리다
dilated = cv2.dilate(img, se)
print("흰 칸:", int((img == 255).sum()), "→", int((dilated == 255).sum()))
# connectedComponents: (흰 덩어리 수 + 바탕 1, 칸마다의 덩어리 번호)를 돌려준다. 첫 값에서 1 을 뺀다
before = cv2.connectedComponents(img)[0] - 1
after = cv2.connectedComponents(dilated)[0] - 1
print("흰 덩어리:", before, "→", after)
# 결과: 흰 칸 18 → 47, 흰 덩어리 2 → 1cv2.connectedComponents 는 맞닿은 흰 칸끼리 한 무리로 묶어 번호를 매깁니다. 바탕(검은 칸)에도 0 번을 주므로 첫 값에서 1 을 빼야 흰 덩어리 수가 됩니다. 이 함수는 12장에서 자세히 배우고, 이 장에서는 개수만 셉니다.
주의할 점
- 팽창은 흰 덩어리를 굵게 만듭니다. 구멍을 메우고 싶어도 물체가 한 겹 커지므로, 실제로는 침식과 묶어(닫기) 씁니다.
- 침식과 팽창은 서로를 되돌리지 못합니다. 침식으로 사라진 점은 팽창해도 돌아오지 않고, 팽창으로 메운 구멍은 침식해도 다시 열리지 않습니다. 열기와 닫기는 이 성질을 이용합니다.