12장. Contour Detection 과 객체 분석
찾는 방식과 계층 구조
아파트 단지 안내도를 떠올려 봅시다. "단지 안의 동만 세어 주세요"라고 하면 건물의 바깥 모양만 보면 됩니다. "모든 공간을 적어 주세요"라고 하면 건물 안의 안뜰까지 적습니다. "어느 공간이 어느 건물 안에 있는지 적어 주세요"라고 하면 건물과 안뜰, 안뜰 속의 놀이터까지 누가 누구 안인지 나무 모양으로 적어야 합니다. 윤곽선을 찾는 방식(mode)도 이렇게 세 가지를 고릅니다.
초록 상자의 RETR_EXTERNAL 은 가장 바깥의 테두리만 찾습니다. 다홍 상자의 RETR_LIST 는 구멍의 테두리까지 모두 찾지만 누가 누구 안인지는 적지 않습니다. 보라 상자의 RETR_TREE 는 모두 찾고 부모 자식 관계까지 적습니다. 여기서 동의 바깥 모양이 바깥 윤곽선, 안뜰이 구멍의 윤곽선, 놀이터가 구멍 속 물체의 윤곽선입니다.
도넛, 가운데 구멍에 점이 하나 든 액자, 꽉 찬 원이 있는 그림입니다. EXTERNAL 은 세 물체의 바깥 테두리 3개만, LIST 는 도넛 구멍, 액자 구멍, 구멍 속 점까지 6개를 찾습니다. TREE 도 6개지만 층마다 색을 다르게 칠할 수 있습니다. 초록이 가장 바깥, 다홍이 구멍, 보라가 구멍 속의 물체입니다.
hierarchy 한 줄 읽기
hierarchy 는 윤곽선마다 한 줄이고, 한 줄에 네 칸이 있습니다. 칸마다 다른 윤곽선의 번호를 적고, 그런 윤곽선이 없으면 -1 을 적습니다.
| 칸 | 이름 | 뜻 |
|---|---|---|
| [0] | 다음(next) | 같은 층에서 다음 윤곽선의 번호 |
| [1] | 이전(previous) | 같은 층에서 앞 윤곽선의 번호 |
| [2] | 첫 자식(first child) | 바로 안쪽에 있는 첫 윤곽선의 번호 |
| [3] | 부모(parent) | 바로 바깥에 있는 윤곽선의 번호 |
TREE 로 찾은 여섯 윤곽선을 나무로 그렸습니다. 점선 화살표는 같은 층의 "다음", 실선 화살표는 "첫 자식"입니다. 4번(액자 구멍)의 줄은 [-1, -1, 5, 3] 입니다. 같은 층에 다음도 이전도 없고, 첫 자식은 5번(구멍 속 점), 부모는 3번(액자 바깥)이라는 뜻입니다.
세 방식 견주기
| 방식 | 찾는 윤곽선 | 이 그림에서 개수 | hierarchy 의 부모 칸 | 쓰는 곳 |
|---|---|---|---|---|
| RETR_EXTERNAL | 가장 바깥만 | 3 | 모두 -1 | 물체 세기, 면적 재기 |
| RETR_LIST | 모두 | 6 | 모두 -1 | 구멍까지 모든 테두리를 그릴 때 |
| RETR_TREE | 모두 | 6 | 바깥 윤곽선의 번호 | 구멍 세기, 구멍 속 물체 찾기 |
짧은 예시
import cv2
nested = cv2.imread("ch12_nested.png", cv2.IMREAD_GRAYSCALE)
for name in ["RETR_EXTERNAL", "RETR_LIST", "RETR_TREE"]:
# getattr(cv2, 이름): 글자로 적은 상수 이름으로 cv2 의 값을 꺼낸다
contours, hierarchy = cv2.findContours(nested, getattr(cv2, name), cv2.CHAIN_APPROX_SIMPLE)
print(f"{name:13}: {len(contours)}개")
# TREE 의 hierarchy 를 줄마다 찍는다: [다음, 이전, 첫 자식, 부모]
contours, hierarchy = cv2.findContours(nested, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for i, row in enumerate(hierarchy[0]):
print(i, [int(v) for v in row])교안 PC 에서는 EXTERNAL 3개, LIST 6개, TREE 6개가 나오고, TREE 의 줄은 0번부터 [1, -1, -1, -1], [3, 0, 2, -1], [-1, -1, -1, 1], [-1, 1, 4, -1], [-1, -1, 5, 3], [-1, -1, -1, 4] 입니다. 부모 칸(넷째)이 -1 인 0, 1, 3번이 가장 바깥의 물체이고, 1번(도넛)의 첫 자식이 2번(도넛 구멍)입니다.
주의할 점
- hierarchy 는 (1, 윤곽선 수, 4) 모양이라
hierarchy[0]으로 첫 칸을 벗겨 내고 씁니다.hierarchy[0][i]가 i 번 윤곽선의 줄입니다. - 이 밖에 바깥과 구멍 두 층만 적는
RETR_CCOMP(connected components, 연결 요소)도 있습니다. 이 장에서는 세 가지만 씁니다.