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

[OpenCV] 04-9. Contours (2)

728x90
반응형

< Contour Features >

이번에는

  • contours의 다른 특징인, 영역(area), 둘레(perimeter), 중심(centroid), 경계 상자(bounding box)등을 찾을 것이다.
  • contours와 관련된 수많은 함수를 볼 것이다.

1. Moments

이미지 모멘트(Image moments)는 객체의 무게중심, 객체의 면적과 같은 특성을 계산하는데 도움을 준다. 위키피디아 페이지를 참고하자! Image Moments

cv2.moments() 함수는 계산된 모든 moments 값을 딕셔너리로 제공한다. 아래 코드를 보자!

import cv2
import numpy as np 

img = cv2.imread('./images/golden.jpg',0)
ret, thresh = cv2.threshold(img,127,255,0)
contours, hierarchy = cv2.findContours(thresh,1,2)

cnt = contours[0]
M = cv2.moments(cnt)
print(M)

### 출력
{'m00': 0.5, 'm10': 108.83333333333333, 'm01': 165.33333333333331, 'm20': 23689.416666666664, 'm11': 35987.541666666664, 'm02': 54670.25, 'm30': 5156408.45, 'm21': 7833294.399999999, 'm12': 11899881.9, 'm03': 18077647.7, 'mu20': 0.027777777777373558, 'mu11': -0.01388888888322981, 'mu02': 0.027777777788287494, 'mu30': -0.0037037022411823273, 'mu21': 0.0018518507713469745, 'mu12': 0.0018518510405556299, 'mu03': -0.003703709691762924, 'nu20': 0.11111111110949423, 'nu11': -0.05555555553291924, 'nu02': 0.11111111115314998, 'nu30': -0.0209513037618867, 'nu21': 0.010475649905319876, 'nu12': 0.010475651428194002, 'nu03': -0.020951345908735212}

이 모멘트로부터, 영역, 중심등과 같은 유용한 정보를 추출할 수 있다. 중심은 다음식으로 알 수 있다.

Cx=M10M00,Cy=M01M00

코드로 보면 다음과 같다.

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

2. Contour Area

Contour 영역은 cv2.contourArea() 함수나 모멘트(M[‘m00’])로부터 얻을 수 있다.

area = cv2.contourArea(cnt)

3. Contour Perimeter

contour의 호 길이라고도 불린다. cv2.arcLength() 함수를 사용하여 찾을 수도 있다. 두 번째 인자는 contour가 닫힌 것(True)인지 아니면 그냥 커브인지 모양을 정해준다.

perimeter = cv2.arcLength(cnt,True)

4. Contour Approximation

우리가 지정한 정밀도(precision)에 따라 정점 수가 적은 다른 형태의 contour를 근사한다. 이것이 “Douglas-Peucker” 알고리즘 구현이다.

이를 이해하기 위해, 이미지에서 정사각형을 찾으려고 하지만, 이미지의 일부 문제로 인해 완벽한 정사각형이 아니라 “ 이상한 모양”(아래 첫 번째 이미지에서 보여지는 것 처럼)을 얻었다고 가정해 보자. 이제 이 기능을 사용하여 대략적인 모양을 만들 수 있다. 이 경우 두 번쩨 인수를 입실론이라고 하는데, contour에서 근사 contour까지의 최대 거리이다. 이는 정확도 매개 변수이다. 정확한 출력을 얻으려면 입실론을 현명하게 선택하는 것이 필요하다.

epsilon = 0,1*cv2.arclength(cnt,True)
approx = cv2. approxPolyDP(cnt,epsilon,True)

아래의 두 번째 이미지의 초록선은 “epsilon = 10% of arc length”의 근사 곡선이다. 세 번째 이미지는 “epsilon = 1% of the arc length”와 같다. 세 번째 인자는 곡석이 닫혀있는지 아닌지를 정한다.



5. Convex Hull

Convex Hull은 contour 근사와 유사하게 보이지만, 그렇지 않다(둘 다 가끔 동일한 결과를 낼 수 있다). cv2.convexHull() 함수는 곡선의 볼록성 결함을 확인하고 수정한다. 일반적으로 볼록 곡선은 항상 돌출되어 있거나 가장 낮은 평탄한 곡선이다. 그리고 안에 볼록하게 쌓이면 볼록성 결함(convexity defects)라고 한다. 예를 들어 아래 이미지를 보면, 붉은 선은 볼록한 손의 선체를 보여준다. 양면 화살표는 윤곽선으로부터 선체가 국부적으로 최대 편차인 볼록성 결함을 나타낸다.


여기서 구문에 관하여 더 논의할 것이 조금 있다.

  • cv2.convexHull()

위 함수의 인자들은 다음과 같다.

  • points : points는 우리가 입력하려하는 contours이다.
  • hull : hull은 출력이고 보통 우리가 피하려한다.
  • clockwise : 방향 flag이다. True일 경우 convex hull이 시계방향이고 반대의 경우 반시계방향이다.
  • returnPoints : 기본값은 True이다. 그러면 hull points의 좌표를 반환한다. 만약 False일 경우, hull points에 상응하는 contour points의 지표들을 반환한다.

따라서 위 이미지처럼 convex hull을 얻으려면 다음처럼 하면 충분하다

hull = cv2.convexHull(cnt)

하지만 만약 볼록성 결함(convexity defects)을 찾고싶다면 returnPoints = False를 넘겨줘야한다. 이를 이해하기 위해, 우리는 위의 사각형 이미지를 취할 것이다. 첫 번째로 cnt로 그 contour를 찾았다. 그리고 방금 returnPoints = True로 conex hull을 찾았다.

[[[234 202]], [[ 51 202]], [[ 51 79]], [[234 79]]]

이는 정사각형의 네 꼭짓점 좌표이다. 만약 returnPoints = False를 한다면 다음의 결과를 얻는다.

[[129],[ 67],[ 0],[142]]

이는 contours의 점과 상응하는 지표이다. cnt[129] = [[234, 202]]인데 이는 첫 번째의 결과와 같다.

6. Checking Convexity

cv2.isContourConvex()라는 커브가 볼록한지 아닌지를 확인하는 함수가 있다. 이는 True, False로 출력된다.

k = cv2.isContourConvex(cnt)

7. Bounding Rectangle

두 가지 타입의 경계 직사각형(bounding rectangle)이 있다.

7.a. Straight Bounding Rectangle

이는 곧은 직사각형으로, 객체의 회전을 신경쓰지 않는다. 그래서 bounding rectangle의 영역이 최소가 되지 않는다. 이는 cv2.boundingRect()함수로 찾는다.

(x,y)는 rectangle의 좌측 위의 좌표이고, (w,h)는 폭과 높이다.

7.b. Rotated Rectangle

이번에는 회전을 신경써서 bounding rectangle의 영역을 최소로 만드는 방법이다. cv2.minAreaRect()로 가능하다. 하지만 이 직사각형을 그리려면, 직사각형의 4 코너를 필요로한다. 이는 cv2.boxPoints()가 가지고 있다.

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

ret,thresh = cv2.threshold(imgray,127,255,0)
contours,hierarchy = cv2.findContours(thresh, 1, 2)

cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)

im = cv2.drawContours(img,[box],0,(0,255,0),3)

plt.imshow(im,'gray')
plt.show()


파란색 직사각형은 보편적인 bounding rect를 보여준다. 초록색 사각형은 ratated rect로 최소영역을 보여준다!

8. Minimum Enclosing Circle

다음으로 cv2.minEnclosingCircle()을 이용하여 객체의 외접원을 찾을 것이다. 이는 객체와 최소의 영역을 갖도록 덮는 원을 말한다.

img = cv2.imread('./images/light.jpg')

(x,y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)

plt.imshow(img)
plt.show()


9. Fitting an Ellipse

다음에는 타원으로 객체를 맞춰볼 것이다. 이는 타원이 새겨져있는 회전된 직사각형을 반환한다.

img = cv2.imread('./images/light.jpg')

ellipse = cv2.fitEllipse(cnt)
img = cv2.ellipse(img,ellipse,(0,255,0),2)
plt.imshow(img)
plt.show()


10. Fitting a Line

비슷하게 일련의 점들에 선을 맞출 수 있다. 아래 이미지에는 일련의 파란점이 포함되어 있다. 그것에 대해 대략 직선으로 말할 수 있다.

img = cv2.imread('./images/light.jpg')

rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt,cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

plt.imshow(img)
plt.show()


728x90
반응형