OPENCVでONNXを利用できるようにONNX RUNTIMEをビルドする

OPENCVでONNXが利用できるように、ONNX RUNTIMEをビルドしました。
ONNX RUNTIMEのEPの、CUDA、DirectMLもビルドしましたので、ご紹介します。

ONNX RUNTIME、OPENCVをビルドしたのは、C++で利用できるようするためです。

なお、Windows VisualStudioでONNX RUNTIMEを利用したい場合には、NuGetで導入することになります。
詳しくは[コチラ](https://qiita.com/calcMCalcm/items/00fe171bddd9ded86019#%EF%BC%96visual-studioc%E3%81%A7onnxruntime%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84%E6%99%82)を参照ください。

## 環境
Windows11
Python 3.11（Windowsストア版）
ONNX RUNTIME 1.21.0
CUDA 12.4
cuDNN 9.1
OPENCV 4.10
VisualStudio 2022 Version 17.12.2
CMAKE 3.31.1

## １．ONNX RUNTIME（CPU）をビルドする。

https://onnxruntime.ai/docs/build/inferencing.html

基本的に公式onnxruntimeのチュートリアル通り実施しています。
### １．gitで落としてくる。
```cmd
git clone --recursive https://github.com/Microsoft/onnxruntime.git
cd onnxruntime
```
### ２．cmakeをpipで入れる
CMAKEはpythonでpipするような手順になっています。
cmakeは既にセットアップ済みでしたが、後の手順がバッチファイルからpythonで実行されるようなので念のため手順通りpipで入手しています。
```cmd
  python -m pip install cmake
  where cmake
```
### ３．ビルド
バッチファイルのオプションをつけて実行します。
ONNX RUNTIME CPUをビルドするだけであれば公式に記載の通りで全く問題ないと思います。
```
.\build.bat --config RelWithDebInfo --build_shared_lib --parallel --compile_no_warning_as_error --skip_submodule_sync
```

以下の記事にビルドオプションの中でも有用そうなものについて紹介されています。

https://qiita.com/mgmk2/items/c802598b9dbe7a06a425#%E3%83%93%E3%83%AB%E3%83%89%E3%81%AE%E5%AE%9F%E8%A1%8C

### ４．インストール
ビルドが終わって・・・で、どうしたらいいの？OPENCVのcmakeのビルドでONNX YESにならないけど？人間が欲しいファイルをピックアップする感じ？？
とうろたえましたが、githubにはインストールが書いてありました。

https://github.com/microsoft/onnxruntime-inference-examples/blob/main/c_cxx/README.md#option-2-build-from-source

以下を実行することで、`C:\Program Files\onnxruntime`にいい感じでコピーしてくれます。
```
cmake --install .\build\Windows\RelWithDebInfo --config RelWithDebInfo
```

## ２．ONNX RUNTIME（CUDA）をビルドする
### １．ビルド
こちらも基本的にonnxruntime公式に記載されている通りです。

https://onnxruntime.ai/docs/build/eps.html#cuda

```
.\build.bat --config Release --build_shared_lib --parallel --use_cuda --cudnn_home <cudnn home path> --cuda_home <cuda home path>
```
cuDNN、cudaのパスを記載するようになっています。
`--parallel`も利用可能です。ただ、かなりメモリを食います。利用しましたが途中のメモリ使用量は32GBかそれ以上だったかと思います。
CUDAのビルドはかなり時間がかかりました。1時間くらい？もっと？？
そのためオプション`--parallel`を利用したいところですが、メモリが少ないPCではメモリ不足でエラーになるかもしれません。

他のオプションとして、アーキテクチャの指定ができるので、もし事前に対象とするGPUの想定があれば記載してもいいかもしれません
`--cmake_extra_defines CMAKE_CUDA_ARCHITECTURES=80;86;89`
他にもたくさんオプションがあるので、一度CUDAの説明ページを読んでいただくのがいいと思います。

CUDAでは`--config RelWithDebInfo`は使えませんでした。Releaseとdebugしかないのかもしれません。
わたしは`--config Release`でビルドしました。

### ２．インストール
基本的に先ほどと同じです。
わたしはReleaseだけインストールしました。
```
cmake --install .\build\Windows\Release --config Release
```
インストールすると、共通のファイルは上書きされます。
~~全部を確認していませんが、たぶん同じ内容そうなので、そのままでいいんじゃないかなと思っています。
もし違ったら記事修正します。違うよって教えてください。~~
configが異なる場合は分けておく方が無難です

## ３．ONNX RUNTIME（DirectML）をビルドする
### １．ビルド
こちらも基本的にonnxruntime公式に記載されている通りです。

https://onnxruntime.ai/docs/build/eps.html#directml

```
build.bat --config RelWithDebInfo --build_shared_lib --parallel --use_dml
```
こちらは、`--config RelWithDebInfo`が使えました。また、`--parallel`も利用できます。
ビルドは短いです。15分くらい？

### ２．インストール
```
cmake --install .\build\Windows\RelWithDebInfo --config RelWithDebInfo
```
インストールすると、共通のファイルは上書きされます。
~~全部を確認していませんが、たぶん同じ内容そうなので、そのままでいいんじゃないかなと思っています。
もし違ったら記事修正します。違うよって教えてください。~~
configが異なる場合は分けておく方が無難です

## ４．OPENCVでWith ONNX
GUIのCMAKEでビルドしています。
ONNXのパスの指定をするようになるので、`C:\Program Files\onnxruntime`を指定すると、ちゃんと認識してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/d2b02e2f-b583-80d4-62b9-367de396d9b4.png)

## ５．その他
CUDAはバージョンによってアプリ側から利用できる/利用できないがあって、もうちょっと何とかならないかとは思うのですが、CUDA 11.8、cuDNN 8.9.7が間違いないだろう、と思い、当初こちらで進めようとしていたのですが、CUDA 11.8は、VisualStudio 2022 Version 17.12.2 では、ビルドできないバージョンになってました（バージョンの上限側を過ぎている模様）
強制的にバージョン無視してビルドするオプションもありそうだったのですが、いまいちよくわからなかったので、最近使っていたCUDA 12.4、cuDNN 9.1で、ONNX、OPENCVともにビルドしました。
OPENCVのビルドでは、CMAKEのバージョンが上過ぎるよというワーニングがでますが、こちらは無視しています。

## ６．Visual Studio(C++)でONNXRUNTIMEを利用したい時
NuGetでONNXRUNTIMEを導入してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/97fff9cb-038c-3ca9-408a-7246938fa6d5.png)

OPENCVなどと同様に、外部インクルードや追加の依存ファイルを設定すればビルドしたonnxruntimeが利用できるかと思いましたが、Windows11ではsystem32にonnxruntime.dllが存在し、windows起動時に既に読み込まれています。
そのため、外部インクルードなどを設定しても、dllは読み込み済みのsystem32のものが適用され、
`The given version [21] is not supported, only version 1 to 10 is supported in this build.`
などと表示され、ONNXRUNTIME初期化時に例外がスローされてします。
（21はビルドしプロジェクトに設定したONNXRUNTIME、1-10はsystem32のonnxruntime.dll）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/22979a2e-3af2-e015-687b-3b356b5c866f.png)

EXEと同フォルダのDLLが優先されるとのことですが、ビルドしたONNXRUNTIME.DLLをプロジェクトと同フォルダに置いても既に読み込まれているsystem32のものが優先されてしまうようなので、VisualStudioでの開発では素直にNuGetを利用ください。

https://github.com/microsoft/onnxruntime/issues/11230

onnxruntime公式のGet StartedでもNuGetを使えと指示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/8a7fa40e-8f0f-decc-1419-55abbffec9ac.png)

VisualStudio C++でONNXRUNTIMEのサンプルを動かすのは、こちらの記事が参考になりました。（Mnist画像はgithubにあります）

https://shimons-labo.blog.jp/archives/29653894.html

## まとめ
- ONNX RUNTIMEのビルドをご紹介しました
- 公式に記載されている手順と同じなので、実はこの記事にあまり価値がｎ
- インストールはgithub側にしか書いてなかったので、多少価値はあるかもしれません
（なんで公式ウェブ側には書いてないのか？
- windowsにおいてはONNXRUNTIMEのビルドはopencvなどで利用したい場合のみになるかと思います