写真から人間の３Ｄモデルを出力できたら、イラストに活用できるよねという話。- SMPL -

#はじめに
近頃、人間の姿勢推定に興味をもっています。
姿勢推定というと個人的にはカメラの姿勢推定を連想します。
しかしそうではなく、ここで言う姿勢推定とは
写真などの何かしらの媒体から、人の姿勢を推定する技術です。
<img src="https://github.com/Pose-Group/DCPose/raw/main/docs/gifs/val_3.gif" alt="サンプル">

個人的なモチベーションとしては、
写真からポーズを推定して３Ｄモデルに当てはめたいと考えています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/d4ab7fa0-d753-b94f-d53d-cb8421431756.png)


より具体的にいえば、趣味や仕事でイラストを書いているので、
イラストの構図の模索に使えそうだなーということで、調べてみます。

#SMPL
まずは３Ｄモデルの準備です。
最近はVtuberなどで３Ｄモデルを動かすシーンを目にすることが多くなりました。
Vtuberの３Ｄモデルの動きを見ていると、モデルによっては関節が人形のように
カクカクと曲がって動くものがあります。こういう３Dモデルってすこし不自然ですよね。

<iframe  height="315" src="https://www.youtube.com/embed/kuBlUyHeV5U" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

SMPLが提案するモデルでは、動きに合わせて肌が連動して動くので、
動画のように非常に自然に動きます。２４個の関節の動きを指定することで、
モデルを動かすことができるようです。まずは、動かすサンプルがあるようなのでそれを試しに動かしてみます。

なお、SMPLは営利目的では使用できないので注意して下さい。
また余談ですが、3Dモデルを動かすだけならblenderでできますが、
SMPLを導入する最大の理由はgitの姿勢推定系の研究は
SMPLを使っていることが多いのでこちらを利用します。
それから、SMPLによる形状変化は私のやりたいことと関係ないので一切触れない。

##環境構築
###環境
####pythonのバージョン
　python 2.7 ←　重要です。
　　　　　　　普段、実行名だけ入れてパスを入れずにpython系のツールを実行している人は、
　　　　　　　pythonのバージョンとpipのバージョンに死ぬほど注意しましょう。
####OS：
　私はWindows　(公式はmac/Linux推奨。以降に書きますがソースを一部改変しました。).
####ライブラリ：
　numpy==-1.16.6
　opencv-python==3.2.0.7
　Chumpy==0.70
　　※2.7の環境でopencvを何も考えずにpipからデフォルトバージョンでインスコしたら
　　　エラーが出たので、2.7に対応していそうな時代のバージョンに下げました。

###ダウンロード
https://smpl.is.tue.mpg.de/en
サインインの画面から登録画面で登録すると、トップページの上部にダウンロードのタブが現れます。
そこから、「Download version 1.0.0 for Python 2.7」をダウンロードします。

###少しコード変更
####ライブラリのパスの変更
ダウンロードしたzipを展開します。Readmeのとおりに行ってもよいのですが、
windowsでちょっとしたソースのパスを環境設定に追加するのはちょっとめんどくさいので、
ちょっとだけ変えます。めんどくさいので、実行ファイル配下に必要なもの全部ぶちこみます。
（たぶんもっとうまいやり方が有るとは思います）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/c4a39f2f-bbad-45c1-610b-37cdb8e9e64e.png)
ライブラリのソースをサンプル実行ファイルの「hello_smpl.py」と同じ階層に貼り付けて、
「hello_smpl.py」内のライブラリのimport部分を同じ階層から呼び出すようにします。

####読み込む３Dモデルの保存場所の変更
ライブラリからモデルを呼び出すパスも少し修正します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/13aa466f-715c-b4b6-e5bb-63b8a68436f0.png)
上の方の階層にあるmodelというフォルタをコピーして、
サンプル実行ファイルの「hello_smpl.py」と同じ階層に貼り付けます。

####モデルの呼び出し変更
「hello_smpl.py」の"m = load_model"で３Ｄモデルを呼び出しますが、
私が開いた時は、モデル名のバージョンがちがかったり、
windows特有のパスの指定方法がわかりづらかったので修正しました。

```test.py
# model_name = "basicModel_f_lbs_10_207_0_v1.0.0.pkl"
model_name = "basicmodel_f_lbs_10_207_0_v1.1.0.pkl"
model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)) , os.path.join("models", model_name))
m = load_model( model_path )
```

####出力結果保存先の変更
objファイルを書き込むところがあるので、
そこのところを修正。

```test.py
# outmesh_path = './hello_smpl.obj'
outmesh_path = os.path.join(os.path.dirname(os.path.abspath(__file__)) , hello_smpl.obj)
```

実行ファイル「hello_smpl.py」と同階層に[models]フォルダを置いてあるという前提で修正します。

###実行
プログラムを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/67349ec6-1733-88a4-d44d-c8238e66ae0b.png)
objファイルができました。
これが3Dファイルとなります。ファイルを見るためのビューアは本当に何でも良いのですが、
https://chrome.google.com/webstore/detail/obj-viewer/niolinkgjinohfdlpnnabmbjnlnlmmgi/related?hl=ja
chromeの拡張機能で見るのが一番らくそうなので、これで見てみます。

なお個人的には、intel内蔵のグラフィックボードでもなんでもいいので、
グラボが入っているPCならMeshlabがおすすめです。

結果はこんな感じ。棒立ちじゃなく、ちゃんと動きをとってますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/3f6ec7a0-91ba-bbd9-9fd7-66ea6a9255e1.png)


###どうやって動きを指定してるんだろう？
SMPLのパラメータとして、
姿勢(Pose)と形状(Shape)の2種類があります。今回はポーズの方を言及します。

```test.py
m.pose[:] = np.random.rand(m.pose.size) * .2
```
コード中の上記の部分で指定しいます。この例ではランダムな数値を入れています。
当然ですが、m.pose[:]をコメントアウトすると、デフォルトのポーズが表示されます。
入っている数値は全てゼロの値です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/45a347dc-dc1b-f10e-e756-203cd872c1c1.png)
出力結果を見ると当たり前ですが棒立ちです。

m.poseに関節をどう動かすかを指定することで、モデルの制御ができるわけですが
どう動くのか、どれくらい回転するのかの数値は
「axis-angle rotation」という表現方法でで表されています。
https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation

SMPLで調整可能な関節は合計23個あります。
これに対して、「axis-angle rotation」で表現された角度の情報が1つの関節あたり３つあります。
ではこれらのパラメータがどのようにコードで表現されているかみてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/f1157278-9a37-a8c7-2cbe-bfdec331bc1e.png)

m.poseの中身はこんな感じのnumpy配列が入っていて、コードでは一次元の配列に格納されています。
しかし内部的な扱いとしては、
23個の関節 × （x,y,z）軸ごとの3次元回転角　＝　69次元の情報が入っています。

・・・と、言いつつも、"m.pose"の次元を調べると全てで72次元ありました。

実は、72次元の情報の中には、
「23個の関節の回転情報=69個」と、
「初期表示の向きの情報= 3個」が入っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/cc218a4d-bb73-c42e-91c5-a98d63174ebb.png)

(上図)初期表示の向きの情報とは、
モデルを最初に表示した時にどのように回転させるかを表した数値になります。
ついでに、23個の関節の回転情報がどのインデックスに対応しているかについても見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/3e24c502-fbdc-88cf-2b11-2b865d73bf28.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/8b8fea62-df18-1a23-78aa-164b1e86d334.png)

適当に作ってみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/5bd2e2f7-6c16-126b-8dea-158433a9866d.png)


さて、以下は自分用のメモですが、
「axis-angle rotation」で表現された回転情報をロドリゲスの公式で3x3の回転行列に変換できるらしいです。
https://virtualhumans.mpi-inf.mpg.de/papers/SMPL15/SMPL15.pdf
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581710/1f89ed47-164c-0ef4-9d43-6a35c13dc0c2.png)


#まとめ
今回はSMPLで遊んでみただけでしたが、
次回はCVPR2021で紹介されていた人間の姿勢推定のOSSで遊んでみようと思います。