opencv 윤곽선(Contours)값의 의미(내용) 최대값 최소값 구하기

윤곽선(Contours)이란

영상이나 이미지의 윤곽선(컨투어)를 검출하기 위해서 사용합니다. 윤곽선은 외곽 뿐만 아니라 내곽도 검출 가능합니다.

컨투어(contour)란 동일한 색 또는 동일한 픽셀값(강도,intensity)을 가지고 있는 영역의 경계선 정보입니다. 물체의 윤곽선, 외형을 파악하는데 사용됩니다.

윤곽선(Contours)

윤곽선 검출

윤곽선의 검출 과정은 cv2.findContours을 사용해서 취득합니다. 해당 함수로 이미지의 컨투어 정보, 컨투어의 상하구조(hierachy) 정보를 출력합니다. 흑백이미지 또는 이진화된 이미지만 적용할 수 있습니다.

images, contours, hierachy = cv2.findContours(image, mode, method)

  • image: 흑백이미지 또는 이진화된 이미지
  • mode : 컨투어를 찾는 방법
    • cv2.RETR_EXTERNAL: 컨투어 라인 중 가장 바깥쪽의 라인만 찾음
    • cv2.RETR_LIST: 모든 컨투어 라인을 찾지만, 상하구조(hierachy)관계를 구성하지 않음
    • cv2.RETR_CCOMP: 모든 컨투어 라인을 찾고, 상하구조는 2 단계로 구성함
    • cv2.RETR_TREE: 모든 컨투어 라인을 찾고, 모든 상하구조를 구성함
  • method : 컨투어를 찾을 때 사용하는 근사화 방법
    • cv2.CHAIN_APPROX_NONE: 모든 컨투어 포인트를 반환
    • cv2.CHAIN_APPROX_SIMPLE: 컨투어 라인을 그릴 수 있는 포인트만 반환
    • cv2.CHAIN_APPROX_TC89_L1: Teh_Chin 연결 근사 알고리즘 L1 버전을 적용하여 컨투어 포인트를 줄임
    • cv2.CHAIN_APPROX_TC89_KCOS: Teh_Chin 연결 근사 알고리즘 KCOS 버전을 적용하여 컨투어 포인트를 줄임

실행

예제 이미지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import cv2

src = cv2.imread("contours.jpg", cv2.IMREAD_COLOR)

gray = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary = cv2.bitwise_not(binary)

contours, hierachy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

print("contours = ", contours)

cv2.drawContours(src, [contours[0]], 0, (0, 0, 255), 2)
cv2.imshow("src", src)
cv2.waitKey(0)

cv2.destroyAllWindows()

위 예제는 최외곽 윤곽선을 찾고 윤곽선을 빨간색으로 그려주는 소스입니다.

결과는 아래와 같습니다.

최외곽 윤곽선

drawContours 함수를 사용하면 컨투어 정보에서 비트맵 이미지를 만들 수 있습니다.

cv2.drawContours(이미지, [윤곽선], 윤곽선 인덱스, (B, G, R), 두께, 선형 타입)을 의미합니다.

contours를 print하면 아래와 같은 결과를 보여줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
contours =  [array([[[116,  11]],

[[115, 12]],

[[114, 12]],

...,

[[119, 11]],

[[118, 11]],

[[117, 11]]], dtype=int32)]

contours는 3차원의 리스트로 이루어져 있습니다.

첫번째 차원은 윤곽선 인덱스을 의미합니다.

예제의 그림에서는 최외곽선 윤곽선 하나만을 추출하였기 때문에 첫번째 차원은 한개로 이루어져 있습니다.

contours[0]을 print하면 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
contours[0] =  [[[116  11]]

[[115 12]]

[[114 12]]

...

[[119 11]]

[[118 11]]

[[117 11]]]

결과적으로 contours[i]에서 i값을 변경하면서 다른 윤곽선을 선택할 수 있습니다.

두번째 차원은 외곽선의 x,y좌표를 뜻합니다.

contours[0][0], contours[0][1]을 print하면 다음과 같습니다.

1
2
contours[0][0] =  [[116  11]]
contours[0][1] = [[115 12]]

각각의 의미는 컨투어(윤곽선)의 좌표를 뜻합니다. 해당 좌표들을 이어서 선을 만들어 지는 것이 윤곽선입니다.

이제 첫번째 좌표의 x,y값을 취득하려면

contours[0][0][0][0], contours[0][0][0][1]을 해주면 됩니다.

두번째 좌표의 x,y값은 두번째 차원의 값을 변경해 주면 됩니다.

contours[0][1][0][0], contours[0][1][0][1]

해당 값을 print하면 다음과 같습니다.

1
2
3
4
contours[0][0][0][0] =  116 # 첫번째 윤곽선 좌표의 x값
contours[0][0][0][1] = 11 # 첫번째 윤곽선 좌표의 y값
contours[0][1][0][0] = 115 # 두번째 윤곽선 좌표의 x값
contours[0][1][0][1] = 12 # 두번째 윤곽선 좌표의 y값

윤곽선의 최대,최소값 구하기

이제 윤곽선의 x,y좌표를 구할 수 있으므로 x,y좌표의 최대 최소값 또한 구할 수 있습니다.

최대 최소값의 좌표를 구하기 위해서는 numpy의 argmin, argmax를 쓰면 됩니다.

해당 함수는 최소값, 최대값의 색인 위치를 리턴합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cv2
import numpy as np

src = cv2.imread("contours.jpg", cv2.IMREAD_COLOR)

gray = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary = cv2.bitwise_not(binary)

contours, hierachy = cv2.findContours(binary, cv2.RETR_TREE , cv2.CHAIN_APPROX_NONE)

contours_min = np.argmin(contours[0], axis = 0)
contours_max = np.argmax(contours[0], axis = 0)

print("contours_min = ", contours_min)
print("contours_max = ", contours_max)

위에서 결과는 다음과 같습니다.

1
2
contours_min =  [[119   0]]
contours_max = [[488 245]]

해당 값의 뜻은 contours_min에서는 x값이 최소가 되는 색인은 119, y값의 최소가 되는 색인은 0입니다.
최대 또한 x값이 최대가 되는 색인은 448, y값이 최대가 되는 색인은 245가 됩니다.

해당 값의 좌표를 구하면 아래와 같습니다.

1
2
3
4
5
6
7
8
9
print("x-Min =", contours[0][contours_min[0][0]][0][0])
print("y-Min =", contours[0][contours_min[0][1]][0][1])
print("x-Max =", contours[0][contours_max[0][0]][0][0])
print("y-Max =", contours[0][contours_max[0][1]][0][1])

x-Min = 30
y-Min = 11
x-Max = 326
y-Max = 189

위 처럼 numpy의 함수를 쓰면 윤곽선에서 좌표의 최대 최소값을 구할 수 있습니다.

공유하기