# Ultralytics의 YOLO 모델
## 소개
You Only Look Once (YOLO)는 일련의 CNN 기반 실시간 물체 탐지 모델입니다. 첫 버전은 2015년에 발표되었고, 최신 버전(YOLOv12)는 2025년 초에 발표되었습니다. 지난 10년동안 이 모델은 물체 탐지, 인스턴스 분할, 포즈 감지, 얼굴 탐지 등에 널리 사용되었습니다.

근래의 YOLO모델은 [Ultralytics](https://www.ultralytics.com/)의 Python 패키지로 배포되고 있습니다. 이 튜토리얼에서는 Ultralytics가 공식 발표한 물체 탐지용 모델 중 하나인 YOLOv8n을 사용합니다.
## 모델 준비
모델을 다운로드하기에 앞서 Ultralytics의 패키지를 다음과 같이 설치합니다.

```bash
pip install ultralytics
```

그 후 다음의 CLI 명령어를 사용하여 모델을 다운로드합니다. 이 튜토리얼에서 우리는 YOLO모델의 기본 입력값 크기인 (640, 640)을 사용하기로 했기에 아래 명령어를 제외한 추가적인 입력이 필요하지 않습니다.

```bash
yolo export model=yolov8n.pt format=onnx
```

![다운로드 된 YOLO 모델](../res/image/yolo_structure.png "다운로드 된 YOLO 모델")

## Calibration Dataset 준비
물체 탐지, 인스턴스 분할을 위한 모델들은 보통 [COCO](https://cocodataset.org/#home) 데이터셋을 사용하여 학습이 이루어집니다. 따라서 이 경우에도 학습에 사용된 이미지들과 유사한 분포를 가진 데이터셋을 Calibration Dataset으로 사용하는 것이 권장됩니다. 이 튜토리얼을 위해 모빌린트의 엔지니어들은 COCO의 학습용 데이터셋으로부터 100개의 이미지를 추출해서 제공하고 있습니다.

YOLO모델의 경우 버전에 따라 전처리과정이 상이할 수 있습니다. 이러한 이유로 표준화된 전처리 연산들을 조합하여 Calibration Dataset을 만드는 방식을 사용하기 곤란한 경우가 대분입니다. 따라서 이 섹션에서는 사용자 정의 함수를 사용하는 예시만을 제공합니다. Ultralytics의 경우 전처리에 사용한 코드를  [GitHub](https://github.com/ultralytics/ultralytics)에 공개하고 있기에, 해당 코드를 바탕으로 다음과 같이 사용하실 수 있습니다.

```python
import cv2
import numpy as np
from qubee.calibration import make_calib_man

img_size = [640, 640] # or [1280, 1280]

def preprocess_yolo(img_path: str):
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    h0, w0 = img.shape[:2]  # original hw
    r = min(img_size[0] / h0, img_size[1] / w0)  # ratio
    new_unpad = int(round(w0 * r)), int(round(h0 * r))
    dh, dw = (
        img_size[0] - new_unpad[1],
        img_size[1] - new_unpad[0],
    )  # wh padding

    dw /= 2  # divide padding into 2 sides
    dh /= 2
    if not (img.shape[1], img.shape[0]) == new_unpad:
        img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    img = cv2.copyMakeBorder(
        img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(114, 114, 114)
    )  # add border
    img = (img / 255).astype(np.float32)

    return img


make_calib_man(
    pre_ftn=preprocess_yolo,  # callable function to pre-process the calibration data
    data_dir="/workspace/calibration/train2014_calib", # path to folder of original calibration data files such as images
    save_dir="/workspace/calibration/", # path to folder to save pre-processed calibration data files
    save_name="yolov8n_cali",  # tag for the generated calibration dataset
    max_size=100,  # Maximum number of data to use for calibration
)
```

## 모델 컴파일
변환된 모델과 Calibration Dataset이 준비되었다면 이제 모델을 컴파일할 수 있습니다.

```python
""" Compile YOLO """ 
from qubee import mxq_compile
onnx_model_path = "/workspace/yolov8n.onnx"
calib_data_path = "/workspace/calibration/yolov8n_cali"

mxq_compile(
    model=onnx_model_path,
    calib_data_path=calib_data_path,
    quantization_method=1, # per channel quantization
    quantization_mode=2, # max percentile quantization
    percentile=0.999,
    quantization_output=0, # per channel quantization for the output layer
    save_path="yolov8n.mxq",
    backend="onnx" 
)
```

한 가지 유의할 점으로는, YOLO 모델에 포함된 일부 후처리 연산들은 NPU상에서 지원되지 않을 수 있거나, 연산의 정확도가 낮을 수 있습니다. 이 경우 qubee 컴파일러는 별도의 설정이 없는 한, 지원되지 않는 부분을 잘라내서 컴파일하게 됩니다. 따라서 컴파일 결과에 표시되는 연산 지원 범위를 확인하고, 지원되지 않는 연산은 별도의 후처리 함수를 외부에 연결하여 작업을 수행할 수 있도록 해야합니다. 지원되지 않는 연산에 대한 후처리 연결 방식은 [Mobilint Model Zoo](https://github.com/mobilint/mblt-model-zoo)를 통해 배포되는 모델들의 예시를 참고하여 해결하실 수 있습니다.

![YOLO 컴파일 결과](../res/image/yolo_truncation2.png "YOLO 컴파일 결과")
