# YOLOv4 -> TensorRT
> [name=謝朋諺(Adam Hsieh)]
> [time=Mon, Oct 19, 2020 09:03 AM]
###### tags: `Document`
---
> [TOC]
## Outline
主要介紹如何使用 Nvidai Jetson Xavier NX 將 YOLOv4 (Darknet) 轉到 TensorRT 的方法。
## Transfer model to TensorRT
首先本文都是以這篇 [tensorrt_demos](https://github.com/jkjung-avt/tensorrt_demos) 的 Github 為基礎將 YOLOv4 轉到 TensorRT 的環境上做實作。
根據這段的 [Demo#5: YOLOv4](https://github.com/jkjung-avt/tensorrt_demos#demo-5-yolov4) 的步驟去做安裝基本上問題不大,但根據幾個步驟有些東西需要去注意,以下會根據他的條列來做補充:
1. 第一個 Pycuda 基本上會內建安裝,如果沒有的話請遵照他的指示來執行:
```shell
$ cd ${HOME}/project/tensorrt_demos/ssd
$ ./install_pycuda.sh
```
2. 根據第二點由於在 TensorRT 部分還是要先轉到 onnx 才能執行,因此要先安裝 onnx,而且因為後面的 yolo_layer 會需要吃他的編譯函式庫 **libyolo_layer.so**,因此要裝他指定的版本 **1.4.1**,否則會編譯不過。
```shell
$ sudo pip3 install onnx==1.4.1
```
3. 這個步驟是由於有太多的客製化 Layer 是 onnx 沒有的,因此因此必須先編譯 Yolov4 的一些 Layer,以產生 **libyolo_layer.so**。
```shell
$ cd ${HOME}/project/tensorrt_demos/plugins
$ make
```
4. 到了這步你就可以直接下載你的模型到這邊,先從 Darknet model 轉到 onnx model,再從 onnx model 轉到 TensorRT model,請記得先備好 YOLOv4 weights (Ex: yolov4-512.weights) 跟 YOLOv4 config 檔案 (Ex: yolov4-512.cfg)。
:::danger
:fire: 另外在這邊特別強調一下,如果想要用這個 github,盡量不要自己轉 onnx 才丟到這邊轉 TensorRT,他會有不支援 Layer 的問題,還是強烈建議一切都在這邊轉完執行。
:::
:::danger
:fire: 請記得在 onnx_to_tensorrt.py 這份檔案中,在最前面要先 import cv2,否則後面會有問題。
:::
```shell
$ cd ${HOME}/project/tensorrt_demos/yolo
$ ./download_yolo.sh # 不一定要,只是下載 model 的功能
$ python3 yolo_to_onnx.py -m yolov4-416 -c 1
$ python3 onnx_to_tensorrt.py -m yolov4-416 -c 1
```
:::info
:pencil: 請記得要根據你 model 的 Input Size 給予檔名,例如你是放 $512\times 512$ 的圖片,並且是 YOLOv4 的 model,則檔名為 **yolov4-512.weights**;如果是放 $608\times 256$ 的圖片,且是 YOLOv4-tiny 的 model,則名字就為 **yolov4-tiny-608x256.weights**
:pencil: 而我們的模型只有一個行人類別,因此參數 ==-c 1== 一個類別即可。
:::
以下有 yolo_to_onnx.py 的參數解說
```
usage: yolo_to_onnx.py [-h] [-c CATEGORY_NUM] -m MODEL
optional arguments:
-h, --help show this help message and exit
-c CATEGORY_NUM, --category_num CATEGORY_NUM
number of object categories [80]
-m MODEL, --model MODEL
[yolov3|yolov3-tiny|yolov3-spp|yolov4|yolov4-tiny]-[{d
imension}], where dimension could be a single number
(e.g. 288, 416, 608) or WxH (e.g. 416x256)
```
轉出成功會出現類似以下畫面,就代表成功完成囉:
```
Checking ONNX model...
Saving ONNX file...
Done.
```

以下為 onnx_to_tensorrt.py 的參數解說
```
usage: onnx_to_tensorrt.py [-h] [-v] [-c CATEGORY_NUM] -m MODEL [--int8]
[--dla_core DLA_CORE]
optional arguments:
-h, --help show this help message and exit
-v, --verbose enable verbose output (for debugging)
-c CATEGORY_NUM, --category_num CATEGORY_NUM
number of object categories [80]
-m MODEL, --model MODEL
[yolov3|yolov3-tiny|yolov3-spp|yolov4|yolov4-tiny]-[{d
imension}], where dimension could be a single number
(e.g. 288, 416, 608) or WxH (e.g. 416x256)
--int8 build INT8 TensorRT engine
--dla_core DLA_CORE id of DLA core for inference (0 ~ N-1)
```
轉出成功會出現類似以下畫面,就代表成功完成囉:
```
Coompleted creating engine.
Serialized the TensorRT engine to file: yolov4-tiny-416.trt
```

基本上到了這一步就可以轉出副檔名為 onnx 以及 trt 的模型囉。
## Transfer model to TensorRT INT8
根據 [Demo #6: Using INT8 and DLA core](https://github.com/jkjung-avt/tensorrt_demos#demo-6-using-int8-and-dla-core) 可以將 model 執行 INT8 的加速。
1. 首先為了要將模型轉為 INT8,必須準備一些影像做 "Calibration",在 Nvidia 官方文件是建議500 張影像,本文的 Github 是使用 1000 張 "COCO val2017" 的影像,可以使用 Github 付的程式:
```shsell
$ cd ${HOME}/project/tensorrt_demos/yolo
$ mkdir calib_images
### randomly pick and copy over 1,000 images from "val207"
$ for jpg in $(ls -1 ${HOME}/data/coco/images/val2017/*.jpg | sort -R | head -1000); do \
cp ${HOME}/data/coco/images/val2017/${jpg} calib_images/; \
done
```
上面這段程式就只是將影像複製到 ```tensorrt_demos/yolo/calib_images``` 資料夾下而已,你也可以自己手動複製過去。
2. 由於前面你已經有 TensorRT 的模型,為了做區別這邊還是建議用捷徑方式做另一個專做 INT8 模型的 onnx 跟 cfg 檔:
```shell
$ ln -s yolov3-608.cfg yolov3-int8-608.cfg
$ ln -s yolov3-608.onnx yolov3-int8-608.onnx
$ python3 onnx_to_tensorrt.py -v --int8 -m yolov3-int8-608
```
與前面指令相同,基本上只是多加一個 ```--int8``` 這個選項而已,跑完之後就會產生 INT8 模型,在這邊需要等待的時間會比前面久很多,因為需要對每張影像做校正。
## Excute TensorRT model
可以直接用 Github 的指令來執行,針對單張影像的 Inference。
```shell
$ cd ${HOME}/project/tensorrt_demos
$ python3 trt_yolo.py --image ${HOME}/Pictures/dog.jpg -m yolov3-int8-608
```
若是像我一樣只是想要 import 函式庫來用的話,可以直接在目錄下寫:
```python
from utils.yolo_with_plugins import TrtYOLO # 引用的函式
# TrtYOLO(model 路徑, (輸入影像的高, 輸入影像的寬), 類別數量)
trt_yolo = TrtYOLO(model_path, (height, width), category_num) # 類別初始化
# 單張影像所有預測框的座標, 信心分數, 物件類別 = trt_yolo.detect(輸入影像, 信心分數的閾值)
boxes, confs, clss = trt_yolo.detect(image, conf_th)
```
如果想要對 Jetson 卡片用最大資源去跑,可以打下指令:
```shell
$ sudo jetson_clocks
```