[OpenCV] 04-9. Contours (4)
🐍Python/OpenCV

[OpenCV] 04-9. Contours (4)

728x90
반응형

< Contours : More Functions >

이번 장에서는

  • Convexity defects와 이를 어떻게 찾아내는지를 볼 것이다
  • 점에서 다각형까지의 최단 거리를 찾을 것이다.
  • 다른 모양들을 매칭시킬 것이다.

Theory and Code

1. Convexity Defects

두 번째 장에서 convex hull이 무엇인지 보았다. 이 hull에서 객체의 이탈은 convexity defect로 간주될 수 있다.

OpenCV는 이를 찾을 수 있는 완성된 함수인 cv2.convexityDefects() 를 제공한다. 기본 함수는 다음과 같다.

hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)

convex hull을 찾을 때, convexity defects를 찾기 위해 returnPoints = False를 넘겨야 한다는 것을 기억하자.

각 행이 다음의 값을 포함하는 array를 반환한다. [ 시작점, 끝점, 가장 먼점, 가장 먼 점까지의 대략적인 거리 ] 이를 이미지를 이용하여 시각화 할 수 있다. 시작점과 끝점을 연결하는 선을 그은 다음 가장 먼 지점에 원을 그린다. 처음 세 값이 cnt의 지수라는 것을 기억하자. 그래서 우리는 그 값들을 cnt에서 가져와야한다.

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('./images/star.jpg')
imgray =cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

thr = cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)[1]

contours,_ = cv2.findContours(thr,2,1)
# cnt = max(contours, key=cv2.contourArea)
cnt = contours[-1]

hull = cv2.convexHull(cnt)
cv2.drawContours(img,[hull],0,(0,0,255),2)

hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)

for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(img,start,end,[0,255,0],4)
    cv2.circle(img,far,5,[255,0,0],10)
plt.imshow(img)
plt.axis('off')
plt.show()


returnPoints = False를 두지 않으므로써 초록 선을 찾고 False로 두고나서 defects를 구하여 빨간 점을 나타냈다.

2. Points Polygon Test

이 함수는 이미지 내의 점과 contour(윤곽) 간의 가장 짧은 거리를 찾아준다. 이는 거리를 출력하고 contour 밖에 점이 있을 경우 음의 값으로 나타내고, 양의 값은 내부에 있거나 0일 경우, 점이 contour위에 있을 때를 말한다.

예시로, 점 (50,50)으로 확인해보자.

dist = cv2.pointPolygonTest(cnt,(50,50),True)
cv2.circle(img,(50,50),3,(255,0,0),10)
plt.imshow(img)
plt.show()
print("Contour와 점의 거리 : {0:.2f}".format(dist))


점이 윤곽 밖에 있기에 음의 값으로 나온 것을 볼 수 있다.

이 함수에서, 세 번째 인자는 measureDist이다. 이것이 True일 경우, contour와 점 사이의 최단 거리를 반환한다. 만약 False일 경우, 점이 내부에 있는지 외부에 있는지 또는 contour 위에 있는지를 나타낸다 ( +1,-1, 0)

거리를 알고싶지 않다면 3번째 인자를 False로 설정하여 2~3배 빠른 속도를 얻을 수 있다.

3. Match Shapes

OpenCV의 cv2.matchShapes()는 두 모양이나 두 contours를 비교하고 유사도를 보여주는 지표를 도출한다. 값이 낮을 수록 더 잘 맞는 것이다. 이는 hu-moment 값을 기반으로 계산된다. 다른 측정 방식은 문서에 설명되어있다.

img1 = cv2.imread('./images/star.jpg',0)
img2 = cv2.imread('./images/box.jpg',0)
img3 = cv2.imread('./images/ho.jpg',0)

thr1 = cv2.threshold(img1,127,255,cv2.THRESH_BINARY)[1]
thr2 = cv2.threshold(img2,127,255,cv2.THRESH_BINARY)[1]
thr3 = cv2.threshold(img3,127,255,cv2.THRESH_BINARY)[1]

contours,_ = cv2.findContours(thr1,2,1)
cnt1 = contours[0]

contours2,_ = cv2.findContours(thr2,2,1)
cnt2 = contours2[0]

contours3,_ = cv2.findContours(thr3,2,1)
cnt3 = contours3[0]

retAB = cv2.matchShapes(cnt1,cnt2,1,0.0)
retAC = cv2.matchShapes(cnt1,cnt3,1,0.0)
retBC = cv2.matchShapes(cnt2,cnt3,1,0.0)

plt.figure(figsize=(12,8))
plt.subplot(1,3,1),plt.imshow(img1,'gray')
plt.axis('off'), plt.title('A')
plt.subplot(1,3,2),plt.imshow(img2,'gray')
plt.axis('off'), plt.title('B')
plt.subplot(1,3,3),plt.imshow(img3,'gray')
plt.axis('off'), plt.title('C')

print("A와 B차이 : {0:.2f} / A와 C차이 : {1:.2e} / B와 C차이 : {2:.2e}".format(
    retAB,retAC,retBC))
plt.show()


비교시에는 방향은 큰 의미가 없고 모양 자체를 본다.

728x90
반응형