12장. Contour Detection 과 객체 분석
분석 결과를 표로 정리하기
학교에서 건강 검진을 하면 학생마다 번호, 키, 몸무게를 한 줄씩 적은 표가 나옵니다. 번호가 있으니 "17번 학생"을 찾아갈 수 있습니다. 물체 분석도 같습니다. 물체마다 번호를 붙이고 면적, 둘레, 중심, 상자를 한 줄씩 적은 표를 만들고, 그림에도 같은 번호를 써 둡니다.
그림의 다섯 단계로 표를 만듭니다. 거르고 남은 윤곽선마다 숫자를 재고, 물체 하나를 사전 하나(행 하나)로 만들고, 사전 목록을 pandas 의 DataFrame 으로 바꾼 뒤, 그림에도 같은 번호를 씁니다. 여기서 학생이 물체, 검진표의 한 줄이 사전 하나, 학생 번호가 물체 번호입니다.
작업대 사진의 부품마다 곧은 상자(초록)와 번호(다홍)를 그렸습니다. 번호는 위쪽 줄부터, 같은 줄에서는 왼쪽부터 매겼습니다. cv2.findContours 가 준 차례 그대로 번호를 매기면 아래쪽 물체가 1번이 되어 표를 읽기 어렵습니다.
표의 열과 재는 함수
| 열 | 재는 함수 | 단위 |
|---|---|---|
| 번호 | 위치로 정렬한 차례 | - |
| 면적 | cv2.contourArea | 칸의 넓이 |
| 둘레 | cv2.arcLength(c, True) | 칸 |
| 중심 x, y | cv2.moments 의 m10 ÷ m00, m01 ÷ m00 | 칸 |
| 상자 x, y, w, h | cv2.boundingRect | 칸 |
cv2.putText 로 번호 쓰기
cv2.putText(그림, 글, 왼쪽 아래 좌표, 글꼴, 크기, 색, 두께) 로 그림에 글자를 씁니다. 글꼴은 cv2.FONT_HERSHEY_SIMPLEX 처럼 OpenCV 에 든 것만 쓸 수 있고, 이 글꼴에는 한글이 없습니다. 그래서 그림에는 숫자와 영문(작음은 S, 보통은 M, 큼은 L 처럼)만 쓰고, 한글 설명은 표나 Matplotlib 제목에 씁니다. OpenCV 5.0 의 cv2.putText 는 글자 가장자리를 부드럽게 그리므로, 보기용 그림에는 그대로 써도 되지만 이진 그림에 쓰면 0 과 255 사이의 값이 생깁니다.
짧은 예시
import cv2
import pandas as pd
gray = cv2.imread("ch12_tray.png", cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
parts = [c for c in contours if cv2.contourArea(c) >= 100]
rows = []
for c in parts:
m = cv2.moments(c)
x, y, w, h = cv2.boundingRect(c)
rows.append({"면적": cv2.contourArea(c), "둘레": round(cv2.arcLength(c, True), 1),
"cx": round(m["m10"] / m["m00"], 1), "cy": round(m["m01"] / m["m00"], 1),
"x": x, "y": y, "w": w, "h": h})
table = pd.DataFrame(rows)
# 위치로 정렬: cy 를 80 칸 단위의 줄로 나누고, 같은 줄에서는 cx 가 작은 쪽부터
table["줄"] = (table["cy"] // 80).astype(int)
table = table.sort_values(["줄", "cx"]).reset_index(drop=True)
table.index = table.index + 1 # 번호를 1 부터
print(table.head(4))교안 PC 에서는 첫 줄(1번)이 왼쪽 위의 큰 원으로, 면적 1542.0, 중심 (40.0, 45.0) 입니다. 줄을 80 칸으로 나눈 것은 이 사진에서 부품이 세 줄로 놓였기 때문입니다. 다른 사진에서는 부품 크기에 맞춰 줄 높이를 고칩니다.
주의할 점
- 정렬하기 전 차례와 정렬한 뒤 차례가 다르므로, 그림에 번호를 쓸 때는 정렬한 표의 번호를 씁니다. 표에 상자 좌표를 함께 두면 그림에 그릴 때 그대로 꺼내 쓸 수 있습니다.
cv2.putText의 좌표는 글자의 왼쪽 아래입니다. 상자의 왼쪽 위 (x, y) 에 쓰면 글자가 상자 위로 올라가는데, 그림 맨 위의 물체면 글자가 잘립니다.max(y - 3, 12)처럼 아래로 내려 줍니다.