13장. Shape 분석과 ROI 처리
종횡비와 채움 비율
택배 상자에 물건을 담는다고 해 봅시다. 우산은 길쭉한 상자가 필요하고, 공은 정육면체 상자가 맞습니다. 그리고 공을 상자에 넣으면 네 귀퉁이가 비고, 책을 넣으면 상자가 꽉 찹니다. 상자의 가로세로 비율이 종횡비이고, 상자를 얼마나 채웠는지가 채움 비율입니다.
그림의 보라 네모가 12장에서 본 바운딩 박스(cv2.boundingRect)입니다. 긴 막대는 상자가 가로로 길어 종횡비가 3 쯤이고, 네모와 원은 1 입니다. 채움 비율은 네모와 막대가 1 에 가깝고, 원은 귀퉁이가 비어 0.73 쯤입니다. 오른쪽 기운 네모는 상자가 커져 채움 비율이 0.6 쯤으로 떨어집니다. 다홍 네모는 12장의 회전 상자(cv2.minAreaRect)이고, 이 상자로 재면 기운 네모도 꽉 찹니다. 여기서 택배 상자가 감싸는 상자, 빈 귀퉁이가 채움 비율을 깎는 몫입니다.
두 숫자 구하기
| 숫자 | 식 | 긴 막대(65×21, 넓이 1,280) | 네모(53×53, 넓이 2,704) |
|---|---|---|---|
| 종횡비 | 가로 ÷ 세로 | 65 ÷ 21 ≈ 3.10 | 53 ÷ 53 = 1.00 |
| 채움 비율 | 넓이 ÷ (가로 × 세로) | 1,280 ÷ 1,365 ≈ 0.94 | 2,704 ÷ 2,809 ≈ 0.96 |
boundingRect 의 가로세로는 끝 칸까지 세어서 그린 크기보다 1칸 큽니다. 넓이(contourArea)는 테두리 점을 이은 다각형의 넓이라 반 칸쯤 작게 재어집니다. 그래서 꽉 찬 네모도 채움 비율이 1 이 아니라 0.96 쯤 나옵니다.
짧은 예시
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)
for c in contours:
x, y, w, h = cv2.boundingRect(c)
# 가운데가 오른쪽 위(x 250 넘음, y 100 아래)인 것이 긴 막대다
if x > 240 and y < 100:
aspect = w / h # 종횡비
extent = cv2.contourArea(c) / (w * h) # 채움 비율
print(w, h, round(aspect, 2), round(extent, 3)) # 결과: 65 21 3.1 0.938주의할 점
boundingRect는 언제나 똑바로 선 상자입니다. 물체가 기울면 상자가 커져 채움 비율이 떨어지고, 기운 막대는 종횡비도 1 에 가까워집니다. 기운 물체는cv2.minAreaRect의 가로세로로 잽니다.- 종횡비는 가로 ÷ 세로라서, 세로로 선 막대는 0.3 처럼 1 보다 작게 나옵니다. 방향과 관계없이 "얼마나 길쭉한가"만 보려면 긴 변 ÷ 짧은 변으로 잽니다.