본문으로 바로가기

파이썬 OpenCV 도형 검출하기

category OpenCV 2020. 8. 5. 23:54

예제

 

전체 예제 코드입니다.

import os
import math
import cv2

def setLabel(img, pts, label):
    (x, y, w, h) = cv2.boundingRect(pts)
    pt1 = (x, y)
    pt2 = (x + w, y + h)
    cv2.rectangle(img, pt1, pt2, (0, 255, 0), 2)
    cv2.putText(img, label, (pt1[0], pt1[1]-3), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255))

path = os.path.join('img', 'shape.png')

img = cv2.imread(path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, thr = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)

contours, _ = cv2.findContours(thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

for cont in contours:
    approx = cv2.approxPolyDP(cont, cv2.arcLength(cont, True) * 0.02, True)
    vtc = len(approx)

    if vtc == 3:
        setLabel(img, cont, 'Tri')
    elif vtc == 4:
        setLabel(img, cont, 'Rec')
    elif vtc == 5:
        setLabel(img, cont, 'Pen')
    else:
        area = cv2.contourArea(cont)
        _, radius = cv2.minEnclosingCircle(cont)

        ratio = radius * radius * math.pi / area
        if int(ratio) == 1:
            setLabel(img, cont, 'Cir')

cv2.imshow('img', img)
cv2.imshow('binary', thr)

cv2.waitKey()
cv2.destroyAllWindows()

 

사용한 이미지입니다.

 


이미지를 불러오고 그레스케일로 변환한 후에 이진화를 합니다.

img = cv2.imread(path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, thr = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)

 

외곽선을 검출합니다.

계층정보는 필요없어서 생략했구요.

contours, _ = cv2.findContours(thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

 

검출된 외곽선들을 반복하면서 꼭지점을 구합니다.

for cont in contours:
    approx = cv2.approxPolyDP(cont, cv2.arcLength(cont, True) * 0.02, True)
    vtc = len(approx)

 

cv2.approxPolyDP(curve, epsilon, closed, approxCurve=None) -> approxCurve

  • curve: 외곽선 좌표 (contour)

  • epsilon: 극사화 정밀도 (cv2.arcLength(pts, True) * 0.01 ~ 0.05 로 많이 사용)

  • closed: True (폐곡선)

  • approxCurve: 극사화된 곡선의 좌표

 

cv2.arcLength(curve, closed) -> retval

  • curve: 외곽선 좌표 (contour)

  • closed: True (폐곡선)

  • retval: 외곽선 길이 (폐곡선이라면 둘레)

 

꼭지점 개수가 3이면 삼각형, 4이면 사각형, 5이면 오각형으로 식별했습니다.

if vtc == 3:
    setLabel(img, cont, 'Tri')
elif vtc == 4:
    setLabel(img, cont, 'Rec')
elif vtc == 5:
    setLabel(img, cont, 'Pen')

 

다음은 원인데요.

다음과 같이 두 식의 결과를 비교해서 원이라고 식별했습니다.

area = cv2.contourArea(cont)
_, radius = cv2.minEnclosingCircle(cont)

ratio = radius * radius * math.pi / area
if int(ratio) == 1:
    setLabel(img, cont, 'Cir')

 

cv2.contourArea(curve, oriented=None) -> retval

  • curve: 외곽선 좌표 (contour)

  • oriented(default=False)

  • True: 외곽선 진행 방향이 시계/반시계 방향에 따라 +/-

  • False: 항상 +

  • retval: 외곽선으로 둘러 쌓인 영역의 면적

 

cv2.minEnclosingCircle(curve) -> center, radius

  • curve: 외곽선 좌표 (contour)

  • center: bounding circle 의 중심 좌표 (x, y)

  • radius: bounding circle 의 반지름(실수)