import cv2
import numpy as np
import matplotlib.pyplot as plt
import random

Contour Hierarchy

이전에 findContours로 Contours Points와 Hierarhcy를 반환받는다고 하였다. Image에는 여러가지 Contours가 존재하고, 그 사이에는 상관관계가 존재하며, 그 관계를 Contours Hierarchy라고 한다. 이들의 상관관계를 통해서 Parent, Child 혹은 동등한 상관관계에서의 이웃을 파악할 수 있다.

Contours를 찾다보면 해당 Contours가 독립적으로 존재하는 Contours일 경우도 있고 내부에 Contour가 존재하는 경우도 있다. 각각 동일한 수준에서 Contour가 존재하는 경우 같은 계층에 존재한다고 하며 Contour 내부에 Contour가 존재하는 경우 바깥의 Contour를 부모계층, 내부의 Contour를 자식계층이라고 한다.

이를 Contours의 index와 -1로 표현한다. 반환된 hierarchy의 index가 의미하는 바는 다음과 같다.

  • 0 : 동일 계층에서 다음 Contour의 index, 없으면 -1
  • 1 : 동일 계층에서 이전 Contour의 index, 없으면 -1
  • 2 : 자식 계층 Contour의 index, 가장 하위 계층이면 -1
  • 3 : 부모 계층 Contour의 index, 가장 상위 계층이면 -1

findContours 함수로 Contours Points를 찾을 때, hierarchy에 영향을 주는 인자는 mode라고 이야기한 바 있다. Hierarchy를 구성하는 다양한 mode가 있으며 가각이 무슨 역할을 하는지는 RetrievalModes를 통해 참고할 수 있다.

  1. RETR_LIST
  2. RETR_TREE
  3. RETR_EXTERNAL
  4. RETR_CCOMP
img = cv2.imread('hierarchy.jpg')
img2 = img.copy()
img3 = img.copy()
img4 = img.copy()

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
res, thr = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)

cnt_list, hierarchy_list = cv2.findContours(thr, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt_tree, hierarchy_tree = cv2.findContours(thr, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt_external, hierarchy_external = cv2.findContours(thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt_ccomp, hierarchy_ccomp = cv2.findContours(thr, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(img, cnt_list, -1, (0, 0, 255), 3)
cv2.drawContours(img2, cnt_tree, -1, (0, 255, 255), 3)
cv2.drawContours(img3, cnt_external, -1, (255, 255, 0), 3)
for i in range(len(cnt_ccomp)):
    ccomp = cnt_ccomp[i]
    if hierarchy_ccomp[0][i][2] == -1 and hierarchy_ccomp[0][i][3] == -1:
        cv2.drawContours(img4, [ccomp], -1, (255, 0, 255), 3)        
    elif hierarchy_ccomp[0][i][2] == -1:
        cv2.drawContours(img4, [ccomp], -1, (255, 0, 0), 3)
    elif hierarchy_ccomp[0][i][3] == -1:
        cv2.drawContours(img4, [ccomp], -1, (0, 0, 255), 3)

titles = ['LIST', 'TREE', 'EXTERNAL', 'CCOMP']
images = [img, img2, img3, img4]

plt.figure(figsize=(8, 8))
for i in range(len(images)):
    plt.subplot(2, 2, i+1)
    plt.title(titles[i])
    plt.imshow(images[i])
    plt.axis('off')

plt.tight_layout()
plt.show()

 

 

1. RETR_LIST

해당 mode는 모든 Contours를 찾지만 hierarchy의 상관관계를 고려하지 않는 특성이 있다. 즉, 부모/자식 간의 관계를 고려하지 않은 채 Contours의 선/후 관계만을 표현한다.

print(hierarchy_list[0])

 

★ 2. RETR_TREE

해당 mode는 모든 Contour hierarchy의 상관관계를 나타내는 특성이 있다. 상하관계가 확실하며 동일 계층에 포함되어 있는 contour들 끼리의 순서도 정해진다.

print(hierarchy_tree[0])

index 2의 contour는 1번 hierarchy에 속해 있으며 다음 contour와 이전 contour를 나타내는 index가 모두 -1이므로 1번 hierarchy에 속해 있는 contour는 2번 외에는 없는 것을 알 수 있다. 자식 contour로는 3번 index를 가지므로 3번 contour를 확인했을 때 부모 hierarchy를 나타내는 index가 2를 가리키는 것을 알 수 있다.

 

3. RETR_EXTERNAL

해당 mode는 가장 바깥쪽, 즉 최상위 부모계층에 있는 Contours 만을 반환한다. RETR_TREE mode로 반환된 hierarchy에서 3번째 index가 -1인 Contours 만을 반환한다.

print(hierarchy_external[0])

 

4. RETR_CCOMP

해당 mode는 부모/자식 관계를 2단계로 표현한다. 독립적으로 떨어져 있는 Contours는 부모/자식 관계가 따로 존재하지 않으므로 해당 Contours의 hierarchy의 2번째, 3번째 index는 -1이지만, 안에 구멍이 뚫려있는 Contours의 경우는 조금 다르다.

안에 구멍이 뚫려있는 직사각형의 바깥쪽 Contours Line은 내부의 Contours Line을 포함하고 있으므로 부모, 내부의 Contours는 자식 관계가 명확하게 된다. 하지만 자식 Contours Line 내부에 또 다른 구멍이 뚫려있으면 해당 구멍을 나타내는 Contours Line은 다시 부모의 hierarchy 값을 가지게 된다. 즉 가장 바깥쪽 Line과 구멍 속 구멍 Line은 동일한 hierarchy 값을 가진다.

아래 그림을 통해서 보면 5번과 6번 contour는 박스에 구멍이 뚫린 상자의 바깥쪽 line과 안쪽 line을 가리킨다. 이 때 계층 구조는 바깥쪽 line - 안쪽 line으로 명확하게 된다. 다만, 그 안쪽에 있는 3번과 4번 contour는 6번보다 안쪽에 있음에도 불구하고 contour 계층관계를 다시 정립한다. 마찬가지로 바깥쪽 line - 안쪽 line의 계층 관계를 가지므로 5번과 3번 contour는 같은 계층에 속하게 된다.

print(hierarchy_ccomp[0])

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기