WindowsでdlibをCUDA対応にする

# dlib.DLIB_USE_CUDA を有効にする

これはdlib.DLIB_USE_CUDA を有効にしたdlibがimportすらできず、
>File "C:\Users\***\lib\site-packages\dlib\__init__.py", line 19, in <module>
    from _dlib_pybind11 import *
ImportError: DLL load failed while importing _dlib_pybind11: The specified module could not be found.  

のエラーとなる原因が意外なところで失敗していた話。
答えはcuDNNのフォルダ構成にありました。
そのほかちょっと注意点もあるので備忘録もかねて書きます。

## 環境を整える
### 物
CUDAの使えるnVIDIAのグラフィックボード
Windows 10/11のPC

## アプリ(最新でよいと思います）
#### Visual Studio Community 2022
https://visualstudio.microsoft.com/ja/vs/community/

#### Cmake 3.28.1
https://cmake.org/download/

#### CUDA Toolkit 12.3 Update 2 for Windows 
https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html

#### NVIDIA cuDNN v8.9.7.29
https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#install-windows

cudnn-windows-x86_64-8.9.7.29_cuda12-archive.zip
をダウンロードし、展開したファイルの中のフォルダをそのまま
C:\Program Files\NVIDIA\CUDNN\v8.9.7.29\
の下に保存。cuDNNのインストールガイドでは、
>    Copy bin\cudnn*.dll to C:\Program Files\NVIDIA\CUDNN\v8.x\bin.
>    Copy include\cudnn*.h to C:\Program Files\NVIDIA\CUDNN\v8.x\include.
>    Copy lib\cudnn*.lib to C:\Program Files\NVIDIA\CUDNN\v8.x\lib.

と書いているけど、lib\x64\...というフォルダ構成になっているため、 ここでx64フォルダを無視するとimport dlibで失敗する。


#### dlib 19.24.2
https://github.com/davisking/dlib/releases

## dlibをコンパイルする
ダウンロードしたdlibを展開（Program Filesなど権限が必要なフォルダはNG）し、buildフォルダを作成する。
他は下記のやり方でOK
https://www.linkedin.com/pulse/installing-dlib-cuda-support-windows-10-chitransh-mundra

注意点は、cuDNNが見つからないのでパスを指定する必要がある点。
下記の通り設定する。
### ユーザ環境変数の設定
変数：CMAKE_PREFIX_PATH
値：C:\Program Files\NVIDIA\CUDNN\v8.9.7.29

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/26bd61a0-8623-970f-eb97-4129448d6ec0.png)

### システム環境変数の設定
cuDNNのインストールガイドに従うと下記を設定する必要がある
変数：PATH
値：C:\Program Files\NVIDIA\CUDNN\v8.9.7.29\bin

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/299f4286-46ae-a6ea-b4aa-ce545099b884.png)


ここでパスを指定しないと、次のCmakeでエラーが出ます。

失敗例：https://github.com/davisking/dlib/issues/221
>-- *** cuDNN V5.0 OR GREATER NOT FOUND.                                                       ***
-- *** Dlib requires cuDNN V5.0 OR GREATER.  Since cuDNN is not found DLIB WILL NOT USE CUDA. ***
-- *** If you have cuDNN then set CMAKE_PREFIX_PATH to include cuDNN's folder.                ***
-- Disabling CUDA support for dlib.  DLIB WILL NOT USE CUDA


### CMake
1. フォルダを指定
Where is the source code
Whete to build the binaries
のフォルダに、展開したdlib、dlib/buildを指定。

2. [Configure]ボタンを押す

3. DLIB_USE_CUDAにチェックを付ける
チェックボックスではなく「OFF」になっている場合は「1」にすればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/fd376b08-3d63-7b93-1ca2-a705eb6c9398.png)

4. DLIB_USE_CUDA_COMPUTE_CAPABILITIESを設定
Compute capabilitiesは下記で使っているグラフィックボードを選ぶと表示される。
https://developer.nvidia.com/cuda-gpus#compute
初期値が50なので、8.6なら86と指定すればよいらしい。
でも、参考2を見るとdlibの作者はcuDNNを使っているから、そのバージョンが適切なら大丈夫、と言っている。
参考1）https://github.com/davisking/dlib/discussions/2861
参考2）https://github.com/davisking/dlib/issues/1156

 
5. [Configure]　2回目
（1回目は設定が読み込まれ、赤い背景色になるので、上記☑)

6. [Generate]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/d9e6475f-f174-0dfe-d6af-c2283c10a080.png)


### build
powershellでbuildフォルダを開き
```powershell
cmake .
```
すぐに処理が完了する。
これでインストールの準備は完了。

## dlibをインストールする
dlibのフォルダにsetup.pyがあるので、Powershellで

```powershell
python setup.py install
```
を実行。
かなり時間がかかるけれどインストールできるはず。
もし途中でcmakeがないというエラーが出た場合は
```powershell
pip install Cmake
```
で対応可能。
cmakeの時にうまくいったとしても、環境変数を設定後にターミナルを開かないと環境変数が読まれないので失敗する。インストールの途中でよく見ているとcuDNN　not foundというようなメッセージが出てCUDAを使わないバージョンがインストールされてしまう。


## CUDAが有効になっていることのチェック
```python
>>> import dlib
>>> dlib.DLIB_USE_CUDA
True
```

これがFalseだと、cuDNNのパスが見つからずに失敗しているはず。その際は
```terminal
pip uninstall dlib
```
でアンインストールしてやり直し

### 失敗例
そもそもimport dlibができないパターンにはまった。

```python
>>> import dlib
File "C:\Users\***\lib\site-packages\dlib\__init__.py", line 19, in <module>
    from _dlib_pybind11 import *
ImportError: DLL load failed while importing _dlib_pybind11: The specified module could not be found.
```

エラーの出ている `__init__.py`は下記。

```__init__.py
~~
04 def add_lib_to_dll_path(path):
    　　~~~
09      try:
            ~~~
12      except (AttributeError,KeyError,FileNotFoundError):
13        pass
14        
15 if 'ON' == 'ON':
16    add_lib_to_dll_path('C:/Program Files/NVIDIA/CUDNN/v8.9.7.29/lib/cudnn.lib')
17    add_lib_to_dll_path('C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.3/lib/x64/cudart.lib')
18 
19 from _dlib_pybind11 import *
20 from _dlib_pybind11 import __version__, __time_compiled__
```

CUDAが使える状態でインストールされていると、15行目が if 'ON' == 'ON' となっているはず。
どこでおかしいのか分からず、エラーメッセージどおり19行目の_dlib_pybind11 を調べたりしていたけれど、add_lib_to_dll_path関数のエラーをチェックすると

'C:/Program Files/NVIDIA/CUDNN/v8.9.7.29/lib/cudnn.lib'

このファイルが（存在するのに）読み取れていなかった。
cuDNNの展開したフォルダ構成の通りに

'C:/Program Files/NVIDIA/CUDNN/v8.9.7.29/lib/x64/cudnn.lib'

にファイルを移動するとエラーが解消した。

# CUDAの効果
face_recognitionが高速化！
自作アプリAvatarian(アバタリアン)は人の顔を区別して画像に置き換えるのだが、人の検出と特徴量の計算に時間がかかってフレームレートが落ちていた。
5人が映っているYoutubeを例にとると、
CPU:Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz   2.90 GHz
メモリ:16GB
の2018年のノートPCで
顔の検出：```face_recognition.face_locations(frame)```に2.0秒
顔の判別：```face_recognition.face_encodings(frame, face_locations)```に3.5秒
かかっていたものが、RTX1070の外付けGPUでそれぞれ
0.8秒（2.5倍）
0.06秒（58倍！）
に短縮された。

### Avatarian
GPUのある方はぜひAvatarianで遊んでみてください。
会議の相手全員を今話題の「笑い男」にできます（それだけならface_recognitionはいらないのですが）

https://github.com/mtsnrtkhr/Avatarian