본문으로 바로가기

파이썬 OpenCV 외곽선 검출(Contours)

category OpenCV 2020. 7. 26. 17:03
728x90

외곽선 검출

외곽선을 검출할 때는 findContours 를 사용하고 그릴때는 drawContours 를 사용합니다.

검은색 배경에 흰색 객체를 식별하여 검출합니다.

 

cv2.findContours(src, mode, method, contours=None, hierarchy=None, offset=None) -> contours, hierarchy

  • src: 입력 이미지
  • mode: 외곽선 검출 방식
  • cv2.RETR_EXTERNAL
  • 가장 바깥쪽의 영역만 추출
  • 계층 정보 없음
  • cv2.RETR_LIST
  • 모든 영역을 추출
  • 계층 정보 없음
  • cv2.RETR_TREE
  • 바깥 영역부터 계층적 구조를 추출
  • method: 외곽선 근사 방법
  • contours:
  • 외곽선 좌표(np.ndarray)
  • len(contours) : 외곽선 개수
  • hierarchy
  • 외곽선 계층 정보를 담고 있는 (1, n, 4) shape 의 list(n은 contour 개수)
  • next, previous, child, parent 를 의미
  • offset: 좌표 이동 offset(default=(0, 0)

cv2.drawContours(src, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None) -> src

  • src: 입력 이미지
  • contours: cv2.findContours 에서 찾은 외곽선 좌표
  • contourIdx: 외곽선 인덱스(-1 이면 모든 외곽선을 그림)
  • color: 외곽선 색상
  • thickness: 외곽선 두께(음수이면 내부를 채움)
  • lineType: LINE_$, LINE_8, LINE_AA 중 선택
  • hierarchy: 외곽선 계층 정보
  • maxLevel: 그리기를 수행할 때 최대 외곽선 레벨

 

cv2.RETR_EXTERNAL

계층정보는 없으며, 외부 영역만을 추출합니다.

import os
import cv2
import numpy as np

path = os.path.join('img', 'shape.png')
img = cv2.imread(path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
    img = cv2.drawContours(img, [contour], -1, (0, 0, 255), 2)

cv2.imshow('img', img)
cv2.imshow('gray', gray)

cv2.waitKey()
cv2.destroyAllWindows()

 

cv2.RETR_TREE

외부 영역부터 계층적 구조를 추출합니다.

import os
import cv2

path = os.path.join('img', 'shape2.png')
src = cv2.imread(path)

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
tree = src.copy()

contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
    src = cv2.drawContours(src, [contour], -1, (0, 255, 0), 2)

contours, hierarchy = cv2.findContours(gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for contour in contours:
    tree = cv2.drawContours(tree, [contour], -1, (0, 255, 0), 2)

cv2.imshow('ext', src)
cv2.imshow('tree', tree)

cv2.waitKey()
cv2.destroyAllWindows()

 

cv.RETR_EXTERNAL 과는 달리 cv.RETR_TREE 는 안쪽 영역까지 검출하는 것을 볼 수 있습니다.

 

hierarchy 를 사용한 코드입니다.

# contour 예제
import os
import cv2
import numpy as np

path = os.path.join('img', 'shape.png')
img = cv2.imread(path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

idx = 0
while idx >= 0:
    cv2.drawContours(img, contours, idx, (255, 0, 0), 2, cv2.LINE_8, hierarchy)
    idx = hierarchy[0, idx, 0]

cv2.imshow('img', img)
cv2.imshow('gray', gray)

cv2.waitKey()
cv2.destroyAllWindows()

 

검출된 hierarchy 배열을 보면

하나의 행은 [next, previous, child, parents] 의미를 가집니다.

contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(hierarchy)
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 3  1 -1 -1]
  [ 4  2 -1 -1]
  [ 5  3 -1 -1]
  [ 6  4 -1 -1]
  [-1  5 -1 -1]]]

 

검출된 요소를 선택하여 비교할 수 있게 해보았습니다. (쓰잘데기)

import os
import cv2

path = os.path.join('img', 'shape.png')
src = cv2.imread(path)
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

contours, hierarchy = cv2.findContours(gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
print(hierarchy)

idx = 0
while True:
    sel = input(f'items 0 ~ {len(hierarchy[0])-1} >> (quit: q) ')
    if sel == 'q':
        break
    if sel.isdigit() and int(sel) < len(hierarchy[0]):
        print(hierarchy[0][int(sel)])

        idx = hierarchy[0, int(sel), 0]
        # idx = hierarchy[0][sel][0]
        dst = src.copy()
        cv2.drawContours(dst, contours, idx, (255, 0, 0), 2, cv2.LINE_8, hierarchy)
        cv2.imshow('src', src)
        cv2.imshow('dst', dst)
        cv2.waitKey(2000)
        cv2.destroyAllWindows()