12장. Contour Detection 과 객체 분석
물체를 감싸는 상자와 원
택배 상자에 물건을 넣는 일을 떠올려 봅시다. 바닥과 나란한 상자를 고르면 비스듬히 놓인 긴 물건은 상자가 커집니다. 물건 방향으로 상자를 돌려 담으면 훨씬 작은 상자로 됩니다. 둥근 통에 담으려면 물건의 가장 먼 두 끝이 들어가는 원을 고릅니다. 물체를 감싸는 틀도 이렇게 세 가지를 씁니다.
초록 상자의 곧은 상자(cv2.boundingRect)는 변이 화면의 가로세로와 나란합니다. 다홍 상자의 회전 상자(cv2.minAreaRect)는 넓이가 가장 작아지게 물체를 따라 기웁니다. 보라 상자의 최소 외접원(cv2.minEnclosingCircle)은 모든 점을 품는 가장 작은 원입니다. 여기서 바닥과 나란한 택배 상자가 곧은 상자, 돌려 담은 상자가 회전 상자, 둥근 통이 외접원입니다.
같은 도형에 세 가지 틀을 그렸습니다. 왼쪽의 곧은 상자(초록)는 30도 기운 네모와 타원에서 빈자리가 크게 남습니다. 가운데 회전 상자(다홍)는 기운 네모에 꼭 맞습니다. 오른쪽 외접원(보라)은 원에는 꼭 맞지만 길쭉한 도형에서는 빈자리가 많습니다.
세 가지 틀 견주기
| 틀 | 함수 | 돌려주는 값 | 그리는 법 | 쓰는 곳 |
|---|---|---|---|---|
| 곧은 상자 | cv2.boundingRect(c) | (x, y, w, h) 정수 넷 | cv2.rectangle | 물체 잘라 내기, 탐지 결과 표시 |
| 회전 상자 | cv2.minAreaRect(c) | ((중심 x, y), (가로, 세로), 각도) | cv2.boxPoints 로 꼭짓점을 구해 cv2.drawContours | 기운 물체의 길이와 폭 |
| 최소 외접원 | cv2.minEnclosingCircle(c) | ((중심 x, y), 반지름) | cv2.circle | 둥근 물체의 지름 |
기운 네모로 견줘 보기
도형 판의 기운 네모는 가로 76, 세로 34 를 30도 돌려 칠했습니다. 교안 PC 에서 잰 값입니다.
| 잰 것 | 값 | 넓이 |
|---|---|---|
| contourArea | 윤곽선이 감싼 넓이 | 2,596 |
| 회전 상자 | 76.4 × 35.3 | 약 2,696 |
| 곧은 상자 | 83 × 69 | 5,727 |
회전 상자는 칠한 크기(76×34)와 거의 같습니다. 곧은 상자는 물체 넓이의 두 배가 넘습니다. 기운 물체의 길이를 재려면 회전 상자를 씁니다.
짧은 예시
import cv2
import numpy as np
shapes = cv2.imread("ch12_shapes.png", cv2.IMREAD_GRAYSCALE)
contours, _ = cv2.findContours(shapes, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
tilted = contours[5] # 30도 기운 네모
# boundingRect: 왼쪽 위 x, y 와 가로 w, 세로 h
x, y, w, h = cv2.boundingRect(tilted)
print("곧은 상자:", (x, y, w, h))
# minAreaRect: ((중심 x, 중심 y), (가로, 세로), 각도)
(cx, cy), (rw, rh), angle = cv2.minAreaRect(tilted)
print("회전 상자 크기:", round(rw, 1), round(rh, 1))
# boxPoints: 회전 상자의 꼭짓점 네 개(실수). 그리려면 정수(int32)로 바꾼다
box = np.round(cv2.boxPoints(((cx, cy), (rw, rh), angle))).astype(np.int32)
print("꼭짓점:", box.tolist())
# minEnclosingCircle: (중심 x, 중심 y), 반지름
(ox, oy), r = cv2.minEnclosingCircle(contours[6]) # 원
print("원의 외접원 반지름:", round(r, 1))교안 PC 에서는 곧은 상자가 (221, 24, 83, 69), 회전 상자 크기가 35.3, 76.4, 원의 외접원 반지름이 35.0 입니다. 원은 반지름 35 로 칠했으므로 외접원이 원 자체와 같습니다. 꼭짓점 네 개는 기운 네모의 네 모서리 근처에 놓입니다.
주의할 점
cv2.boxPoints는 실수를 돌려줍니다. OpenCV 5.0 의cv2.drawContours는 정수 좌표만 받으므로np.round(...).astype(np.int32)로 바꿉니다.- 회전 상자의 가로·세로와 각도는 OpenCV 가 정한 기준을 따라서, 같은 상자가 (35.3, 76.4) 로도 (76.4, 35.3) 으로도 나올 수 있습니다. 길이와 폭은 두 값 가운데 큰 쪽과 작은 쪽으로 읽고, 각도는 그림을 그려 확인합니다.
cv2.boundingRect의 w, h 는 칸 수입니다.cv2.rectangle로 딱 맞게 그리려면 오른쪽 아래를 (x + w - 1, y + h - 1) 로 줍니다.