OpenCV 강좌, YOLO, SSD 모델을 OpenCV로 로드하여 활용하기

이번 강좌에서는 OpenCV를 사용하여 YOLO(You Only Look Once)와 SSD(Single Shot MultiBox Detector) 모델을 로드하고 활용하는 방법에 대해 자세히 알아보겠습니다. 이 두 가지 모델은 객체 탐지 분야에서 널리 사용되며, 각각의 특성과 장단점이 있습니다. 또한, OpenCV를 사용한 다양한 이미지 및 비디오 처리 방법을 함께 소개할 것입니다.

1. OpenCV란 무엇인가?

OpenCV(Open Source Computer Vision Library)는 이미지 및 비디오 처리를 위한 오픈 소스 라이브러리입니다. 강력한 기능을 제공하여 컴퓨터 비전과 머신 러닝 분야에서 많이 사용됩니다. 다양한 프로그래밍 언어에서 사용할 수 있으며, 특히 Python과 C++에서 많이 활용됩니다. 고객 맞춤형 어플리케이션을 쉽게 개발할 수 있도록 도와줍니다.

2. YOLO란?

YOLO(You Only Look Once)는 객체 탐지를 위한 딥 러닝 기반의 알고리즘입니다. YOLO의 가장 큰 장점은 동영상이나 실시간 이미지를 처리할 때 매우 빠른 속도로 높은 정확도의 객체 탐지를 가능하게 한다는 것입니다. YOLO는 이미지 전체를 한 번만 보고, 여러 객체의 위치와 계층을 동시에 예측합니다.

2.1 YOLO 모델의 동작 원리

YOLO는 입력 이미지를 SxS의 그리드로 나눈 다음 각 그리드가 탐지하는 객체의 바운딩 박스와 클래스 확률을 예측하는 방식으로 동작합니다. 각 그리드셀은 고유한 바운딩 박스와 객체 클래스를 예측할 수 있습니다. 이러한 구조는 CNN(Convolutional Neural Network)을 기반으로 하며, 신속한 처리 속도를 자랑합니다.

2.2 YOLO 버전

현재 YOLO는 여러 가지 버전이 존재합니다. 그 중에서도 YOLOv5는 가장 유연하고 발전된 형태로, PyTorch로 작성되어 있어 쉽게 사용하고 실험할 수 있습니다.

3. SSD란?

SSD(Single Shot MultiBox Detector)는 빠른 속도와 높은 정확도를 가진 또 다른 객체 탐지 알고리즘입니다. SSD는 다양한 크기의 바운딩 박스를 사용하여 다양한 크기의 객체를 탐지합니다. 이 방식은 여러 개의 스케일을 적용하여 진행되며, 입력 이미지를 서로 다른 스케일의 멀티 레이블로 처리합니다.

3.1 SSD 모델의 동작 원리

SSD는 CNN 기반의 구조로 다양한 크기의 프레임워크(Feature Map)에서 여러 비율의 바운딩 박스를 생성하고, 각 바운딩 박스에 대해 클래스와 바운딩 박스를 예측합니다. 내부에 있는 여러 계층을 통해 다양한 크기의 객체를 탐지할 수 있습니다.

4. OpenCV에서 YOLO와 SSD 모델 사용하기

4.1 YOLO 모델 로드하기

YOLO 모델을 로드하기 위해 먼저 필요 라이브러리를 설치하고, 모델과 클래스 파일을 다운로드해야 합니다. 다음은 YOLOv3 모델을 사용하기 위한 단계입니다.

4.1.1 필요한 라이브러리 설치

pip install opencv-python numpy

4.1.2 YOLOv3 모델 파일 다운로드

github 혹은 YOLO 공식 사이트에서 다음 파일을 다운로드합니다:

  • yolov3.weights
  • yolov3.cfg
  • coco.names (클래스 이름 파일)

4.1.3 YOLOv3 모델 로드 코드

import cv2
import numpy as np

# YOLOv3 모델과 클래스 파일의 경로 설정
weights_path = "yolov3.weights"
config_path = "yolov3.cfg"
class_names_path = "coco.names"

# 클래스 이름 로드
with open(class_names_path, 'r') as f:
    classes = f.read().strip().split('\n')

# YOLO 모델 로드
net = cv2.dnn.readNet(weights_path, config_path)

4.1.4 이미지에서 객체 탐지하기

def detect_objects(image):
    height, width = image.shape[:2]
    
    # 이미지를 YOLO에 적합한 형식으로 변환
    blob = cv2.dnn.blobFromImage(image, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
    net.setInput(blob)

    # 출력 레이어 이름 가져오기
    layer_names = net.getLayerNames()
    output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]

    # 탐지된 객체
    detections = net.forward(output_layers)

    boxes, confidences, class_ids = [], [], []
    
    for detection in detections:
        for obj in detection:
            scores = obj[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > 0.5:  # 신뢰도 필터링
                center_x = int(obj[0] * width)
                center_y = int(obj[1] * height)
                w = int(obj[2] * width)
                h = int(obj[3] * height)

                # 바운딩 박스 좌표
                x = int(center_x - w / 2)
                y = int(center_y - h / 2)

                boxes.append([x, y, w, h])
                confidences.append(float(confidence))
                class_ids.append(class_id)

    # 비최대 억제
    indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

    for i in indices:
        i = i[0]
        box = boxes[i]
        x, y, w, h = box
        label = str(classes[class_ids[i]])
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.putText(image, label, (x, y + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

    return image

# 테스트 이미지에서 객체 탐지
image = cv2.imread('test.jpg')
output_image = detect_objects(image)
cv2.imshow('Image', output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.2 SSD 모델 로드하기

SSD 모델도 마찬가지로 OpenCV에서 사용할 수 있습니다. SSD 예제 코드는 다음과 같습니다.

4.2.1 SSD 모델 파일 다운로드

다음 파일을 다운로드합니다:

  • SSD_Mobilenet_v2.caffemodel
  • SSD_Mobilenet_v2.prototxt

4.2.2 SSD 모델 로드 코드

# SSD 모델 로드
ssd_net = cv2.dnn.readNetFromCaffe('SSD_Mobilenet_v2.prototxt', 'SSD_Mobilenet_v2.caffemodel')

4.2.3 이미지에서 객체 탐지하기

def detect_objects_ssd(image):
    height, width = image.shape[:2]
    
    # 이미지를 SSD에 적합한 형식으로 변환
    blob = cv2.dnn.blobFromImage(image, 0.007843, (300, 300), 127.5)
    ssd_net.setInput(blob)

    # 탐지된 객체
    detections = ssd_net.forward()

    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence > 0.5:  # 신뢰도 필터링
            class_id = int(detections[0, 0, i, 1])
            box = detections[0, 0, i, 3:7] * np.array([width, height, width, height])
            (x, y, x1, y1) = box.astype("int")
            label = f'{class_names[class_id]}: {confidence:.2f}'

            cv2.rectangle(image, (x, y), (x1, y1), (0, 255, 0), 2)
            cv2.putText(image, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    return image

# 테스트 이미지에서 객체 탐지
image = cv2.imread('test.jpg')
output_image = detect_objects_ssd(image)
cv2.imshow('Image', output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

5. 결론

이번 강좌에서는 OpenCV를 사용하여 YOLO 및 SSD 모델을 로드하고 객체 탐지를 수행하는 방법을 알아보았습니다. 두 모델은 각각 장단점을 가지고 있으며, 사용자의 필요에 따라 선택할 수 있습니다. YOLO는 속도가 빠르고, SSD는 여러 크기와 비율의 객체를 탐지하는 데 유리합니다. 앞으로도 OpenCV와 그와 관련된 다양한 기술을 활용하여 더 많은 프로젝트를 진행해 나가시길 바랍니다.

6. 참고 자료