Language - Python(Opencv)

Python Opencv - #29, 외곽선 검출

KimTory 2021. 11. 23. 22:15

▶ 다양한 외곽선 함수

(1) cv.arcLength()

 외곽선 길이를 반환합니다.

 

(2) cv2.contourArea()

 외곽선이 감싸는 영역의 면적을 반환합니다.

 

(3) cv2.boundingRect()

 주어진 점을 감싸는 최소 크기 사각형(바운딩 박스)를 반환합니다.

 

(4) cv2.minEnclosingCircle()

 주어진 점을 감싸는 최소 크기 원을 반환합니다.

 

 

(5) cv2.minAreaRect()

 주어진 점을 감싸는 최소 크기 회전된 사각형을 반환합니다.

 

 

(6) cv2.minEnclosingTriangle()

 주어진 점을 감싸는 최소 크기 삼각형을 반환합니다.

 

(7) cv2.approxPolyDP()

 외곽선을 근사화(단순화) 합니다.

 

 

(8) cv2.fitEllipse()

 주어진 점에 적합한 타원을 반환합니다.

 

 

(9) cv2.fitLine()

 주어진 점에 적합한 직선을 반환합니다.

 

 

(10) cv2.isContourConvex()

 컨벡스 인지를 검사합니다.

 

(11) cv2.convexHull()

 주어진 점으로부터 컨벡스 헐을 반환합니다.

 

 

(12) cv2.convexityDefects()

 주어진 점과 컨벡스 헐로부터 컨벡스 디펙트를 반환합니다.

 

 

▶자주 쓰이는 외곽선 함수 설명

→ 외곽선 길이 구하기 - cv2.arcLength()

cv2.arcLength(curve, closed) -> retval

• curve: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
• closed: True이면 폐곡선으로 간주
• retval: 외곽선 길이

→ 면적 구하기 - cv2.contourArea()

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

• contour: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
• oriented: True이면 외곽선 진행 방향에 따라 부호 있는 면적을 반환. 기본값은 False.
• retval: 외곽선으로 구성된 영역의 면적

→ 바운딩 박스(외곽선을 외접하여 둘러싸는 가장 작은 사각형) 구하기 - cv2.boundingRect()

cv2.boundingRect(array) -> retval

• array: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
• retval: 사각형 정보. (x, y, w, h) 튜플.

→ 바운딩 서클(외곽선을 외접하여 둘러싸는 가장 작은 원) 구하기

cv2.minEnclosingCircle(points) -> center, radius

• points: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
• center: 바운딩 서클 중심 좌표. (x, y) 튜플.
• radius: 바운딩 서클 반지름. 실수

→ 외곽선 근사화 - cv2.approxPolyDP

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

• curve: 입력 곡선 좌표. numpy.ndarray. shape=(K, 1, 2)
• epsilon: 근사화 정밀도 조절. 입력 곡선과 근사화 곡선 간의 최대 거리. e.g) cv2.arcLength(curve) * 0.02
• closed: True를 전달하면 폐곡선으로 인식
• approxCurve: 근사화된 곡선 좌표. numpy.ndarray. shape=(K', 1, 2)

더글라스-포이커 알고리즘

→ Convex 검사 - cv2.isContourConvex

cv2.isContourConvex(contour) -> retval

• contour: 입력 곡선 좌표. numpy.ndarray. shape=(K, 1, 2)
• retval: 컨벡스이면 True, 아니면 False

[ 다각형 판별 프로그램 예제 ]

 황선규 박사님의 깃허브에 있는 다각형 판별 프로그램 입니다.

 

 원을 판별하는 방법은 도형의 넓이와 도형을 감싸고 있는 외곽선 길이의 비율을 검사합니다.

 하나의 끈으로 만든 가장 면적이 큰 도형은 원입니다.

 분모는 외곽선 길이, 분자는 도형의 넓이를 이용합니다.

 

 A/P를 구하면 미지수 r이 남게 되므로 P를 제곱하여 구합니다.

 

 

 값이 1에 가까울수록 원으로 판단합니다.

 

# 사각형을 그리는 함수
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, 0, 255), 1)
    cv2.putText(img, label, pt1, cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255))
    
def main():
    img = cv2.imread('polygon.bmp', cv2.IMREAD_COLOR)
    
    if img is None:
        print('Image load failed!')
        return
    
    # 그레이스케일 영상으로 변환
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 이진화, INV 이유는 배경이 흰색, 객체가 어두운 영상이므로
    _, img_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
    
    # 외곽선 검출, EXTERNAL로 바깥 외곽선만 도출
    contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    
    # 외곽선 좌표 받아오기
    for pts in contours:
        if cv2.contourArea(pts) < 400: # 노이즈 제거, 너무 작으면 무시
            continue
        
        # 근사화
        approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True)*0.02, True)
        
        # 근사화 결과 점 갯수
        vtc = len(approx)
        
        # 3이면 삼각형
        if vtc == 3:
            setLabel(img, pts, 'TRI')
        # 4면 사각형
        elif vtc == 4:
            setLabel(img, pts, 'RECT')
        else:
            length = cv2.arcLength(pts, True)
            area = cv2.contourArea(pts)
            ratio = 4. * math.pi * area / (length * length)

            if ratio > 0.85:
                setLabel(img, pts, 'CIR')

    cv2.imshow('img', img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
if __name__ == '__main__':
    main()

 

 

 외곽선 정보를 이용하여 도형을 판별하는 프로그램 입니다.