발생의 이유를 설명하라.
→
import os
from ultralytics import YOLO
import cv2
from tqdm import tqdm
# 1. 학습된 모델 불러오기
model = YOLO("/home/wkdwnsals0413/runs/train/yolo11n_custom2/weights/best.pt")
# 2. 데이터셋 경로
test_images_dir = "/home/wkdwnsals0413/dataset/images/test"
test_labels_dir = "/home/wkdwnsals0413/dataset/labels/test"
save_dir = "misclassified_results"
os.makedirs(save_dir, exist_ok=True)
# 3. 클래스 이름
class_names = model.names
# 4. 라벨(.txt) 읽기
def read_labels(label_path):
boxes = []
with open(label_path, "r") as f:
for line in f.readlines():
cls, x, y, w, h = map(float, line.strip().split())
boxes.append([int(cls), x, y, w, h])
return boxes
# 5. IoU 계산 함수
def compute_iou(box1, box2):
"""
box = [x, y, w, h] (normalized xywh)
"""
x1_min = box1[0] - box1[2] / 2
y1_min = box1[1] - box1[3] / 2
x1_max = box1[0] + box1[2] / 2
y1_max = box1[1] + box1[3] / 2
x2_min = box2[0] - box2[2] / 2
y2_min = box2[1] - box2[3] / 2
x2_max = box2[0] + box2[2] / 2
y2_max = box2[1] + box2[3] / 2
inter_xmin = max(x1_min, x2_min)
inter_ymin = max(y1_min, y2_min)
inter_xmax = min(x1_max, x2_max)
inter_ymax = min(y1_max, y2_max)
inter_area = max(0, inter_xmax - inter_xmin) * max(0, inter_ymax - inter_ymin)
box1_area = (x1_max - x1_min) * (y1_max - y1_min)
box2_area = (x2_max - x2_min) * (y2_max - y2_min)
union_area = box1_area + box2_area - inter_area
return inter_area / union_area if union_area > 0 else 0
# 6. Bounding Box 그리기
def draw_boxes(img, boxes, color, label_text):
h, w = img.shape[:2]
for box in boxes:
cls, x, y, bw, bh = box
x1 = int((x - bw/2) * w)
y1 = int((y - bh/2) * h)
x2 = int((x + bw/2) * w)
y2 = int((y + bh/2) * h)
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
cv2.putText(img, f"{label_text}: {class_names[cls]}",
(x1, max(20, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX,
0.6, color, 2)
return img
# 7. 평가 및 저장
misclassified_count = 0
for img_name in tqdm(os.listdir(test_images_dir)):
if not img_name.endswith((".jpg", ".png", ".jpeg")):
continue
img_path = os.path.join(test_images_dir, img_name)
label_path = os.path.join(test_labels_dir, img_name.rsplit(".", 1)[0] + ".txt")
if not os.path.exists(label_path):
continue
# 실제 라벨
true_boxes = read_labels(label_path)
# 예측
results = model(img_path, conf=0.25, iou=0.45, verbose=False)[0]
pred_boxes = []
for box in results.boxes:
cls = int(box.cls.item())
xywh = box.xywhn.cpu().numpy()[0] # normalized [x,y,w,h]
pred_boxes.append([cls, *xywh])
# mAP 방식 검증 (IoU threshold = 0.3)
misclassified = False
for tb in true_boxes:
t_cls, tx, ty, tw, th = tb
best_iou, best_idx = 0, -1
for idx, pb in enumerate(pred_boxes):
p_cls, px, py, pw, ph = pb
iou = compute_iou([tx, ty, tw, th], [px, py, pw, ph])
if iou > best_iou:
best_iou, best_idx = iou, idx
if best_iou >= 0.30: # IoU 조건 충족
p_cls = pred_boxes[best_idx][0]
if p_cls != t_cls: # 클래스 불일치
misclassified = True
else: # 매칭되는 박스 없음
misclassified = True
# 불일치 데이터만 저장
if misclassified:
img = cv2.imread(img_path)
img = draw_boxes(img, true_boxes, (255, 0, 0), "GT") # 파랑
img = draw_boxes(img, pred_boxes, (0, 0, 255), "Pred") # 빨강
cv2.imwrite(os.path.join(save_dir, img_name), img)
misclassified_count += 1
print(f"[완료] IoU=0.3 기준 잘못 분류된 이미지들이 '{save_dir}'에 저장되었습니다.")
print(f"총 잘못 분류된 파일 개수: {misclassified_count}")
100%|██████████| 8549/8549 [01:07<00:00, 125.80it/s]
[완료] IoU=0.3 기준 잘못 분류된 이미지들이 'misclassified_results'에 저장되었습니다.
총 잘못 분류된 파일 개수: 492