NVIDIA GET3Dの推論をやってみる（eglInitialize() failedエラー対応版）

# はじめに
　NVIDIAが発表した「GET3D」という3Dモデルを生成するニューラルネットワークを試してみました。
https://github.com/nv-tlabs/GET3D
利用している人が少ないのか、情報が少なく、つまづいたところがあったので、備忘録的に残しておきます。
# 実行環境
|パーツ|型番|
|----|----|
|CPU|Intel(R) Xeon(R) Gold 6246R|
|メモリ|96GB|
|GPU|Nvidia Quadro RTX5000|
|OS| Windows10 Pro for Workstations|
# 方法
　環境構築にはDockerを使っていきます。Dockerを用いずとも環境構築はできますが、C言語系のコンパイラのバージョンを変更する必要があるなど、難易度が高くなるのでオススメしません。

実は私もWSL2上やWindows上のAnacondaで構築を試してみたものの、うまく構築することができず、Dockerでの構築にたどり着いたという経緯があります笑

実際、GitHubのページにもDockerの利用が強く推奨されているようなので、ここは大人しくDockerで構築するべきでしょう。

 また、推論にはGPUを使用します。事前にCUDA Toolkit（11.1以降）をWindows側にインストールしておくようにしてください。

::: note info
 Dockerはすでに導入済みであることを前提に進めていきます。まだインストールが済んでいない方はインストールしてから進んでください。
:::

## Githubのリポジトリをダウンロード
```git clone https://github.com/nv-tlabs/GET3D.git```をするか、自分でダウンロードして適当なフォルダに保存してください。
## Dockerイメージの作成
　先程ダウンロードした公式リポジトリからDockerイメージを作成していきます。
### 1.PowerShellを開く
　コマンドを打ち込むためのターミナルは何でもいいのですが、今回はPowerShellを使っていきます。
PowerShellに以下のコマンドを入力。
```shell-session
# ダウンロードしたリポジトリのディレクトリに移動
$cd {保存場所}/GET3D/docker
# DockerfileからDockerイメージを作成（ビルド）
$ docker build ./Dockerfile -t get3d-img
```
　これで、Dockerイメージの作成が完了しました。これをもとにコンテナ（仮想環境）を構築していきます。
### 2.コンテナの作成・起動
　コンテナを作成していきます。以下のコマンドを入力することで、コンテナが作成されます。
```shell-session
docker run --gpus device=all -it -v "マウントするディレクトリ":/workspace -it get3d:v1 bash
```
このとき、マウントするディレクトリにWindows上の適当なフォルダを選択しておくと、そこにコンテナ内で用いる（GET3Dで用いる）データが格納され、Windows上から直接ファイルにアクセスできるようになります。本記事では、マウントせずとも使えるような手順を紹介していますが、後々の利便性を考え、マウントしたほうが良いでしょう。

コンテナの作成が完了すると、自動的に作成したコンテナのターミナルが起動します。
## Docker上でGET3Dを使う準備をする
　ここからは、Dockerコンテナ上でGET3Dが使えるように準備していきます。
### 1.GPUがDocker上で使えるか確認
以下のコマンドを入力します。
```shell-session
$nvidia-smi
```
すると，以下のように返ってくるはずです．
```shell-session
Mon May 29 04:28:14 2023       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 511.04.01    Driver Version: 511.09       CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Quadro RTX 5000     On   | 00000000:2D:00.0  On |                  Off |
| 33%   35C    P8    22W / 230W |    763MiB / 16384MiB |      5%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A        22      G   /Xwayland                       N/A      |
|    0   N/A  N/A        22      G   /Xwayland                       N/A      |
|    0   N/A  N/A        26      G   /Xwayland                       N/A      |
+-----------------------------------------------------------------------------+
```
ここで，搭載のグラフィックボードの名前が正しく示されていればOKです．```N/A```などと表示される場合には，GPUが正しく認識されていませんので，再度CUDAドライバが当たっているか，dockerのオプションにを入れているか確認してください．
### 2.GET3Dのリポジトリをダウンロード
　Dockerのマウント機能を用いて先程ダウンロードしたGitHubのディレクトリを指定すれば、本来この操作は不要なのですが、今回はDockerコンテナ内に```git```を用いて再度ダウンロードしていきます。**（筆者がDocker初心者なだけ）**

```shell-session
git clone https://github.com/nv-tlabs/GET3D.git
```
### 3.学習済みモデルのダウンロード
今回は推論を行うので、事前に学習済みモデルをダウンロードしておきます。以下のコマンドで学習済みモデルをダウンロードします。
```shell-session
gdown --folder 1oJ-FmyVYjIwBZKDAQ4N1EEcE9dJjumdW
```
### 4.ソースコードの書き換え
　***この操作が重要です。***
 　以前はこの操作なしでも実行できたのですが、仕様変更（？）のためかソースコードの書き換えが必要になりました。そのため、以下のようにソースコードに変更を加えます。

:::note alert
 ソースコードそのままだと、```[F glutil.cpp:338] eglInitialize() failed```というエラーが発生してしまい、生成ができません。
:::

```traning/networks_get3d.py```の**385行目を以下のように変更します。**
```traning/networks_get3d.py
self.dmtet_geometry.renderer.ctx = dr.RasterizeCudaContext(device=self.device)
```
```dr.RasterizeGLContext(device=self.device)```の**GL**の部分を**Cuda**に変更します。
また、同様に```uni_rep/render/neural_render.py```の59行目も以下のように変更します。
```uni_rep/render/neural_render.py
self.ctx = dr.RasterizeCudaContext(device=self.device)
```
以上で、準備が終わりました。
## 推論を行う
　では、早速推論を行ってみましょう。GET3Dのディレクトリに移動して以下のコマンドを入力します。
 ```bash
 python train_3d.py --outdir=results/motorbike  --gpus=1 --batch=4 --gamma=40 --data_camera_mode shapenet_motorbike  --dmtet_scale 1.0  --use_shapenet_split 1  --one_3d_generator 1  --fp32 0 --inference_vis 1 --resume_pretrain ./get3d_release/shapenet_motorbike.pt --inference_to_generate_textured_mesh 1
 ```
 今回のコマンドではバイクの3Dモデルが生成されます。ダウンロードした学習済みモデルにはバイク以外にも車や椅子など数種類用意されていますので、```--resume_pretrain　学習済みモデルのパス```を入力して色々試してみてください。
 　出力されたデータは```--outdir=```オプションで指定したパス（今回は```GET3D/results/motorbike```）に格納されます。
## 補足
### 1.推論したデータの取り出し
　推論によって得られたデータをWindows上に取り出すには、別のターミナルを開いて以下のコマンドを実行します。事前に、生成したモデルが格納されているディレクトリの絶対パスを確認しておいてください。
```bash
# 稼働しているコンテナの確認
docker ps -a

# コピー
docker cp コンテナID:生成したモデルのディレクトリパス Windows上でのフォルダ名
```
コピーが終わると、ユーザーディレクトリ直下に先程指定した名前のフォルダが生成され、その中にデータが格納されているはずです。

### 2.推論中に『UserWarning』が表示される問題について
　この手順通りに推論を行っていくと、推論中以下のような警告が表示されることがあります。

```shell-session
/workspace/GET3D/training/networks_get3d.py:430: UserWarning: torch.range is deprecated and will be removed 
in a future release because its behavior is inconsistent with Python's range builtin. Instead, use torch.arange, which produces values in [start, end).
  camera_theta = torch.range(0, n_camera - 1, device=self.device).unsqueeze(dim=-1) / n_camera * math.pi * 2.0
```

エラー文の通り、「今後この関数は削除予定だから、この関数を使うのはやめてほしい」ということですので、このような表示がされても、推論自体に特に影響はありません（今のところは）。

ただ、このようなエラーが発生するのも精神衛生上良くないと思いますので、この警告が出ないような対処法をご紹介します。

```/traning/networks_get3d.py```の**430行目**を以下のように変更してください。

```/traning/networks_get3d.py
# 「torch.range」⇒「torch.arrange」，「n_camera -1」⇒「n_camera」
camera_theta = torch.arange(0, n_camera, device=self.device).unsqueeze(dim=-1) / n_camera * math.pi * 2.0
```

このように変更することで、警告が表示されなくなります。

# 使ってみた印象
## 生成したモデルについて
「shapenet_car」という学習済みモデルを用いて生成してみました。アニメーションGIF作成にはBlenderとAdobe Media Encoderを用いています。
![生成したモデル](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2676391/1fd5911a-a5de-509e-db33-8ca978139098.gif)
　車の方は結構うまくできているんじゃないかと思います。メッシュがおかしくなっているとこもありませんし、ドアミラーなどの細かい部分もきちんとモデリングされています。

また、テクスチャの方も破綻があるようなところはなく、我々が想像する車がそのまま出力されています。
　続いて「shapenet_motorbike」の方を試してみました。
![shapenet_motorbike](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2676391/48c2f3ba-ed87-34e8-ec9c-0160f1dc5ee7.gif)

こちらも我々が想像するバイクそのものがモデリングされていると感じます。

ただ、こちらについてはエンジン部分のモデリングの粗さが目立ちます。また、テクスチャの部分も一部描画されていない部分があったり、カラーリングに統一感がない（別のバイクのカラーリングと混ざっている）と感じたりするなど、車のほうよりモデリングがうまくいっていない印象があります。

単に設定が悪かったのか、元々の学習モデルの精度が低いのか、はたまたGET3Dの苦手とする分野なのか気になるところです。

# 推論時の要求性能について

推論は本環境で5分程度でした。また、推論ではVRAMを11GB使うので、ゲーム用のグラフィックボードでは推論できる機種が限られてきそうです。ゲーム用グラフィックボードを利用するなら、ミドルハイくらいはほしいですね。

また、意外とRAMを食います。正確にどのアプリケーションがどの程度使用しているのかは確認していませんでしたが、45GBほどメモリを消費していました。おそらくDocker関係で結構使うのではないかと思います。
# 参考文献
・ [https://github.com/nv-tlabs/GET3D](https://github.com/nv-tlabs/GET3D)
・ [NVIDIA GET3D で人工知能による3Dモデル生成を試す ](https://note.com/npaka/n/n34786fb7131c)
・ [GET3D训练笔记（无Docker）](https://zhuanlan.zhihu.com/p/571458207)