13장. Shape 분석과 ROI 처리
볼록 껍질과 볼록 비율
선물을 포장할 때 물건 둘레에 고무줄을 한 바퀴 감는다고 해 봅시다. 공이나 상자는 고무줄이 몸에 딱 붙습니다. 그런데 별 모양 쿠키나 초승달 모양 빵에 감으면, 고무줄은 뾰족한 끝끼리 곧게 이어지고 움푹한 곳은 비어 있습니다. 고무줄이 만든 모양이 볼록 껍질이고, 물체가 그 안을 얼마나 채우는지가 볼록 비율입니다.
그림의 다홍 선이 고무줄, 곧 cv2.convexHull 로 구한 볼록 껍질입니다. 네모는 고무줄이 몸에 딱 붙어 볼록 비율이 1 입니다. 별은 뾰족한 끝 사이가 비어 0.51 쯤, 초승달은 안쪽이 크게 비어 0.58 쯤입니다. 여기서 고무줄 안의 넓이가 볼록 껍질 넓이이고, 고무줄과 물체 사이의 빈 곳이 볼록 비율을 깎는 몫입니다.
볼록과 움푹 견주기
| 견줄 것 | 볼록한 모양 | 움푹한 모양 |
|---|---|---|
| 예 | 원, 네모, 세모, 팔각형 | 별, 초승달, 톱니바퀴, 알파벳 C |
| 고무줄 | 몸에 딱 붙는다 | 움푹한 곳 위로 곧게 지나간다 |
| 볼록 비율 | 1 에 가깝다(0.95 넘음) | 1 보다 뚜렷이 작다 |
isContourConvex (근사 다각형에 쓸 때) | True | False |
짧은 예시
import cv2
img = cv2.imread("ch13_shapes.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 넓이가 가장 작은 것(1,230)이 별이다
star = min(contours, key=cv2.contourArea)
hull = cv2.convexHull(star) # 고무줄을 감은 모양의 꼭짓점들
solidity = cv2.contourArea(star) / cv2.contourArea(hull)
print(round(solidity, 3)) # 결과: 0.507
# isContourConvex 는 근사 다각형에 써야 뜻이 분명하다
approx = cv2.approxPolyDP(star, 0.01 * cv2.arcLength(star, True), True)
print(cv2.isContourConvex(approx)) # 결과: False → 움푹한 곳이 있다
print(cv2.isContourConvex(cv2.convexHull(star))) # 결과: True → 고무줄은 언제나 볼록별의 넓이는 볼록 껍질 넓이의 절반쯤입니다. 뾰족한 끝 다섯 개 사이의 빈 곳이 그만큼 큽니다.
주의할 점
- 볼록하게 그린 원도 그림 속에서는 칸의 계단 때문에 윤곽선 그대로는
isContourConvex가 False 로 나올 수 있습니다. 근사 다각형으로 바꾼 뒤 묻거나, 볼록 비율(0.95 넘음)로 판단합니다. - 볼록 비율은 "얼마나 움푹한가"만 알려 줍니다. 별과 초승달처럼 움푹한 것끼리는 꼭짓점 수 같은 다른 숫자로 가릅니다.