Python/파이썬 OpenCV 공부

[파이썬 OpenCV] 영상의 외곽선 검출과 그리기 - cv2.findContours, cv2.drawContours

AI 꿈나무 2020. 10. 11. 20:29
반응형

황선규 박사님의 'OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝' 을 공부하면서 정리해 보았습니다.

출처 : docs.opencv.org/4.3.0/d6/d00/tutorial_py_root.html

 

예제 코드 출처 :  황선규 박사님 github홈페이지

 

『OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝』

예제 소스 코드는 아래 링크를 참고하세요

sunkyoo.github.io


외곽선 검출

 외곽선 검출이란 객체의 외곽선 좌표를 모두 추출하는 작업 입니다.

 바깥선 외곽선뿐 아니라 옵션을 적용함으로써 안쪽 외곽선도 검출할 수 있습니다.

 외곽선 간의 상관관계도 알 수 있으며, 외곽선의 계층 구조도 표현할 수 있습니다.

 

 외곽선 객체의 표현 방법은 numpy.ndaaray, shape=(k, 1, 2) (k는 외곽선 좌표 개수, 2는 x,y 좌표), 데이터 타입은 numpy.int32로 표현됩니다.

 

 여러 외곽선을 표현할 때는 객체 하나의 외곽선을 원소로 갖는 리스트로 표현됩니다.

 따라서 전체 리스트의 갯수는 전체 외곽선 갯수(N)입니다.

 

1. 외곽선 검출 함수 - cv2.findContours

 cv2.findContours 명령어로 외곽선 정보를 검출할 수 있습니다.

 사용하는 함수의 파라미터를 다양하게 줄 수 있어서 복잡합니다.

 

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

• image: 입력 영상. non-zero 픽셀을 객체로 간주함.

• mode: 외곽선 검출 모드. cv2.RETR_로 시작하는 상수.

• method: 외곽선 근사화 방법. cv2.CHAIN_APPROX_로 시작하는 상수.

• contours: 검출된 외곽선 좌표. numpy.ndarray로 구성된 리스트. len(contours)=전체 외곽선 개수(N). contours[i].shape=(K, 1, 2). contours[i].dtype=numpy.int32.

• hierarchy: 외곽선 계층 정보. numpy.ndarray. shape=(1, N, 4). dtype=numpy.int32. hierarchy[0, i, 0] ~ hierarchy[0, i, 3]이 순서대로 next, prev, child, parent 외곽선 인덱스를 가리킴. 해당 외곽선이 없으면 -1.

• offset: 좌표 값 이동 옵셋. 기본값은 (0, 0).

 

image : 입력 영상입니다. 이진 영상을 입력해야 합니다. 흰색 객체의 안쪽, 홀, 외곽 외곽선을 검출할 수 있습니다.

 

mode : 외곽선을 표시할 4가지 방법을 선택할 수 있습니다.

 

출처 https://docs.opencv.org/4.3.0/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71

 

 (1) EXTERNAL : 계층 정보 x, 바깥 외곽선만 검출합니다. 단순히 리스트로 묶어줍니다.

 (2) LIST : 계층 정보 x, 모든 외곽선을 검출합니다.(바깥, 안쪽), 순서는 랜덤입니다.

 (3) CCOMP : 계층 구조를 만들긴 하지만 2층 까지만 표현합니다. 2층이 넘어가면 1층으로 부여합니다.

 (4) TREE : 계층 구조를 만듭니다. 

 

 

 method : 외곽선을 근사화하거나 단순화합니다. 주로 기본값을 사용합니다. .SIMPLE은 수직선, 수평선, 대각선에 대해 끝점만 저장합니다.

 

 contours : 입력인자를 비워두고 출력으로 받습니다.

 

 hierarchy : 외곽선 계층 정보를 ndarray로 저장합니다. 형태는 (1, N, 4)이며 N은 외곽선 갯수고 데이터 타입은 int32 입니다. 계층 구조에 의해서 4열(next, prev, child, parents의 인덱스 번호)를 갖고 있습니다. 인덱스 번호가 없으면 -1로 표현됩니다. mode를 EXTERNAL을 입력하면 parents, child 인덱스 번호는 없고 next 인덱스만 존재하게 됩니다. 상하 관계에서 위로 가는 것이 parents, 아래로 가는 것이 child 입니다. child가 3개 있으면 대표값 하나만 지정해서 1번이 됩니다. 다음 child는 next로 표현됩니다.

 

2. 외곽선 그리기 함수 - cv2.drawContours

 검출한 외곽선을 확인하기 위해 이 함수를 이용하여 외곽선을 화면에 그릴 수 있습니다. 검증 역할을 하기도 합니다.

 

cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=No

• image: 입출력 영상 • contours: (cv2.findContours() 함수로 구한) 외곽선 좌표 정보
• contourIdx: 외곽선 인덱스. 음수(-1)를 지정하면 모든 외곽선을 그린다.
• color: 외곽선 색상 • thickness: 외곽선 두께. thinkness < 0이면 내부를 채운다.
• lineType: LINE_4, LINE_8, LINE_AA 중 하나 지정
• hierarchy: 외곽선 계층 정보.
• maxLevel: 그리기를 수행할 최대 외곽선 레벨. maxLevel = 0 이면 contourIdx로 지정된 외곽선만 그린다.

3. 계층 정보를 사용하는 외곽선 검출 예제

  외곽선을 검출하고 계층 정보로 외곽선을 그려보겠습니다.

 

src = cv2.imread('contours.bmp', cv2.IMREAD_GRATSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# 외곽선 정보 검출
contours, hier = cv2.findContours(src, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

# 컬러 영상으로 변환
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

while idx >= 0:
    # 랜덤 색상 지정
    c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    
    # 외곽선 그리기
    # hier 계층 정보를 입력했으므로 모든 외곽선에 그림을 그리게 됩니다.
    cv2.drawContours(dst, contours, idx, c, 2, cv2.LINE_8, hier)
    
    # 0번째 계층만 그리기. 하지만 hier 계층 정보를 입력했기 때문에 모든 외곽선에 그림을 그립니다.
    # 계층 정보를 입력 안하면 0번 계층만 그립니다.
    idx = hier[0, idx, 0]

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

 

 

 감사합니다.

반응형