Study/ML | DL

[Python] OpenCV를 이용한 이미지/영상 처리 - 이미지 윤곽선(Image Contours)

truthyun 2023. 6. 27. 22:00
728x90
반응형

참고 글

 

[Python] OpenCV를 이용한 이미지/영상 처리 - Adaptive Threshold, otsu algorithm Threshold

이전 글 [Python] OpenCV를 이용한 이미지/영상 처리 - 이진화(Binarization), TrackBar 참고 글 [Python] OpenCV를 이용한 이미지/영상 처리 - 동영상 및 카메라 출력 이전글 1. OpenCV를 이용한 이미지/영상 처리 -

seoyuun22.tistory.com

 


 

18. 윤곽선 (Image Contours)

윤곽선(Contours)은 동일한 색 또는 동일한 강도를 가지고 있는 영역의 경계선을 연결한 선으로 대상의 외형을 파악하는데 유용하게 사용된다.

 

윤곽선을 그리는 과정

윤곽선을 그릴 때는 원본 이미지의 훼손을 방지하기 위해 copy 이미지를 사용한다. 또한 윤곽선을 좀 더 정확하게 찾아내기 위해 Binary Image를 사용한다. 이때 threshold 또는 canny edge를 선처리로 수행한다. 그다음 윤곽선을 그리기 위해서 윤곽선을 찾는 cv2.findContours 함수와 윤곽선을 그리는 cv2.drawContours 함수를 사용한다.

Image copy - Binarizaion - Find contours - Drawcontours

 

Find Contours

윤곽선을 찾기 위해 cv2.findContours()함수를 사용한다. 함수를 사용해 윤곽선 정보(coutours)와 계층구조(hierarchy)를 반환한다.

cv2.findContours(image, mode, method)

mode의 경우 countours를 찾는 방법을 지정하는 다음과 같은 옵션이 있다.

  • cv2.RETR_EXTERNAL : contours line중 가장 바깥쪽 라인만 찾음
  • cv2.RETR_LIST : 모든 contours line을 찾지만 계층구조 관계를 구성하지 않음
  • cv2.RETR_CCOMP : 모든 contours line을 찾으며 2-level의 계층구조 관계를 구성함
  • cv2.RETR_TREE : 모든 contours line을 찾으며 모든 계층구조 관계를 구성함

 

method는 contours를 찾을 때 사용하는 근사화 방법으로, 다음과 같은 옵션이 있다.

  • cv2.CHAIN_APPROX_NONE : 모든 contours point를 저장
  • cv2.CHAIN_APPROX_SIMPLE : contours line을 그릴 수 있는 point만 저장 (ex. 사각형은 4개의 point)
  • cv2.CHAIN_APPROX_TC89_L1 : contours point를 찾는 알고리즘
  • cv2.CHAIN_APPROX_TC89_KCOS : contours point를 찾는 알고리즘

 

Draw Contours

이미지에 윤곽선을 그리기 위해선 cv2.drawContours()함수를 사용한다. 

cv2.drawContours(image, contours, contourIdx, color, thickness)

contourIdx는 contours line type이 몇번째 윤곽선에서 그릴 것인지 정한다. -1일 경우 전체에 그리는 것을 의미한다.

위 과정을 바탕으로 이미지의 윤곽선을 그려보면 다음과 같다.

 

1. RETR_LIST 방법으로 contours를 찾는 경우

# EX. RETR_LIST

import cv2

img = cv2.imread("card.png")

# 원본을 copy한 사본이미지 사용
target_img = img.copy()

# 윤곽선 검출에서 정확도를 높히기 위해 binary Image를 사용한다.
# binarizaion 방법으로 otsu 알고리즘을 사용한다.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, otsu = cv2.threshold(gray, -1, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# cv2.findContours : 윤곽선 정보, 윤곽선간의 계층 구조를 반환
contours, hierarchy = cv2.findContours(otsu, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

#cv2.drawContours : 윤곽선 그리기 
color = (0, 200, 0)
cv2.drawContours(target_img, contours, -1, color, 2)
# 원본에 그릴 경우 원본이 훼손되기 때문에 copy된 사본 이미지에 그린다.

cv2.imshow("origin", img)
cv2.imshow("gray", gray)
cv2.imshow("otsu", otsu)
cv2.imshow("contour", target_img)

cv2.waitKey(0)
cv2.destroyAllWindows()

countours을 찾는 방법 중cv2.RETR_LIST 방법은 위의 마지막 결과처럼 모든 윤곽선을 찾는다. 

 

2. cv2.RETR_EXTERNAL 방법으로 contours를 찾는 경우

# EX. RETR_EXTERNAL

import cv2

img = cv2.imread("card.png")

target_img = img.copy()

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, otsu = cv2.threshold(gray, -1, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

contours, hierarchy = cv2.findContours(otsu, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

color = (0, 200, 0)
cv2.drawContours(target_img, contours, -1, color, 2)

cv2.imshow("origin", img)
cv2.imshow("gray", gray)
cv2.imshow("otsu", otsu)
cv2.imshow("contour", target_img)

cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.RETR_EXTERNAL 방법으로 contours를 찾는 경우 위의 결과처럼 가장 외곽의 윤곽선만을 찾는다.

 

3. cv2. RETR_TREE 방법으로 contours를 찾는 경우

cv2.RETR_LIST 방법으로 contours를 찾게 되면 cv2.RETR_LIST와 같은 결과를 출력한다. 다만 cv2. RETR_TREE의 경우 계층 정보를 트리구조로 생성한다는 차이점이 있다. 이 트리구조를 hierarchy에서 확인할 수 있다.

print(hierarchy)
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[10 1 3 -1]
...
[-1 34 -1 -1]]]

이 길이를 출력해보면 36개가 발견된다.

print(f'총 발견 갯수 : {len(contours)}')
총 발견 갯수 : 36

계층구조는 윤곽선을 포함관계의 여부를 나타낸다. 즉 외곽 윤곽선, 내곽 윤곽선, 같은 계층구조를 구별할 수 있다. 여기서 첫번째 계층구조는 각 4개의 값을 갖는데, 이는 [다음 윤곽선, 이전 윤곽선, 내곽윤곽선, 외곽윤곽선]에 대한 인덱스 정보를 포함하고 있다. 

728x90
반응형