NVIDIA Jetson TX2でTensorFlowによる人体姿勢推定プログラムを動かせるようになるまで

![JetsonTX2&tf-openpose](https://qiita-image-store.s3.amazonaws.com/0/211304/5c039c81-feec-271e-27e7-9765a452b81f.png)
[(画像元1)](http://images.nvidia.com/content/tegra/embedded-systems/images/jx10-jetson-tx2-170203.jpg)
[(画像元2)](https://github.com/ildoonet/tf-openpose/raw/master/etcs/inference_result2.png)
# はじめに
本記事は、NVIDIAの組み込みモジュール[Jetson TX2](http://www.nvidia.co.jp/object/embedded-systems-dev-kits-modules-jp.html)の初期設定から、Jetson TX2で[TensorFlowによる人体姿勢推定プログラム(tf-openpose)](https://github.com/ildoonet/tf-openpose)を動かせるようになるまでに行ったこと、を纏めた備忘録です。

# 内容
- Jetson TX2とは
    -  概要
    -  開発キット
- Jetson TX２の初期設定
    - 設定に必要な道具
    - インストール
- tf-openpose （人体姿勢推定プログラム）を動かせるようになるまで
    - tf-openposeとは 
    - 依存関係のインストール
    - tf-openposeのインストール 
    - 他
- tf-openpose を動かす

# Jetson TX2とは
## 概要
Jetson TX2は、NVIDIAが開発したAIに特化した組み込みプラットフォームです。Jetson TK1, TX1の後継モデルとなります。細かい技術仕様や何ができるか等は、[公式サイト](http://www.nvidia.co.jp/object/embedded-systems-dev-kits-modules-jp.html)を見てください。

Jetson TX2はTegraを搭載しており、**性能に対する消費電力の効率が良い**ことが特徴です。[ここ](https://qiita.com/hidenorly/items/0f71a2698987406a1935)を参考にして、電源効率をGTX1080と比較すると、

```Jetson TX2: 364.742/7.5 = 48.6 GFLOPS/W```(自機のベンチマーク結果)
```GTX 1080: 5458.315/180 = 30.32 GFLOPS/W```

となりました。つまり、Jetson TX2は GTX 1080と比較して約1.6倍の電力効率を持つことになります。

更に、Jetson TX2は本体が50mm x 87mmと、**とてもコンパクト**です。（開発機は、200mm x 200mmくらいになります）

また、用途に応じて消費電力と性能を選択するモードが用意されています。各モードの比較は以下の表の通りです。([参考(英文)](http://www.jetsonhacks.com/2017/03/25/nvpmodel-nvidia-jetson-tx2-development-kit/))

|Mode|Mode Name|Denver 2|Frequency|ARM A57|Frequency|GPU Frequency|
|---|---|---|---|---|---|---|
|0|Max-N|2|2.0 GHz|4|2.0 GHz|1.30 Ghz|
|1|Max-Q|0||4|1.2 Ghz|0.85 Ghz|
|2|Max-P Core-All|2|1.4 GHz|4|1.4 GHz|1.12 Ghz|
|3|Max-P ARM|0	||4|2.0 GHz|1.12 Ghz|
|4|Max-P Denver|2|2.0 GHz|0||1.12 Ghz|



**小さい**ながらも、**低消費電力**でかつDeepLearningをはじめとする**学習済みモデルをリアルタイムで動かせる程度のスペック**はあり、**用途に応じてモードを柔軟に選択できる**ので、IoT用のエッジデバイスとして幅広く活用できそうです。

## 開発キット
開発キットは、[公式サイト](http://www.nvidia.co.jp/object/embedded-systems-dev-kits-modules-jp.html)に公認販売店へのリンクがありますので、そこを経由して入手できます。価格は、個人向けであれば[オリオスペック](http://www.oliospec.com/shopdetail/000000005573/ct502/page1/order/)で、2017年12月現在**税込88,992円**で販売されています。更に、**[学生であれば半額](http://ryoyo-nvidia.jp/products/jetson/academic/)**で購入できるようです。

![Jetson開発キット](http://images.nvidia.com/content/tegra/embedded-systems/images/jx10-jetson-tx2.jpg)
[(画像元)](http://images.nvidia.com/content/tegra/embedded-systems/images/jx10-jetson-tx2.jpg)

# Jetson TX２の初期設定
JetsonTX２の設定方法を解説したWebサイトは、和文英文ともにそこそこあります。基本的に次のようなサイトを参考されると良いでしょう。しかし、所々で**罠にハマりやすい箇所**があるので、本記事ではその部分にポイントを絞って解説します。

- お世話になったサイト（和文）
    - [DEMURA.NET](http://demura.net/athome/13445.html)

- お世話になったサイト（英文） 
    - [NVIDIA JetPack公式ドキュメント](http://docs.nvidia.com/jetpack-l4t/index.html)
    - [NVIDIA公式技術フォーラム](https://devtalk.nvidia.com/default/board/188/jetson-tx2/)
    - [JetsonHacks](http://www.jetsonhacks.com/)
    - [elinux.org](https://elinux.org/Jetson_TX2)

### 設定に必要な道具
- host　PC （Ubuntu x64 v14.04, 10GB以上の空き容量）　
    - 【非推奨】**仮想化環境(VirtualBox等)**、 **MacBook Pro 2016 & 2017にデュアルブート**
- USBキーボード、マウス、（用意できれば： USBハブ）

Jetson TX2に、OSや様々なライブラリをインストールするには、Ubuntuを搭載したhost PCからUSB通信で行います。Ubuntuは、公式サイトからDLして、isoファイルをDVD焼くなりUSBに入れるなりして、host PCにインストールしてください。（本記事では解説を割愛します。）

host PCは**VirtualBox等による仮想化環境を非推奨とします。**なぜなら、Jetson TX2にOSとライブラリをインストールする際、host PCとJetson TX2 の間でUSB通信が必要になるからです。VirtualBoxでUSB通信は可能ですが、不安定であり、OSを転送中に高確率で通信が止まります。（[参考（英文）](http://www.jetsonhacks.com/2017/03/21/jetpack-3-0-nvidia-jetson-tx2-development-kit/)）

また、（2017年12月時点では）**MacBook Pro 2016 & 2017に、Ubuntuをインストールすることはお勧めしません。**キーボードとマウスの入力を受け付けません。（[このようなドライバ](https://github.com/Dunedan/mbp-2016-linux#keyboard--touchpad)を導入すれば動くようですが、導入がとても面倒なので、素直に新しいUbuntu環境を用意した方が良いでしょう。）

USBキーボード・マウスは、インストール後半でJetson　TX2本体を操作するときに使用します。USBポートは２つ（USB 3.0 Type A, USB 2.0 MicroB→TypeA変換コネクタ付き）ありますが、前述したように、OSとライブラリインストール時に１つ使用します。つまり、キーボードとマウスを同時に操作したい場合はUSBハブが必要です）

### インストール
#### 1.JetPackのダウンロード


まず、NVIDIA公式サイトから、[JetPack L4T](https://developer.nvidia.com/embedded/jetpack)(SDK)をダウンロードします。内容は、FlashOS(ubuntu 16.04), CuDNN, OpenCV, CUDA等、OSとDeepLearningやGPUコンピューティングをサポートするソフトウェアツールが含まれています。**ダウンロードには会員登録が必要**なので、事前に登録を済ませておいてください。


#### 2.インストール



インストール手順は、[公式ドキュメント（英文）](http://docs.nvidia.com/jetpack-l4t/index.html)や[ここ](http://demura.net/athome/13445.html)を参考にすると良いでしょう。ここでは、**上記のサイトには書かれていないポイント**を補足的に説明します。

まず、ターミナルを開き、DLしたファイルが存在するディレクトリへ移動し、以下のコマンドを実行します。
```$ chmod +x JetPack-L4T-3.1-linux-x64.run```
```$ ./JetPack-L4T-3.1-linux-x64.run```
途中エラーが出ますが気にせず進みましょう。

**実行できない場合**は、
```$ ./_installer/Launcher```
を打ち込めばインストーラが立ち上がるはずです。

インストーラが立ち上がったら、上記参考Webサイトに従い、"JetPack L4T Component Manager"まで進めてください。

"JetPack L4T Component Manager"では、JetsonTX2にインストールするソフトウェアツールを選択します。
　まず、ウィンドウ右上の"Full"をクリックし、下の"Automatically resolve dependency conflicts"にチェックを入れます。
　次に、Package一覧の中で"OpenCV for Tegra on Ubuntu ..."行の"Action"列にマウスを移動させて、クリックします。すると、"install"と"no action"の選択肢が表示されるので、**"no action"**を選んでください。
　（**JetPackで用意されているOpenCVはPython3に対応していない**ので、tf-openposeや他の“Python3でOpenCVを用いるプログラム”が動作しません。このため、この時点では敢えてOpenCVを入れないようにしています。）

　Nextボタンを押して、次に進みます。以降の手順は、上で貼った参考Webサイトに従えば上手くいくと思います。無事にインストールできていれば、CUDAのサンプルが動作するはずです。

# tf-openposeを動かせるようになるまで

ここからは、Jetson TX2を直接操作して、tf-openposeを動かすための各種ライブラリを導入します。Jetson TX2はTegra(Arm系のプロセッサ）を搭載しているので、私たちが普段使っているような(x86、amd64ベースの)PCのノリで、単純にpip install やapt-get installできないケースが多いです。ライブラリ導入には少々手間がかかりますが、我慢して進みましょう。

## tf-openposeとは
[tf-openpose](https://github.com/ildoonet/tf-openpose)とは、画像から複数の人体の姿勢（顔、手指、腕、胴体）を推定するソフトウェア（Caffeベース）を[TensorFlow](https://www.tensorflow.org/)で使用できるようにしたものです。 オリジナルよりも性能が劣ります（指や顔の骨格検出ができない）が、**CPU（GPU演算なし）**や**低電力の組み込みデバイス**でも**リアルタイム**に検出できます。

注意：USB接続のWebカメラでは、通信速度がボトルネックになりリアルタイムで動作しないので、CSIで接続できるカメラが必要です。Jetson TX2の開発キットはデフォルトでCSIカメラが搭載されているので、今回はこのカメラを使用します。

## 依存関係
以下のライブラリ(+他必要なもの)を順次入れていきます。

- python3.5
- tensorFlow 1.3
- opencv3
- protobuf
- python3-tk


### 1.python3.5
Jetson TX2はデフォルトでpython3.5が入っているので、改めてインストールする必要はありません。


### 2.pipのインストール
```$ wget https://bootstrap.pypa.io/get-pip.py```
```$ chmod +x  get-pip/py```
```$ sudo python3 get-pip.py```


### 3.virtualenvのインストール
環境を汚したくない場合は、virtualenvを入れて適当なディレクトリに仮想環境を用意しましょう。
```$ sudo pip install virtualenv```

今回はopenposeというディレクトリに仮想環境を用意しています。
```$ virtualenv openpose```
```$ source openpose/bin/activate```


### 4.TensorFlow 1.3のインストール
(1) [ここ](https://github.com/jetsonhacks/installTensorFlowTX2)からgit cloneまたはzipをダウンロードします。

(2) TensorFlowのコンパイルに必要なメモリ容量が８GB以上なので、スワップ領域を確保します。([参考（英文）](https://syed-ahmed.gitbooks.io/nvidia-jetson-tx2-recipes/content/first-question.html))

```$ fallocate -l 8G swapfile```
```$ chmod 600 swapfile``` 
```$ mkswap swapfile```
```$ swapon swapfile```
```$ swapon -s```

(3) 展開したディレクトリに移動して、Bazel（ビルドツール）と依存ライブラリをインストールします。
```$ sudo bash installPrerequisitesPy3.sh```
(インストールに数十分掛かるので、気長に待ちます。)

(4) TensorFlowリポジトリからv1.3.0をgit cloneして、Aarch64用のパッチを当てます。
```$ sudo bash cloneTensorFlow.sh```

(5) TensorFlow 環境変数の設定を行います。
```$ sudo bash setTensorFlowEVPy3.sh```

(6) TensorFlowをビルドします。
```$ sudo bash buildTensorFlow.sh```
（ビルドに長時間要します。自分の場合、120分程かかりました。）

(7) wheelファイルを作成します。
```$ sudo bash packageTensorFlow.sh```
作成したwheelファイル（tensorflow-1.3.0-cp35-cp35m-linux_aarch64.whl）は、ホームディレクトリにあります。

(8) TensorFlowをインストールします。
```$ pip3 install $HOME/tensorflow-1.3.0-cp35-cp35m-linux_aarch64.whl```

(9) 不要になったswapファイルを削除します。
```$ sudo swapoff swapfile```
```$ rm swapfile``` 


### 5.OpenCV3のインストール
(1) 事前確認
Python2, 3対応版のOpenCV3をインストールします。本記事では、“Jetson TX2の初期設定”の“2.インストール”で、JetPackに付属しているOpenCV（OpenCV4Tegra）を敢えてインストールしませんでした。ホームディレクトリに移動して、該当するディレクトリ（OpenCV4Tegra）が見当たらないことを確認して下さい。

誤ってOpenCV4Tegraを入れしまった場合は、次のコマンドを実行して下さい。（[参考（英文）](https://jkjung-avt.github.io/opencv3-on-tx2/)）
```$ sudo apt-get purge libopencv4tegra-python libopencv4tegra-dev \
                     libopencv4tegra```
```$ sudo apt-get purge libopencv4tegra-repo```


(2) 各種依存関係をインストールします。
```$ sudo apt-get install build-essential make cmake cmake-curses-gui \
                       g++ libavformat-dev libavutil-dev \
                       libswscale-dev libv4l-dev libeigen3-dev \
                       libglew1.6-dev libgtk2.0-dev```
```$ sudo apt-get install libdc1394-22-dev libxine2-dev \
                       libgstreamer1.0-dev \
                       libgstreamer-plugins-base1.0-dev```
```$ sudo apt-get install libjpeg8-dev libtiff5-dev libjasper-dev \
                       libpng12-dev libavcodec-dev```
```$ sudo apt-get install libxvidcore-dev libx264-dev libgtk-3-dev \
                       libatlas-base-dev gfortran```
```$ sudo apt-get install python3-dev python3-pip```
```$ sudo pip3 install numpy```
```$ sudo apt-get install python-dev python-pip```
```$ sudo pip install numpy```

(3) opencv-3.3.0 と opencv_contrib-3.3.0 のソースコードをダウンロードします。
```$ mkdir -p ~/src```
```$ cd ~/src```
```$ wget https://github.com/opencv/opencv/archive/3.3.0.zip -O opencv-3.3.0.zip```
```$ unzip opencv-3.3.0.zip```

(4) OpenCVをビルドします。（Jetson TX1を使用する場合は、CUDA_ARCH_BINを"5.3"に変えます）
```$ cd opencv-3.3.0```
```$ mkdir build```
```$ cd build```
```$ cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local \
        -D WITH_CUDA=ON -D CUDA_ARCH_BIN="6.2" -D CUDA_ARCH_PTX="" \
        -D WITH_CUBLAS=ON -D ENABLE_FAST_MATH=ON -D CUDA_FAST_MATH=ON \
        -D ENABLE_NEON=ON -D WITH_LIBV4L=ON -D BUILD_TESTS=OFF \
        -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF ..```
```$ make -j4```
```$ sudo make install```

(5) virtualenv 仮想環境外で、以下のコマンドを実行します。cv2の場所が表示されます。
```$ python -c ‘import cv2; print(cv2.__file__)’```
```$ python3 -c ‘import cv2; print(cv2.__file__)’```

(6) 表示されたパスをペーストして、リンクをつけます。
```$ ln -s /usr/local/lib/python3.5/dist-packages/cv* (仮想環境のパス)/lib/python3.5/site-packages```

最後に、再び仮想環境に入ってpython3を起動します。import cv2できれば、無事OpenCVが導入できていることになります。


### 6.protobufのインストール
```$ sudo pip install protobuf```
を実行するだけです。


### 7.tkinterのインストール
```$ sudo apt-get install python3-tk```
を実行するだけです。


## tf-openposeのインストール
tf-openposeのrequirement.txt内のscipyをインストールするために、LAPACKを有効にします。（[参考](https://github.com/atinfinity/lab/wiki/Jetson-TX2%E7%92%B0%E5%A2%83%E3%81%A7%E3%81%AEOpenCV%E3%83%93%E3%83%AB%E3%83%89#%E5%82%99%E8%80%83)）
```$ sudo apt-get -yV install libopenblas-dev```
```$ sudo apt-get -yV install liblapacke-dev```

tf-openposeをダウンロードして、requirement.txtの中身をインストールします。
```$ git clone https://www.github.com/ildoonet/tf-openpose```
```$ cd tf-openpose```
```$ pip3 install -r requirements.txt```
(scipyのインストールに数十分要するので、気長に待ちます。)

以上で、tf-openposeのインストールは完了です。

## 他
このままではプログラムは動かないので、以下の操作を行います。
### 1. 学習済みモデルのダウンロード
tf-openposeのリアルタイム姿勢認識プログラム(```realtime_webcam.py```)を実行する際、

- cmu

- mobilenet　（デフォルト）

- mobilenet_fast

- mobilenet_accurate

の学習済みモデルを選択できます。

　```realtime_webcam.py```では、これらのモデルが```models```ディレクトリ内*にそれぞれ格納されているという前提で、プログラムが組まれています。

しかし、**初めの状態ではディレクトリ内に該当するモデルがありません。**代わりに```download.sh```が置かれています。つまり、利用者は**使いたいモデルを適宜ダウンロードしてくれ**ということみたいですので、```download.sh```があるディレクトリに移動して、

```$ sudo bash download.sh```
を実行します。

---
###### *補足1 各モデルの配置場所 
- cmu 
```./models/numpy/```

- mobilenet
```./models/pretrained/mobilenet_v1_0.75_224_2017_06_14/```

- mobilenet_fast
```./models/pretrained/mobilenet_v1_0.50_224_2017_06_14/```

- mobilenet_accurate
```./models/pretrained/mobilenet_v1_1.0_224_2017_06_14/```



---


### 2. ソースコードの改変

#### (1) 学習済みモデルの指定
1.が終わった段階で```realtime_webcam.py```を実行しても、上手く行きません。「保存した学習済みモデルを復元しています(Restoring parameters from　./models/trained/mobilenet_368x368/model-release）」、という感じのメッセージが表示されますが、**存在しないディレクトリを指定してしまっている**ため、復元できません。

復元できるようにするには、**```networks.py```のソースコードを書き換える**必要があります。cmuは37行目、mobilenet系は41~43行目に行き、先程DLしたモデルのパスを指定します。また、mobilenetを使用する場合は、39行目全部と41行目の```% s```をコメントアウトします。

変更を保存したら、```realtime_webcam.py```を実行してみましょう。しかし、残念ながらこれでもおそらく(cmu以外の)モデルは復元できないはずです。(「ニューラルネットワークの<ノード名>が存在しない」旨のエラーメッセージが表示されます。)

この場合は、**代替のモデルを使用**します。　[tf-openpose公式（英文）](https://github.com/ildoonet/tf-openpose#models)の、[mobilenet>368x368 : weight download]から、代替の学習済みモデルをダウンロードして下さい。ダウンロードしたファイルをmobilenetモデルが配置されているディレクトリに置き、先程と同じ要領で```networks.py```の41行目を新しいモデルのパスに書き換えます。


#### (2) カメラ設定

後は、OpenCVでオンボードカメラを読み込むための設定を行います。カメラ情報へのアクセスは、```realtime_webcam.py```の66行目
```cam = cv2.VideoCapture(args.camera)```
によって、行われます。

デフォルトではV4L2 APIを用いる設定になっていますが、[NVIDIA公式フォーラム(英文)](https://devtalk.nvidia.com/default/topic/943129/jetson-tx1/highgui-error-v4l-v4l2-while-opening-camera-with-opencv4tegra-l4t-r24/post/4918143/#4918143)にあるように、GStreamerを用いたパイプラインでカメラ情報にアクセスする必要があります。

方法は、[ここ（英文）](http://www.jetsonhacks.com/2017/09/08/opencv-python-onboard-camera-nvidia-jetson-tx2/)を参考にして```cam = cv2.VideoCapture(args.camera)```を

```cam = cv2.VideoCapture("nvcamerasrc ! video/x-raw(memory:NVMM), width=(int)368, height=(int)368,format=(string)I420, framerate=(fraction)30/1 ! nvvidconv flip-method=0 ! video/x-raw, format=(string)BGRx ! videoconvert ! video/x-raw, format=(string)BGR ! appsink")```

という風にできるよう、引数を指定してあげると良いです。

# tf-openpose を動かす
これで、tf-openposeを動かす準備ができました。```realtime_webcam.py```を実行すると、

![200w_d (1).gif](https://qiita-image-store.s3.amazonaws.com/0/211304/381c30b6-c8dd-8629-408e-a89022b346ac.gif)

という感じに、リアルタイムで姿勢を推定してくれます。

**【性能比較】**
Jetson TX2の概要で説明したように、Jetson TX2は用途に応じて複数のモードを選択できます。各モードでtf-openposeがどのくらいのfpsで動くか比較してみました。

モードの変更方法は以下の通りです。
```$ sudo nvpmodel -m [mode]```
[mode]には、0-4の番号を入力します。

Jetson TX2　全モードで姿勢推定した結果、以下のようなパフォーマンスとなりました。
省電力モード（mode 1, 4）はカクカクしてしまいますが、それ以外は↑のレベルで動作してくれます。

|Mode|Mode Name|Denver 2|Frequency|ARM A57|Frequency|GPU Frequency|<font color="Red">Framerate</front>|
|---|---|---|---|---|---|---|---|
|0|Max-N|2|2.0 GHz|4|2.0 GHz|1.30 Ghz|<font color="Red">6~8fps</font>|
|1|Max-Q|0||4|1.2 Ghz|0.85 Ghz|<font color="Red">3~5fps</font>|
|2|Max-P Core-All|2|1.4 GHz|4|1.4 GHz|1.12 Ghz|<font color="Red">5~7fps</font>|
|3|Max-P ARM|0	||4|2.0 GHz|1.12 Ghz|<font color="Red">5~7fps</font>|
|4|Max-P Denver|2|2.0 GHz|0||1.12 Ghz|<font color="Red">1~4fps</font>|

## 追記

tf-openposeを動かしている時のGPUの使用率を見たい場合は、次のコマンドを実行してください。
```$ sudo ~/tegrastats```

注：```nvidia-smi```はTegraには対応していません。（[（参考）](https://devtalk.nvidia.com/default/topic/883876/installing-nvidia-smi-on-l4t/)）