18장. OpenCV 전처리와 YOLO 연계
추론 결과 다루기
택배를 받으면 상자를 열어 내용물을 꺼내고, 주문서와 맞춰 본 뒤, 필요 없는 포장재는 버립니다. 추론 결과도 같습니다. predict 가 돌려준 결과 상자에서 상자 좌표, 신뢰도, 클래스를 꺼내고, 신뢰도가 낮은 것은 버린 뒤, 남은 것을 사진 위에 그립니다.
그림의 네 단계가 추론 결과를 다루는 순서입니다. 여기서 택배 상자가 results[0], 내용물이 xyxy·conf·cls, 포장재를 버리는 일이 신뢰도 기준값으로 거르기입니다. 마지막 그리기는 12장에서 쓴 cv2.rectangle 과 cv2.putText 를 그대로 씁니다.
결과를 꺼내는 코드 꼴
| 코드 | 하는 일 |
|---|---|
results = model.predict(그림, ...) | 그림 목록을 넣으면 그림마다 결과 하나씩 든 목록을 돌려준다 |
r = results[0] | 그림 한 장만 넣었으면 첫 결과만 쓴다 |
r.boxes.xyxy.cpu().numpy() | 상자 좌표를 NumPy 배열로. .cpu() 는 계산 장치에서 꺼내 오는 일 |
r.boxes.conf.cpu().numpy() | 신뢰도 배열 |
r.boxes.cls.cpu().numpy().astype(int) | 클래스 번호 배열. 실수로 오므로 정수로 바꾼다 |
r.names | 번호 → 이름 사전 |
predict 에는 파일 경로도, cv2.imread 로 읽은 그림 배열도 넣을 수 있습니다. 배열을 넣을 때는 OpenCV 의 BGR 순서 그대로 넣습니다. ultralytics 도 BGR 로 받기 때문입니다. 그래서 OpenCV 로 손질한 그림을 저장하지 않고 바로 넣을 수 있고, 실습 5와 프로젝트가 이 방법을 씁니다.
신뢰도 기준값
predict(..., conf=0.25) 는 신뢰도 0.25 보다 낮은 상자를 버리고 돌려줍니다. 0.25 는 ultralytics 의 기본값입니다.
| 기준값 | 남는 상자 | 좋아지는 것 | 나빠지는 것 |
|---|---|---|---|
| 낮게(예: 0.1) | 많다 | 놓친 것이 줄어 재현율이 오른다 | 헛것이 늘어 정밀도가 떨어진다 |
| 기본(0.25) | 중간 | 둘 사이의 타협 | |
| 높게(예: 0.5) | 적다 | 헛것이 줄어 정밀도가 오른다 | 놓친 것이 늘어 재현율이 떨어진다 |
표는 보통의 경우입니다. 이 장에서 학습한 모델은 가상 그림의 부품을 모두 신뢰도 0.5 이상으로 찾아서, 0.25 와 0.5 의 결과가 같았습니다(실습 3). 기준값의 효과는 모델이 자신 없어하는 사진이 섞일 때 드러납니다.
import cv2
import numpy as np
img = cv2.imread("ch17_raw/part_022.png")
# 찾았다고 치고 지어낸 결과 세 개(실습 3에서 실제 결과로 한다)
xyxy = np.array([[52, 160, 92, 200], [131, 220, 175, 239], [300, 25, 320, 65]], dtype=float)
conf = np.array([0.91, 0.47, 0.12])
cls = np.array([1, 1, 2])
names = {0: "bolt", 1: "nut", 2: "washer"}
keep = conf >= 0.25 # 기준값 이상만 True 인 배열
print("남는 상자:", int(keep.sum()), "개 / 전체", len(conf), "개")
for box, p, c in zip(xyxy[keep], conf[keep], cls[keep]):
x1, y1, x2, y2 = [int(v) for v in box]
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
# 이름과 신뢰도를 상자 위에 쓴다. 위쪽 테두리에 붙으면 글자가 잘리므로 max 로 막는다
cv2.putText(img, f"{names[int(c)]} {float(p):.2f}", (x1, max(y1 - 4, 12)),
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 1)conf >= 0.25 는 1장의 is_red 처럼 칸마다 True/False 가 든 배열입니다. xyxy[keep] 처럼 넣으면 True 인 줄만 남습니다.
주의할 점
xyxy는 소수로 옵니다.cv2.rectangle은 정수만 받으므로int로 바꿔 넣습니다.r.plot()을 쓰면 ultralytics 가 상자를 대신 그려 줍니다. 편하지만 색과 글자를 고르기 어렵고, 결과를 어떻게 꺼내는지 배우지 못합니다. 이 장에서는 직접 그립니다.