LoginSignup
12
14

More than 3 years have passed since last update.

OpenCV for UnityのDnnモジュールをCUDAバックエンドを利用して高速化する

Last updated at Posted at 2020-03-02

はじめに

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.pdfHow 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をビルド

  1. OpenCVのリポジトリからOpenCV for Unityのバージョンに対応したコミット時点のコードをダウンロードする。コミットへのリンクはReadMe.pdfに書いてあります。例えばOpenCV for Unity2.3.8の場合はOpenCV 4.2.0 repository ( git: ​ opencv​ , ​ opencv-contrib​ )です。 
  2. cmakeでビルドプロジェクトを出力する。

    1. VisualStudio 15 2017 x64bitでプロジェクトを生成する。 cmake.PNG
    2. 以下の画像のようにビルド設定を変更する。CUDA_ARCH_BINこのページを参考に適切な数値を指定してください。MKL関連は任意。 cmake2.PNG cmake3.PNG cmake4.PNG
    3. Generateボタンを押して、VidualStudioのプロジェクトを出力する。
  3. VisualStudioのプロジェクトを開いて、ビルドする。

    1. ビルド構成をReleaseに変更する。 visualstudio_release.PNG
    2. INSTALLを右クリックしてビルド開始する。 visualstudio_install.PNG
    3. installフォルダ以下にビルドされたライブラリが出力されます。 copy_dll.PNG

ビルドしたダイナミックライブラリをUnityプロジェクトにコピー

  1. 空プロジェクトにOpenCV for UnityをImportする。
  2. ディフォルトのWindows 64bit用のライブラリファイル(opencvforunity.dll)が入っているAssets/OpenCVForUnity/Plugins/Windows/x86_64/フォルダを削除する。
    delete_static_dll.PNG

  3. Extra.packageをインポートして、Assets/OpenCVForUnity/Extra/dll_version/Windows/x86_64/フォルダAssets/OpenCVForUnity/Plugins/Windows/フォルダ以下に移動させる。
    import_extra0.PNG
    move_dynamic_dll.PNG

  4. 先ほどビルドしたOpenCVのライブラリをAssets/OpenCVForUnity/Plugins/Windows/フォルダ以下にコビーする。
    move_opencv_dll.PNG

  5. ライブラリのImportSettingsを以下の画像のように設定する。すべてのdllファイルを設定してください。
    copy_dll_unity.PNG

速度比較

実際にコードで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
yolov3_cpu_360.gif
CUDAバックエンド 7.4248ms
yolov3_cuda_360.gif
4.5倍くらい速くなりました。

LibFaceDetection

ソースコード

CPUバックエンド 7.1025ms
facedetection_cpu_360.gif
CUDAバックエンド 4.1526ms
facedetection_cuda_360_2.gif
CPUバックエンドでも十分な速度が出ていますが、CUDAバックエンドにしてもなぜかFPSはまったく変わらず。CUDAバックエンドで現在対応しているLayerも関係しているのかもしれません。
1.7倍くらい速くなりました。

Mask-RCNN

ソースコード

CPUバックエンド 1311.3804ms
maskrcnn_cpu_360.gif
CUDAバックエンド 172.2432ms
maskrcnn_cuda_360_2.gif|
7.6倍くらい速くなりました。

OpenPose

ソースコード

CPUバックエンド 785.3027ms
opencv_dnn_cpu_360.gif

CUDAバックエンド 152.0677ms
opencv_dnn_cuda_360.gif
5.2倍くらい速くなりました。

ThreeDPoseUnityForiOS

ついでに@yukihiko_aさんが公開してくださっているThreeDPoseUnityForiOSも試してみました。

CPUバックエンド 42.0681ms
threedpose_cpu_360.gif

CUDAバックエンド 210.3789ms
threedpose_cuda_360.gif

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ファイルの読み込みに失敗してしまいます。
    コンソールに表示されたエラーメッセージ

    Plugins: Failed to load 'Assets/OpenCVForUnity/Plugins/Windows/x86_64/opencvforunity.dll' with error '指定されたプロシージャが見つかりません。
    '.
    DllNotFoundException: opencvforunity

    Unityのバージョンアップに伴いライブラリのロード方法が変わったのかもしれません。どなたか解決方法を知っている方がいたら教えてください。

    1. NVIDIA GTX 1050でOpenPoseモデルが160.561msとあるので、計算するとだいたい6.25FPSとなります。ざっくりですみません。:sweat_smile: 

12
14
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14