PIFuHDをWindows + Anaconda + Git Bash環境で実行する

## 概要

[PIFuHD](https://github.com/facebookresearch/pifuhd)という、1枚（or 複数枚）の写真から立体的なメッシュを生成するというDeep Learningのモデルを試したのでそのメモです。
それを、Windows環境で実行するのに色々ハマりどころがあったのでそのメモです。

GitHubに上がっている動画を引用すると、以下のようなことができるものです。

![PIFuHD demo](https://camo.githubusercontent.com/1b965e5bc1a80b6af71c4ec667837a700e37984e/68747470733a2f2f7368756e73756b65736169746f2e6769746875622e696f2f5049467548442f7265736f75726365732f696d616765732f7069667568642e676966)

- [facebookresearch/pifuhd - GitHub](https://github.com/facebookresearch/pifuhd)


## 環境構築

今回の環境は[Anaconda](https://www.anaconda.com/)を利用しています。（PIFuHDではPythonを利用しているのでPython環境が必要）

通常はAnacondaをインストールするとGUIもついてくるのでそこで環境の設定を行うことができます。
またそこから、Anacondaの環境が設定された状態のターミナルを起動することもできるので、コマンドラインからPythonスクリプトを実行するだけであればそれで事足ります。

しかし、色々なDeep Learningなモデルを試していると必ずShellスクリプトを扱う瞬間があります。
当然、Windowsのコマンドプロンプトでは実行できません。

ということで今回は[Git for Windows](https://gitforwindows.org/)のBash環境でもAnaconda（`conda`コマンド）を利用するようにセットアップしたいと思います。
（PIFuHDのデモを試すだけなら`.sh`内のいくつかのコマンドを自分で叩けばいいだけなので必要ないと言えばないのですが）

Anacondaのインストール後、Git for Windows上で`conda`コマンドが使えるように設定します。

## Anacondaをインストール

まずはAnacondaをインストールします。
Anacondaのページの「Get Started」ボタンをクリックし、**Install Anaconda Individual Edition**をクリックして[ダウンロードページ](https://www.anaconda.com/products/individual)に飛びます。

ダウンロードページに**Download**ボタンがあるのでそれをクリックすると、ページ下部にあるダウンロードリンクの箇所までスクロールするので、そこから**64-Bit Graphical Installer**を選んでインストーラをダウンロードし、Anacondaをインストールします。


### Pathを通す

インストールが終わったら、Bashが認識するようにはPathを通します。
すると`conda`コマンド自体は認識してくれるようになります。

自分の環境では`.basahrc`に以下を追記しました。

```sh
export PATH=$PATH:/c/Users/[USER_NAME]/anaconda3
export PATH=$PATH:/c/Users/[USER_NAME]/anaconda3/Scripts
```

インストールした場所に応じて適宜書き換えてください。
デフォルトのままであれば上と同じで大丈夫だと思います。


### `conda activate` でエラーが出る

Pathを通したあと、Anacondaの環境をActivateしようと`$ conda activate`を実行すると以下のようなエラーが発生しました。

```sh
$ conda activate

CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'.
If using 'conda activate' from a batch script, change your
invocation to 'CALL conda.bat activate'.

To initialize your shell, run

    $ conda init <SHELL_NAME>

Currently supported shells are:
  - bash
  - cmd.exe
  - fish
  - tcsh
  - xonsh
  - zsh
  - powershell

See 'conda init --help' for more information and options.

IMPORTANT: You may need to close and restart your shell after running 'conda init'.
```

以下の記事を参考に解決しました。

- [Visual Studio code＋Anaconda＋git bashでwindowsでもLinuxコマンドが使えるPython環境を整えた（2020/03/10）](https://ozunderground.hatenablog.com/entry/2020/03/11/034449)

どうやら`conda init`を実行すると自動的に書き込まれるものが書き込まれないための問題のようです。
なので手動で`.bashrc`に以下を追記することで無事、Git for WindowsのBash上でも`conda`が認識され、利用することができるようになりました。

```sh
# >>> conda init >>>
__conda_setup="$(CONDA_REPORT_ERRORS=false '$HOME/anaconda3/bin/conda' shell.bash hook 2> /dev/null)"
if [ $? -eq 0 ]; then
    \eval "$__conda_setup"
else
    if [ -f "$HOME/anaconda3/etc/profile.d/conda.sh" ]; then
        . "$HOME/anaconda3/etc/profile.d/conda.sh"
        CONDA_CHANGEPS1=false conda activate base
    else
        \export PATH="$PATH:$HOME/anaconda3/bin"
    fi
fi

unset __conda_setup
# <<< conda init <<<
```

これを追記してから`$ source ~/.bashrc`をするか、ターミナルを再起動すると無事、`$ conda activate [ENV_NAME]`を実行することができるようになります。
うまく環境が適用されれば、プロンプトに環境名が表示されると思います。



## PIFuHD向けのセットアップ

Anacondaの環境構築が終わったら、次にPIFUHD向けのセットアップをしていきます。
GitHubのREADMEには以下のように記述されているので、これらが利用可能なようにセットアップしていきます。

>### Requirements
- Python 3
- PyTorch tested on 1.4.0, 1.5.0
- json
- PIL
- skimage
- tqdm
- cv2
### For visualization
>
- [trimesh](https://trimsh.org/) with pyembree
- PyOpenGL
- freeglut (use `sudo apt-get install freeglut3-dev` for ubuntu users)
- ffmpeg

ちなみに手動によるセットアップについては、[HDがつかないバージョンのリポジトリ](https://github.com/shunsukesaito/PIFu)のREADMEに以下のように書かれているので、これを参考にセットアップしました。
（参考にしたのは**OR manually setup environment**の部分です。**それより上の部分はminicondaを使っていたりするので今回は参考にしていません**）


>### Windows demo installation instuction
- Install [miniconda](https://docs.conda.io/en/latest/miniconda.html)
- Add `conda` to PATH
- Install [git bash](https://git-scm.com/downloads)
- Launch `Git\bin\bash.exe`
- `eval "$(conda shell.bash hook)"` then `conda activate my_env` because of [this](https://github.com/conda/conda-build/issues/3371)
- Automatic `env create -f environment.yml` (look [this](https://github.com/conda/conda/issues/3417))
- OR manually setup [environment](https://towardsdatascience.com/a-guide-to-conda-environments-bc6180fc533)
  - `conda create —name pifu python where pifu is name of your environment`
  - `conda activate`
  - `conda install pytorch torchvision cudatoolkit=10.1 -c pytorch`
  - `conda install pillow`
  - `conda install scikit-image`
  - `conda install tqdm`
  - `conda install -c menpo opencv`
- Download [wget.exe](https://eternallybored.org/misc/wget/)
- Place it into `Git\mingw64\bin`
- `sh ./scripts/download_trained_model.sh`
- Remove background from your image ([this](https://www.remove.bg/), for example)
- Create black-white mask .png
- Replace original from sample_images/
- Try it out - `sh ./scripts/test.sh`
- Download [Meshlab](http://www.meshlab.net/) because of [this](https://github.com/shunsukesaito/PIFu/issues/1)
- Open .obj file in Meshlab


### 追加で必要なモジュールをインストールする

上記以外に、追加で以下のものもインストールしました。（PIFuHDのREADMEには書いてある）


- [trimesh](https://trimsh.org/)
- [PyOpenGL](https://anaconda.org/anaconda/pyopengl)
- freeglut 

それぞれ以下のようにインストールします。

```console
$ conda install -c conda-forge trimesh
```

```console
$ conda install -c anaconda pyopengl
```


#### WindowsにGLUTをインストールする

GLUTをインストールしないままにサンプルを試しに実行してみると以下のエラーが発生しました。

```console
OpenGL.error.NullFunctionError: Attempt to call an undefined function glutInit, check for bool(glutInit) before calling
```

GLUTがないためのエラーです。
が、インストール方法が分からず、こちらの記事に助けられました。

- [PIFuHDをWindowsでとりあえず動かしてみた - Qiita](https://qiita.com/sumita_v09/items/47bc110e1f1505fcdc5f)

この記事内では、さらに別の記事を参考に解消したと書かれています。
別の記事はこちら（[WindowsのPythonでOpenGLを使う - TadaoYamaokaの日記](https://tadaoyamaoka.hatenablog.com/entry/2017/02/28/064625)）

そちらから引用させていただくと、

>### FreeGLUTのインストール
PyOpenGLとは別に、GLUTをインストールする必要がある。
GLUTは、OpenGLをプラットフォーム非依存で扱うためのライブラリのようだ。
>
[本家のGLUT](https://www.opengl.org/resources/libraries/glut/)は、Windowsの64bitのバイナリがダウンロードできない。
代わりに、64bitのバイナリが提供されている[FreeGLUT](http://freeglut.sourceforge.net/)を導入する。
>
[FreeGLUT](http://freeglut.sourceforge.net/)のページから
[Martin Payne's Windows binaries (MSVC and MinGW)](http://www.transmissionzero.co.uk/software/freeglut-devel/)
のリンクをたどり、
[Download freeglut 3.0.0 for MSVC](http://files.transmissionzero.co.uk/software/development/GLUT/freeglut-MSVC.zip)
からfreeglut-MSVC-3.0.0-2.mp.zipをダウンロードする。
>
zipファイル内のfreeglut\bin\x64\にあるfreeglut.dllをC:\Windows\System32にコピーする。
>
これで、GLUTが使用できるようになる。


### ffmpegのインストール

ffmpegのインストールについてはこちらの記事を参考にさせていただきました。

- [【windows】FFmpegをインストールする手順](https://fukatsu.tech/windows-ffmpeg)

基本的には`exe`ファイルを落としてきて、それを保存しPathに追加して認識させるだけです。
上記記事はコマンドプロンプトで使う例ですが、今回はGit Bashを利用しているので、`.bashrc`に以下のようにパスを追加しました。

```bash
export PATH=$PATH:/path/to/bin/ffmpeg
```

## Pythonファイルを修正

参考にしたこちらの記事にも書かれていますが、上記までのセットアップを終えてもなおエラーが出ました。
発生したエラーは以下。

```console
Traceback (most recent call last):
  File "C:\Users\[USER_NAME]\anaconda3\envs\pifu\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Users\[USER_NAME]\anaconda3\envs\pifu\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "D:\MyDesktop\Python-Projects\pifuhd\apps\render_turntable.py", line 69, in <module>
    renderer = ColorRender(width=args.width, height=args.height)
  File "D:\MyDesktop\Python-Projects\pifuhd\lib\render\gl\color_render.py", line 34, in __init__
    CamRender.__init__(self, width, height, name, program_files=program_files)
  File "D:\MyDesktop\Python-Projects\pifuhd\lib\render\gl\cam_render.py", line 32, in __init__
    Render.__init__(self, width, height, name, program_files, color_size, ms_rate)
  File "D:\MyDesktop\Python-Projects\pifuhd\lib\render\gl\render.py", line 45, in __init__
    _glut_window = glutCreateWindow("My Render.")
  File "C:\Users\[USER_NAME]\anaconda3\envs\pifu\lib\site-packages\OpenGL\GLUT\special.py", line 73, in glutCreateWindow
    return __glutCreateWindowWithExit(title, _exitfunc)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
```

上記の記事を参考にさせてもらうと、`lib/render/gl/render.py`内の以下の箇所を書き換えることで解決するようです。

```py
class Render:
    def __init__(self, width=1600, height=1200, name='GL Renderer',
                 program_files=['simple.fs', 'simple.vs'], color_size=1, ms_rate=1):
        self.width = width
        self.height = height
        self.name = name
        self.display_mode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH
        self.use_inverse_depth = False

        global _glut_window
        if _glut_window is None:
            glutInit()
            glutInitDisplayMode(self.display_mode)
            glutInitWindowSize(self.width, self.height)
            glutInitWindowPosition(0, 0)
            # 引数の文字列の指定に`b`を加える
            #_glut_window = glutCreateWindow("My Render.")
            _glut_window = glutCreateWindow(b"My Render.")
```


## デモを実行して結果を出力する

さて、これで本当にセットアップが完了しました。
あとは、用意されているデモ用のスクリプトを走らせるだけです。

```console
bash ./scripts/demo.sh
```

これを実行すると以下のように、サンプル写真から出力された動画が表示されます。

![result_test_512.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5616/cd38d2fd-40de-2cac-8395-798b7f2af19a.gif)

ちなみにこのモデルを生成する元になった写真はこちら↓

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5616/9381bf0d-5601-4ba5-41a6-1956e12c0b51.png)


## 独自の写真でメッシュを生成する

まず、メッシュを生成するのに使ったShellスクリプトを見てみましょう。

```sh
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

python -m apps.simple_test
# python apps/clean_mesh.py -f ./results/pifuhd_final/recon
python -m apps.render_turntable -f ./results/pifuhd_final/recon -ww 512 -hh 512
```

内容はたったこれだけです。
ひとつコメントアウトされていますが、これは多分、前に生成したメッシュなどをクリアするかどうかでしょう。

最初の処理（`apps/simple_test.py`）を見てみるといくつかのパラメータを設定していました。

```py
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.


from .recon import reconWrapper
import argparse


###############################################################################################
##                   Setting
###############################################################################################
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input_path', type=str, default='./sample_images')
parser.add_argument('-o', '--out_path', type=str, default='./results')
parser.add_argument('-c', '--ckpt_path', type=str, default='./checkpoints/pifuhd.pt')
parser.add_argument('-r', '--resolution', type=int, default=512)
parser.add_argument('--use_rect', action='store_true', help='use rectangle for cropping')
args = parser.parse_args()
###############################################################################################
##                   Upper PIFu
###############################################################################################

resolution = str(args.resolution)

start_id = -1
end_id = -1
cmd = ['--dataroot', args.input_path, '--results_path', args.out_path,\
       '--loadSize', '1024', '--resolution', resolution, '--load_netMR_checkpoint_path', \
       args.ckpt_path,\
       '--start_id', '%d' % start_id, '--end_id', '%d' % end_id]
reconWrapper(cmd, args.use_rect)
```

設定項目は入力データの場所、出力データの場所、解像度、などですね。

入力について見てみると`./sample_images`フォルダの画像を使うようですね。
ということで、そこにメッシュを生成したい画像を配置して実行してみました。

結果はこちら。

![result_test_512.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5616/8601be26-40ea-1c05-2e15-a0205f9b0de8.gif)

正常に生成できてますね。
（元画像は拾ってきたものなのでここでは載せません）

元々入っているもの以外でも正常に生成できることが確認できました。


## Out of Memoryでメッシュが生成されない

数回しか遭遇しなかった問題ですが、いくつかメッシュを生成しようと試していたところ、CUDAのOut of Memoryエラーでメッシュの生成が中断するという問題がありました。

正常なメモリ解放の方法があるのかもしれませんが、いったん、`$ conda deactivate`をすることで解消できました。
（一度やってからはそのあとはメモリエラー出ず・・謎）

もし遭遇した場合は試してみてください。

## 生成に成功した環境

こちらの記事を参考に、PIFuHDの出力に成功したAnacondaの環境を貼っておきます。

- [conda環境の保存と再構築の仕方](https://qiita.com/nshinya/items/cb1cffabc3305c907bc5)

<details><summary>Anacondaのインストールリスト</summary>
\# This file may be used to create an environment using:
\# $ conda create --name <env> --file <this file>
\# platform: win-64
@EXPLICIT
https://repo.anaconda.com/pkgs/main/win-64/blas-1.0-mkl.conda
https://conda.anaconda.org/conda-forge/win-64/ca-certificates-2020.6.20-hecda079_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/icc_rt-2019.0.0-h0cc432a_1.conda
https://repo.anaconda.com/pkgs/main/win-64/intel-openmp-2020.1-216.conda
https://repo.anaconda.com/pkgs/main/win-64/vs2015_runtime-14.16.27012-hf0eaf9b_3.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl-2020.1-216.conda
https://repo.anaconda.com/pkgs/main/win-64/vc-14.1-h0510ff6_4.conda
https://repo.anaconda.com/pkgs/main/win-64/cudatoolkit-10.1.243-h74a9793_0.conda
https://repo.anaconda.com/pkgs/main/win-64/icu-58.2-ha925a31_3.conda
https://repo.anaconda.com/pkgs/main/win-64/jpeg-9b-hb83a4c4_2.conda
https://repo.anaconda.com/pkgs/main/win-64/lz4-c-1.9.2-h62dcd97_1.conda
https://conda.anaconda.org/conda-forge/win-64/openssl-1.1.1g-he774522_1.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/tk-8.6.10-he774522_0.conda
https://repo.anaconda.com/pkgs/main/win-64/xz-5.2.5-h62dcd97_0.conda
https://repo.anaconda.com/pkgs/main/win-64/yaml-0.2.5-he774522_0.conda
https://repo.anaconda.com/pkgs/main/win-64/zlib-1.2.11-h62dcd97_4.conda
https://repo.anaconda.com/pkgs/main/win-64/hdf5-1.8.20-hac2f561_1.conda
https://repo.anaconda.com/pkgs/main/win-64/libpng-1.6.37-h2a8f88b_0.conda
https://repo.anaconda.com/pkgs/main/win-64/sqlite-3.33.0-h2a8f88b_0.conda
https://repo.anaconda.com/pkgs/main/win-64/zstd-1.4.5-h04227a9_0.conda
https://repo.anaconda.com/pkgs/main/win-64/freetype-2.10.2-hd328e21_0.conda
https://repo.anaconda.com/pkgs/main/win-64/libtiff-4.1.0-h56a325e_1.conda
https://repo.anaconda.com/pkgs/main/win-64/python-3.7.7-h81c818b_4.conda
https://repo.anaconda.com/pkgs/main/win-64/qt-5.9.7-vc14h73c81de_0.conda
https://repo.anaconda.com/pkgs/main/noarch/cloudpickle-1.5.0-py_0.conda
https://repo.anaconda.com/pkgs/main/noarch/decorator-4.4.2-py_0.conda
https://repo.anaconda.com/pkgs/main/win-64/kiwisolver-1.2.0-py37h74a9793_0.conda
https://repo.anaconda.com/pkgs/main/win-64/libopencv-3.4.2-h20b85fd_0.conda
https://repo.anaconda.com/pkgs/main/win-64/ninja-1.10.0-py37h7ef1ec2_0.conda
https://repo.anaconda.com/pkgs/main/win-64/olefile-0.46-py37_0.conda
https://conda.anaconda.org/anaconda/win-64/pyopengl-3.1.1a1-py37_0.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/pyparsing-2.4.7-py_0.conda
https://conda.anaconda.org/conda-forge/win-64/python_abi-3.7-1_cp37m.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/pyyaml-5.3.1-py37he774522_1.conda
https://repo.anaconda.com/pkgs/main/win-64/sip-4.19.8-py37h6538335_0.conda
https://repo.anaconda.com/pkgs/main/noarch/six-1.15.0-py_0.conda
https://repo.anaconda.com/pkgs/main/noarch/toolz-0.10.0-py_0.conda
https://repo.anaconda.com/pkgs/main/win-64/tornado-6.0.4-py37he774522_1.conda
https://repo.anaconda.com/pkgs/main/noarch/tqdm-4.48.2-py_0.conda
https://repo.anaconda.com/pkgs/main/win-64/wincertstore-0.2-py37_0.conda
https://conda.anaconda.org/conda-forge/win-64/certifi-2020.6.20-py37hc8dfbb8_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/cycler-0.10.0-py37_0.conda
https://repo.anaconda.com/pkgs/main/win-64/cytoolz-0.10.1-py37he774522_0.conda
https://repo.anaconda.com/pkgs/main/noarch/dask-core-2.23.0-py_0.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl-service-2.3.0-py37hb782905_0.conda
https://repo.anaconda.com/pkgs/main/win-64/pillow-7.2.0-py37hcc1f983_0.conda
https://repo.anaconda.com/pkgs/main/win-64/pyqt-5.9.2-py37h6538335_2.conda
https://repo.anaconda.com/pkgs/main/noarch/python-dateutil-2.8.1-py_0.conda
https://repo.anaconda.com/pkgs/main/win-64/numpy-base-1.19.1-py37ha3acd2a_0.conda
https://repo.anaconda.com/pkgs/main/win-64/setuptools-49.6.0-py37_0.conda
https://repo.anaconda.com/pkgs/main/noarch/networkx-2.4-py_1.conda
https://repo.anaconda.com/pkgs/main/win-64/wheel-0.34.2-py37_0.conda
https://repo.anaconda.com/pkgs/main/win-64/pip-20.2.2-py37_0.conda
https://conda.anaconda.org/conda-forge/noarch/trimesh-3.8.1-pyh9f0ad1d_0.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/imageio-2.9.0-py_0.conda
https://repo.anaconda.com/pkgs/main/win-64/matplotlib-3.3.1-0.conda
https://repo.anaconda.com/pkgs/main/win-64/matplotlib-base-3.3.1-py37hba9282a_0.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl_fft-1.1.0-py37h45dec08_0.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl_random-1.1.1-py37h47e9c7a_0.conda
https://repo.anaconda.com/pkgs/main/win-64/numpy-1.19.1-py37h5510c5b_0.conda
https://repo.anaconda.com/pkgs/main/win-64/py-opencv-3.4.2-py37hc319ecb_0.conda
https://conda.anaconda.org/pytorch/win-64/pytorch-1.6.0-py3.7_cuda101_cudnn7_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/pywavelets-1.1.1-py37he774522_0.conda
https://repo.anaconda.com/pkgs/main/win-64/scipy-1.5.0-py37h9439919_0.conda
https://repo.anaconda.com/pkgs/main/win-64/opencv-3.4.2-py37h40b0b35_0.conda
https://repo.anaconda.com/pkgs/main/win-64/scikit-image-0.16.2-py37h47e9c7a_0.conda
https://conda.anaconda.org/pytorch/win-64/torchvision-0.7.0-py37_cu101.tar.bz2
```
<details>

上記は以下のコマンドで書き出したものです。

```console
$ conda list --explicit > env_name.txt
```

上記リストをコピペして、以下のコマンドで環境が構築できます。

```console
$ conda create -n [env_name] --file env_name.txt
```


## 最後に

1枚の写真からこれだけのメッシュが生成されるのは驚きですね。
しかも複数枚の写真から生成させるとさらに精度が高くなるようです。
（しかも動画からも生成できるみたい）

近い将来、今までは写真でシェアしていたものをこうした3Dモデルの形でシェアする時代が来るのでしょうね。
AR時代においては立体的にデータがシェアされるのは間違いないでしょうから、今からそういう時代が来るのが楽しみです。