< Contours : Getting Started >
- 윤곽(Contours)가 무엇인지 이해하자
- 윤곽을 찾는 것, 윤곽을 그리는 것을 배우자
- cv2.findContours(), cv2.drawContours() 함수에 대해 볼 것이다.
What are contours?
윤곽은 같은 색이나 강도를 가지는 연속된 점들을 연결 시켜놓은 커브(경계를 따라)라고 간단히 설명될 수 있다(ex. 등고선). 윤곽은 형태 분석과 객채 탐지, 인식에 유용한 도구이다.
- 더 나은 정확도를 위해, 이진 이미지를 사용한다. 그러니 윤관을 찾기 전에, threshold나 canny edge detection을 적용한다.
- findContours 함수는 원본 이미지를 수정한다. 윤곽을 찾은 후에도 원본 이미지를 원하는 경우 미리 다른 변수에 저장해야한다.
- OpenCV에서, 윤곽을 찾는 것은 마치 검은 배경에서 흰색 객체를 찾는 것과 같다. 그러니 기억해야한다. 찾아야 할 객체는 흰색이어야 하고 배경은 검정색이어야 한다.
이진 이미지에서 윤곽을 찾는 법을 봐보자!
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./images/irene.png')
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray,127,255,0)
contours, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
len(contours)
-> 598
총 598개의 점을 찾아냈다.
cv2.findContours()에는 세 가지 인자가 있는데, 첫 번째로 원본 이미지, 두 번째는 contour 추출 모드, 세 번째는 contour 근사 방법이다. 그리고 결과는 이미지, contours, hierarchy이다. contours는 이미지의 모든 contours의 python 리스트 형태이다. 각 개별 contour은 객체 경계점의 (x,y) 좌표들의 Numpy 배열이다.
How to draw the contours?
Contours를 그리기 위해, cv2.drawContours 함수가 사용된다. 또한 이는 경계점이 있을 때 아무 모양이나 그릴 때도 사용된다. 첫 번째 인자는 원본 이미지, 두 번째 인자는 파이썬 리스트 타입으로 보내질 contours이고, 세 번째 인자는 contours의 index (개별적인 contours를 그릴 때 유용하다. 모든 contours를 그리려면 -1 입력) 그리고 나머지 인자는 색, 두께등을 말한다.
Contour를 이미지로 그리기 위해 아래의 코드를 따라하자:
con_img = cv2.drawContours(img,contours,-1,(0,255,0),3)
plt.imshow(con_img[:,:,::-1])
개별적인 contour를 그리려면, 4번째를 예시로 :
con_img4 = cv2.drawContours(img,contours,3,(0,255,0),3)
plt.imshow(con_img4[:,:,::-1])
plt.axis('off')
plt.show()
결과는 비슷하다.
하지만 대부분의 경우 아래의 방법이 제일 유용하다.
cnt = contours[4]
img = cv2.drawContours(img,[cnt],0,(0,255,0),3)
plt.imshow(img[:,:,::-1])
plt.axis('off')
plt.show()
- 결과는 똑같지만 뒤로 갈 수록 마지막의 방법이 유용하다는 것을 알 수 있다!
Contour Approximation Method
이는 cv2.findContours 함수의 세 번째 인자이다. 이는 어떤 역할을 할까?
위에, contours는 같은 강도를 가진 모양의 경계를 말한다고 했다. 이는 모양의 경계의 (x,y) 좌표를 가지고 있다. 하지만 이것이 모든 좌표를 저장하고 있을까? 이것이 바로 contour approximation method에 의해 정해진다.
만약에 cv2.CHAIN_APPROX_NONE를 사용하면, 모든 경계점은 저장된다. 하지만 실질적으로 모든 점이 필요한가? 예를 들어, 직선의 contour를 찾았다고 해보자. 선을 표현하기 위해 모든 점이 필요할까? 아니다. 그냥 선의 양 끝 두 점만을 필요로한다. 이것이 바로 cv2.CHAIN_APPROX_SIMPLE이 하는 것이다. 모든 중복되는 점을 제거하고 contour를 압축하여 메모리를 절약한다.
아래의 이미지는 직사각형으로 위 기술을 증명한다. 그냥 쭉 둘러서 contour 좌표를 나타낸 것이 cv2.CHAIN_APPROX_NONE(좌)로 하는 것이고 두 번째 이미지가 cv2.CHAIN_APPROX_SIMPLE(우)로 최소한의 점으로 표현하여 메모리를 절약하는 방법이다.