[OpenCV] 04-9. Contours (5)
🐍Python/OpenCV

[OpenCV] 04-9. Contours (5)

728x90
반응형

< Contours Hierarchy >

이번 장에서는, Contours의 층에 대해서 알아볼 것이다.(Contours의 부모-자식 관계)

Theory

cv2.findContours() 함수를 이용하여 이미지의 contours를 찾게되면, Contour Retrival Mode 인자를 넘겨야한다. 보통은 cv2.RETR_LIST 나 cv2.RETR_TREE를 사용하고 작동도 잘한다. 하지만 사실상 의미하는 것이 무엇일까?

결과에서 또한, 3가지 arrays를 얻는데, 첫 번째는 이미지, 두 번째는 contours, 나머지 하나는 “계층”이라 불리우는 것이다. 하지만 이 계층을 어디에서도 쓴적이 없다. 그러면 계층은 무엇이고 어디에 사용될까? 이전에 언급했던 인자들과는 무슨 관계가 있는 것일까?

이번 장에서 다루게 될 주제가 바로 이것이다.

What is Hierarchy?

보통 이미지내의 객체를 탐지하기 위해서 cv2.findContours() 함수를 사용한다. 때로 객체가 다른 위치에 있을 수 있다. 하지만 이런 상황에서, 어떤 모양들은 다른 모양들 안에 있다. 둥지의 모양과 유사하다. 이러한 경우에, 바깥의 것을 “부모”라고하고 내부의 것을 “자식”이라고 한다. 이미지 내의 contours는 각각 서로 관계가 있는 것이ㅣ다. 그리고 어떤 contour끼리 연결되었는지도 알 수 있다. (A가 B의 자식 contours다) 이러한 관계를 표현하는 것이 바로 “계층”이 되는 것이다.

아래 예를 보자.


이 이미지에서, 0-5로 표시된 모양들과 2와 2a는 가장 바깥쪽의 contours의 외부와 내부를 의미한다.

여기, contours 0,1,2는 외부 혹은 가장 바깥쪽이다. 그래서 이를 계층-0이라고 하거나 간단하게 그들이 같은 계층 레벨을 가지고 있다고 말할 수 있다.

다음으로는 contour-2a가 온다. 이는 contour-2의 자식이라고 생각할 수 있다(반대로 contour-2a의 부모는 contour-2이다.) 그래서 계층-1이 되는 것이다. 유사하게 contour-3은 contour-2의 자식이므로 다음 계층이 된다. 마지막으로 contours 4,5는 contour-3a의 자식이기에 마지막 계층 레벨이 된다. 번호 매긴 박스로부터, contour-4가 contour-3a의 첫 자식이라고 말 할 수 있다. (contour-5도 마찬가지다.)

같은 계층 레벨, 외부 contour, 자식 contour, 부모 contour, 첫 자식과 같은 용어를 이해하기위해 위를 설명했다. 이제 OpenCV로 넘어가자

### Hierarchy Representation in OpenCV

그래서 각 contour는 누구의 자식인지, 누가 부모인지, 계층이 무엇인지와 같은 정보를 가지고 있다. OpenCV는 이를 4가지 값을 가지는 배열로 나타낸다 : [Next,Previous,First_Child,Parent]


“Next는 같은 계층 레벨에서 다음 contour를 말한다”

예를 들어서, contour-0을 보자. 같은 레벨에서 다음 contour는 무엇일까? contour-1이다. 그래서 간단하게 Next=1을 넣는다. Contour-1과 유사하게, 다음은 contour-2이다. 그래서 Next=2가 되는 것이다.

contour-2에 대해서는 어떨까? 같은 레벨에서 다음의 contour가 없다. 그래서 간단하게 Next=-1이다. contour-4에 대해서는 어떨까? 이는 contour-5와 같은 레벨에 있따. 그래서 다음은 contour-5가 되고 Next=5가 된다.


“Previous는 같은 계층 레벨에서 이전의 contour를 말한다.”

이는 위와 같다. Contour-1의 이전 contour는 contour-0이다. 유사하게 contour-2에게는 contour-1이다. 그리고 contour-0에게는 이전의 것이 없어서, -1을 넣는다.


“First_Child는 first child contour이다”

설명이 필요없다. contour-2는 contour-2a의 자식이다. contour-3a의 경우는 어떨까? 두개의 자식이 있다. 하지만 오직 하나의 자식만 가져갈 수 있다. 그래서 이는 contour-4이고 First_Child=4이다.


“Parent는 부모 contour의 인자이다”

이는 First_Child와 반대이다. contour-4,5의 부모 contour는 contour-3a이다. contour-3a의 부모는 contour-3이다.


** 만약에 자식이나 부모가 없다면 -1의 값을 취한다.

이제 OpenCV의 많은 계층 스타일을 봤다. 이것들이 Contour Retrieval Modes를 보는데 도움이 된다.

Contour Retrieval Mode

1. RETR_LIST

4개의 flag중에 가장 간단하다. 이는 모든 contours를 되찾지만, 부모-자식 관계를 생성하진 않는다. 부모나 자식은 이 규칙아래서 동등하고 그저 contours일 뿐이다. 그들은 같은 계층 레벨에 속해있다는 것이다.

그래서 계층에서 세 번째, 네 번째 항은 항상 -1이다. 하지만 명백하게, 다음과 이전 항은 해당 값을 갖는다.

아래 결과의 각 행은 해당 contour의 계층 세부사항이다. 예를 들어 첫 번째 행은 contour 0에 대응한다. 다음 contour는 contour 1이다. 그래서 Next = 1 이다. 이전 contour는 없으니 Previous = 0이다. 그리고 남은 두 가지는 말했던 것 처럼 -1 이다.

>>> hierarchy
array([[[ 1, -1, -1, -1],
 [ 2,  0, -1, -1],
 [ 3,  1, -1, -1],
 [ 4,  2, -1, -1],
 [ 5,  3, -1, -1],
 [ 6,  4, -1, -1],
 [ 7,  5, -1, -1],
 [-1,  6, -1, -1]]])

계층을 사용하지 않는 경우 이러한 방법이 유용하다.

2. RETR_EXTERNAL

이 flag를 사용한다면, 이는 가장 밖에 있는 flags만 반환한다. 모든 자식 contours는 뒤로 제쳐둔다. 이 규칙하에서는 오직 각 가족에서 맏인 것들만 다룬다. 다른 구성원은 신경쓰지 않는다.

그래서 우리의 이미지에서, 제일 밖에 있는 contours는 몇 개나 있을까? 계층 0 레벨은 3개 뿐이다.

>>> hierarchy
array([[[ 1, -1, -1, -1],
 [ 2,  0, -1, -1],
 [-1,  1, -1, -1]]])

이는 바깥의 contours만을 추출하고자 할 때 유용하다.

3. RETR_CCOMP

이 flag는 모든 contours를 되찾고, 2 레벨 계층으로 배열한다. 객체의 외부 contours는 계층-1로 놓여진다. 그리고 객체 내부 구멍의 contours는 계층-2에 놓인다. 만약 객체가 내부에 있다면, contour은 다시 계층-1로 놓여진다. 그러면서 계층-2에 구멍이 생긴다.

검은 배경에 “큰 흰색 0”을 생각해보자. 0의 바깥의 원은 첫 계층에 속하고, 0의 내부 원은 두 번째 계층에 속한다.

우리는 이를 간단한 이미지로 설명할 수 있다. contour의 순서를 빨간색으로, 그리고 그들이 속해 있는 계층구조에 녹색으로 라벨을 붙였다. 이 순서는 OpenCV가 contours를 탐지하는 순서와 동일하다.


첫 contour를 보면, 즉 contour-0이다. 이는 계층-1이다. 여기에 두 개의 구멍인 contour 1&2가 있고, 그리고 계층-2에 속해있다. 그래서 contour-0에 대해서, 다음 contour는 같은 계층 레벨인 contour-3이다. 그리고 이전의 것은 없다. 그리고 첫 자식은 contour-1, 계층-2이다. 계층-1이기에 부모는 없다. 그래서 계층 배열은 [3,-1,1,-1]이다.

contour-1을 보자. 계층-2이다. 다음은 같은 계층인 contour-2이다. 이전의 것은 없고 자식도 없지만 부모는 contour-0이다. [2,-1,-1,0] 으로 나타난다.

유사하게 contour-2도 계층-2이다. 다음의 contours는 없다. 그렇기에 이전의 것이 contour-1이 된다. 자식도 없고, 부모는 contour-0이다. 그래서 배열이 [-1,1,-1,0]이 된다.

contour-3 : 계층-1에서 다음은 contour-5이다. 이전은 contour-0이고 자식은 contour-4이고 부모는 없다. [5,0,4,-1]

contour-4 : contour-3하의 계층-2이고 자매형제는 없다. 다음도 없고 이전도 없고, 자시곧 없고 부모는 contour-3이다. 그래서 배열이 [-1,-1,-1,3]이 된다.

>>> hierarchy
array([[[ 3, -1,  1, -1],
 [ 2, -1, -1,  0],
 [-1,  1, -1,  0],
 [ 5,  0,  4, -1],
 [-1, -1, -1,  3],
 [ 7,  3,  6, -1],
 [-1, -1, -1,  5],
 [ 8,  5, -1, -1],
 [-1,  7, -1, -1]]])

나머지를 채우면 위와 같이 된다.

4. RETR_TREE

이게 마지막이다. 이는 contours를 반환하고 모든 가족 계층 리스트를 생성한다. 이는 누가 할아버지이고, 아버지이고, 아들이고, 손자인지를 말해준다.

예를 들어, 이미지를 받아서, cv2.RETR_TREE를 사용한다면, 재구성하게 된다. 빨간 글자는 contour 숫자이고 초록 글자는 계층 순서를 말한다.


contour-0 : 이는 계층-0이다. 다음 contour는 같은 계층의 contour-7이다. 이전은 없고 자식은 contour-1이다. 부모는 없다. [7,-1,-1,1]

contour-2 : 계층-1이다. 같은 레벨의 contour가 없다. 이전의 것도 없다. 자식은 contour-2이다. 부모는 contour-0이어서 배열이 [-1,-1,2,0]이다.

>>> hierarchy
array([[[ 7, -1,  1, -1],
 [-1, -1,  2,  0],
 [-1, -1,  3,  1],
 [-1, -1,  4,  2],
 [-1, -1,  5,  3],
 [ 6, -1, -1,  4],
 [-1,  5, -1,  4],
 [ 8,  0, -1, -1],
 [-1,  7, -1, -1]]])

나머지를 채우면 위와 같이 된다.

728x90
반응형