#はじめに
Unityでマルチプラットフォーム対応でディープラーニングの推論をしたいときに便利なアセットにOpenCV for Unityがあります。Deep Learning with OpenCVによると、かなり最適化されていてCPUの推論処理でもそこそこの速度がでているようです。
しかし、最近になってOpenCVのリポジトリにYashasSamagaさんのコミットがマージされました。このコミットによってDnnモジュールの推論処理を**NVIDIA社製のGPUを利用するしくみ(CUDA)**を使用してより高速に行うことができるようになりました。
この記事ではCUDAを有効にしたOpenCVライブラリを自前でビルドしてOpenCV for Unityで利用すると、はたしてDNNモジュールの推論処理がどのくらい高速化されるのかを比較してみたいと思います。
#テスト環境
- OpenCV 4.2.0
- Unity 2017.5f1
- OpenCV for Unity 2.3.8
- Cmake 3.15.5
- Visual Studio 2017
- OS : Windows10
- CPU : Intel Core i7-7700K 4.2GHz
- GPU : NVIDIA GeForce GTX 1050
#Windows用OpenCVダイナミックライブラリをCUDAバックエンドを有効にしてビルドする手順
OpenCV for Unityに同梱されているReadMe.pdfのHow to use OpenCV Dynamic Link Library with customized build settingsの項目を参考にして、CUDAを有効にしたOpenCVのダイナミックライブラリをビルドしていきます。
ディフォルトでAssets/OpenCVForUnity/Plugins/Windows/x86_64/フォルダに入っているopencvforunity.dll(おそらくC#側のコードとC++側のOpenCVライブラリを繋ぐ役割のファイルだと思います。)はOpenCVライブラリがスタティックライブラリとして埋め込まれて一つのファイルとしてビルドされています。
この記事では自分でビルドしたOpenCVのダイナミックライブラリをAssets/OpenCVForUnity/Extra/dll_version/Windows/x86_64/フォルダに入っているOpenCVライブラリが埋め込まれていないopencvforunity.dllから呼び出す形で利用します。
##CUDA関連のセットアップ
この記事を参考に以下のCUDA関連のプログラムをインストールし、環境変数にパスを設定したりしてセットアップを完了してください。
- NVIDIA グラフィックスボード・ドライバ
- NVIDIA CUDA ツールキット
- NVIDIA cuDNN
- (任意)Intel MKL
##OpenCVをビルド
-
OpenCVのリポジトリからOpenCV for Unityのバージョンに対応したコミット時点のコードをダウンロードする。コミットへのリンクはReadMe.pdfに書いてあります。例えばOpenCV for Unity2.3.8の場合は**OpenCV 4.2.0 repository ( git: opencv , opencv-contrib )**です。
-
cmakeでビルドプロジェクトを出力する。
-
以下の画像のようにビルド設定を変更する。CUDA_ARCH_BINはこのページを参考に適切な数値を指定してください。MKL関連は任意。
-
Generateボタンを押して、VidualStudioのプロジェクトを出力する。
-
VisualStudioのプロジェクトを開いて、ビルドする。
##ビルドしたダイナミックライブラリをUnityプロジェクトにコピー
-
空プロジェクトにOpenCV for UnityをImportする。
-
ディフォルトのWindows 64bit用のライブラリファイル(opencvforunity.dll)が入っているAssets/OpenCVForUnity/Plugins/Windows/x86_64/フォルダを削除する。
-
Extra.packageをインポートして、Assets/OpenCVForUnity/Extra/dll_version/Windows/x86_64/フォルダをAssets/OpenCVForUnity/Plugins/Windows/フォルダ以下に移動させる。
-
先ほどビルドしたOpenCVのライブラリをAssets/OpenCVForUnity/Plugins/Windows/フォルダ以下にコビーする。
#速度比較
実際にコードでCUDAバックエンドを有効にして推論処理をするためには、以下のように設定を変更するだけでOKです。
net = Dnn.readNetFromCaffe(prototxt_filepath, caffemodel_filepath);
//net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
//net.setPreferableTarget(Dnn.DNN_TARGET_CPU);
net.setPreferableBackend(Dnn.DNN_BACKEND_CUDA);
net.setPreferableTarget(Dnn.DNN_TARGET_CUDA);
テストしたソースコードはgithubにアップしてあります。元々同梱されていたExampleをVideoCaptureクラスを使用して好きな動画を読み込めるように改変しました。WebCamTextureバージョンも入ってます。もし、シーンを実行したときにdllの読み込みエラーが発生した場合は、dllファイルの依存関係を調べるツールを使用してきちんとパスが通っているかなどを確認してみて下さい。
追記
下記のテストコードを参考に推論処理部分のみを計測した結果を追加しました。最初の3回の結果を捨てて、そのあとの10回の平均値を出しました。FpsMonitor.csで表示されるFPSではなく純粋な推論処理にかかる時間で比較したいと思います。
Test Code: https://gist.github.com/YashasSamaga/71157cf0c3768c497e5e70fb95435596
TickMeter tm = new TickMeter();
tm.start();
net.setInput(blob);
net.forward(outs, outBlobNames);
tm.stop();
Debug.Log ("Inference time, ms: " + tm.getTimeMilli ());
では、推論処理がどのくらい速くなったかを見てきたいと思います。
##Yolo v3
ソースコード
CPUバックエンド 33.6401ms
CUDAバックエンド 7.4248ms
約4.5倍くらい速くなりました。
##LibFaceDetection
ソースコード
CPUバックエンド 7.1025ms
CUDAバックエンド 4.1526ms
CPUバックエンドでも十分な速度が出ていますが、CUDAバックエンドにしてもなぜかFPSはまったく変わらず。CUDAバックエンドで現在対応しているLayerも関係しているのかもしれません。
約1.7倍くらい速くなりました。
##Mask-RCNN
ソースコード
CPUバックエンド 1311.3804ms
CUDAバックエンド 172.2432ms
|
約7.6倍くらい速くなりました。
##OpenPose
ソースコード
CUDAバックエンド 152.0677ms
約5.2倍くらい速くなりました。
##ThreeDPoseUnityForiOS
ついでに@yukihiko_aさんが公開してくださっているThreeDPoseUnityForiOSも試してみました。
約5.0倍くらい遅くなりました。CPUバックエンドでは20FPSも出ているのに、CUDAバックエンドにするとなぜか4.5FPSくらいまで下がってしまいました。謎です。モデルによっては必ずしもCUDAにすれば速くなるということではないようです。
#まとめ
ということで、CUDAバックエンドの力を使うと推論速度は多くのモデルで向上するようです。中には7倍以上も向上するモデルもありました。今回の結果をこのページのベンチマークを比べてみると、OpenPoseモデルはだいたい近い結果1になっているのでこの記事で紹介したやり方は間違ってはいないようです。たぶん。
また、DnnモジュールのCUDAバックエンドに関するコードは続々と新しいコミットがマージされているので、今後さらなる推論速度向上が期待できそうです。
#おまけ
- この記事で紹介したように、OpenCV for Unityには自分でビルド設定をカスタマイズしたダイナミックライブラリを使用できる方法があります。**VideoCaptureクラスのGStreamerバックエンドやFeature2DモジュールのNON_FREEアルゴリズム(SIFT、SURF)**を有効にしてビルドすれば、ディフォルトビルドでは無効になっているいろいろな機能を使用することができるようになると思います。
- 今回いろいろ試しているなかで、Unityのバージョンを2019.3.0f3にしたところ、シーン実行時にdllファイル群が読み込めないというエラーメッセージがコンソールに表示され、なぜかdllファイルの読み込みに失敗してしまいます。Unityのバージョンアップに伴いライブラリのロード方法が変わったのかもしれません。**どなたか解決方法を知っている方がいたら教えてください。**
コンソールに表示されたエラーメッセージ
Plugins: Failed to load 'Assets/OpenCVForUnity/Plugins/Windows/x86_64/opencvforunity.dll' with error '指定されたプロシージャが見つかりません。
'.
DllNotFoundException: opencvforunity-
NVIDIA GTX 1050でOpenPoseモデルが160.561msとあるので、計算するとだいたい6.25FPSとなります。ざっくりですみません。 ↩
-