tf-openposeで3D姿勢推定するときにハマった話

# 概要
　tf-openposeを用いて骨格推定をして、その後3d-pose-estimatorを用いて3D姿勢推定をする一連の流れを解説します。また、それとともにハマった点とその解決方法を備忘録代わりに共有します。

# tf-openposeについて
tf-openposeを用いれば手軽に骨格推定を行うことが可能です。
<img width="898" alt="スクリーンショット 2019-06-18 19.06.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440634/2f47226e-6213-cdce-0f9b-23c002d71184.png">


## tf-openposeのインストール

```terminal:gitからclone
git clone https://github.com/ildoonet/tf-pose-estimation
```

```terminal:必要モジュールをまとめてインストール
cd tf-pose-estimation
pip install -r requirements.txt
```
swigというC/C++で書かれたライブラリを多言語で利用できるようにするツールをインストールします。

```terminal:swigのインストール
brew install swig
```
次に学習済みのグラフをダウンロードします。

```terminal:pbファイルのダウンロード
cd models/graph/cmu
bash download.sh
```
最後に

```terminal:swigの実行
cd ~/tf-pose-estimation/tf_pose/pafprocess/
swig -python -c++ pafprocess.i && python3 setup.py build_ext --inplace
```
を実行してください。以上でtf-openposeのsettingは完了です。

## tf-openposeの実行
```実行
cd ~/tf-pose-estimation
python run.py --model=mobilenet_thin --resize=432x368 --image=images/p2.jpg
```
以下が出力されれば成功です！
<img width="409" alt="スクリーンショット 2019-06-18 18.01.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440634/2a5fd088-bbdf-eb69-8fc6-04a43c00abab.png">

# 3D Pose Estimation
　openposeを用いて画像から二次元の骨格情報を抽出することができました。これを3d-pose-estimationと組み合わせることで3次元空間での姿勢推定をすることが可能です。
<img width="347" alt="スクリーンショット 2019-06-18 18.06.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440634/235b9eb4-9406-cf5a-fd01-2b7390a4b0ae.png">
 この姿勢情報をunityなどと連携させることで3Dモデルを動画の人間の動きをトレースして動かすといったことが可能になります。

## 3D Pose Estimationのインストール
　3D Pose Estimationは以前は先ほどクローンしてきた gitリポジトリ tf-pose-estimation に組み込まれていましたが現在は削除されています。これを利用するため（邪道であり、開発者様の意向に背いている気がしますが...）masterではなくbranchにあるコードを引用することにします。

まずはディレクトリを移動してブランチの一覧を取得します。

```teminal:ブランチの一覧
cd ~/tf-pose-estimation
git branch -r
```
一覧は以下です。

```
  origin/HEAD -> origin/master
  origin/dev/architecture-mobilenet-v2
  origin/devel
  origin/master
  origin/package
```
このうち、devel branchに切り替えてディレクトリを移動します。

```terminal:ブランチの切り替えとディレクトリ移動
git checkout devel
cd src
```
このsrcフォルダの中にあるliftingフォルダをクリップボードにコピーします。デスクトップなどにliftingフォルダを一度移すのでも構いません。
liftingフォルダをコピー（移動）することができたら、再びmasterに戻ります。

```terminal:masterに戻る
cd ~/tf-pose-estimation
git checkout master
```
次に、tf-pose-estimationフォルダの中にliftingフォルダをペーストしてください。
最後に仕上げです。run.pyの中身を以下のように書き換えてください（新しい.pyファイルを作っても良いです）。

```python:run.py
import argparse
import logging
import sys
import time

import cv2
import numpy as np
import matplotlib.pyplot as plt

from tf_pose import common
from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh

from lifting.prob_model import Prob3dPose
from lifting.draw import plot_pose

logger = logging.getLogger('TfPoseEstimator')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='tf-pose-estimation run')
    parser.add_argument('--image', type=str, default='./images/p1.jpg')
    parser.add_argument('--resolution', type=str, default='432x368', help='network input resolution. default=432x368')
    parser.add_argument('--model', type=str, default='mobilenet_thin', help='cmu / mobilenet_thin')
    parser.add_argument('--scales', type=str, default='[None]', help='for multiple scales, eg. [1.0, (1.1, 0.05)]')
    args = parser.parse_args()
    scales = ast.literal_eval(args.scales)

    w, h = model_wh(args.resolution)
    e = TfPoseEstimator(get_graph_path(args.model), target_size=(w, h))

    # estimate human poses from a single image !
    image = common.read_imgfile(args.image, None, None)
    # image = cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21)
    t = time.time()
    humans = e.inference(image, scales=scales)
    elapsed = time.time() - t

    logger.info('inference image: %s in %.4f seconds.' % (args.image, elapsed))

    image = TfPoseEstimator.draw_humans(image, humans, imgcopy=False)

    logger.info('3d lifting initialization.')
    poseLifting = Prob3dPose('./lifting/models/prob_model_params.mat')

    image_h, image_w = image.shape[:2]
    standard_w = 640
    standard_h = 480

    pose_2d_mpiis = []
    visibilities = []
    for human in humans:
        pose_2d_mpii, visibility = common.MPIIPart.from_coco(human)
        pose_2d_mpiis.append([(int(x * standard_w + 0.5), int(y * standard_h + 0.5)) for x, y in pose_2d_mpii])
        visibilities.append(visibility)

    pose_2d_mpiis = np.array(pose_2d_mpiis)
    visibilities = np.array(visibilities)
    transformed_pose2d, weights = poseLifting.transform_joints(pose_2d_mpiis, visibilities)
    pose_3d = poseLifting.compute_3d(transformed_pose2d, weights)

    for i, single_3d in enumerate(pose_3d):
        plot_pose(single_3d)
    plt.show()
    pass
```
## 3D Pose Estimationの実行
```terminal:実行
python run.py --model=mobilenet_thin --resize=432x368 --image=images/p2.jpg
```
上記のコマンドを出力すれば以下の3d骨格プロットが得られます。人間が2人座っている画像の骨格を抽出したのでプロット画像も二つ出力されます。
<img width="319" alt="スクリーンショット 2019-06-18 19.06.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440634/338eec42-e575-00c7-c4d7-b3eb882b2ac7.png">

骨格情報はpose_3dに入っています。サイズとともに出力すれば、

```
(2, 3, 17)
[[[  71.48704986  -58.26023795 -320.11229876 -183.15640429  216.8238699
    -11.48274567    4.4091282    82.49019393   39.70915699   -2.9661351
     27.84004239  205.97311297  362.78178305  171.281097   -122.7005334
   -202.29460299 -130.81136751]
  [ 113.24948881  194.50322367 -142.71512489 -148.85424371   31.99573068
   -302.24754322 -251.21557536  133.0771608    75.74529577  -14.18464889
     21.06375219    9.19473249  -60.88986293 -124.23290074  164.22177682
    206.25643283   58.97202917]
  [ -96.65909433  -58.91258719 -197.41306904 -613.05761654  -84.34510465
   -211.71201318 -647.53588523  126.09740822  363.46207416  413.2905669
    528.89131252  358.09240553  112.7854445   -58.77230858  318.24367281
     58.29690181  -41.64239587]]

 [[  91.95496863  -31.66337898 -316.19207754 -184.66998433  227.81491288
    -12.584797    -14.16200051   93.84625488   42.73471689   -2.67287331
     12.31833787  206.851387    344.64506511  164.43221306 -119.26256458
   -197.9342645  -122.32756546]
  [ 139.42261847  219.89602532 -162.84033638 -181.27356255   58.94920292
   -336.55624415 -283.76607766  149.11535314   83.52183789   -7.40499281
     30.51344165   14.21467296  -69.95918306 -120.88036364  172.58527775
    200.78417873   49.94852315]
  [-101.36476255  -62.85719618 -176.51123952 -564.96819735  -93.23573586
   -195.51713744 -604.7114395   113.59787852  344.77517494  387.22147932
    498.07333475  339.10907614  105.01815399  -57.24477445  296.2909129
     49.35912406  -34.22891739]]]
```
となっています。(人数, 三次元, 部位座標）という内訳です。部位座標はindexを0~16として

```
0 尻(尾てい骨)
1 右尻(右足付け根)
2 右ひざ
3 右足首
4 左尻(左足付け根)
5 左ひざ
6 左足首
7 脊椎
8 胸
9 首/鼻
10 頭
11 左肩
12 左ひじ
13 左手首
14 右肩
15 右ひじ
16 右手首
```
になっています。
本記事は以上です！お疲れ様でした！

### 参考URL
https://qiita.com/keel/items/0d64167850566586d22a
https://qiita.com/mdo4nt6n/items/d9523aff14dd9fb70c37

### 参考github
https://github.com/DenisTome/Lifting-from-the-Deep-release
https://github.com/ildoonet/tf-pose-estimation