iOSに3D姿勢推定を実装してみた

昨年から機械学習の勉強として仕事にも使えるかと思い姿勢推定に挑戦しています。まだ全然理解できていないですが勢いで３Dの姿勢推定にも挑戦しています。Twitterに途中経過をアップしていますが、思いの外問い合わせが増えてきたので、まだモデルも自分も学習の途中ですが簡単にここにまとめます。

下の動画が2019/3/10時点での最新の結果です。昨年のゴールデンウイークから機械学習の勉強を初めて、今年の正月休みに3Dのコアになる所を一気に作りました（昨年進めてた２Dの環境を3D向けに改修した）。その為かなりバグやミスが多かったので、なおし直し（だましだまし）モデルの学習を進めてきたので、以下の説明でうまく行かなかった等の話はそれらのバグが原因も十分考えられますので、その点はご了承ください。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">あれから一日学習したモデルで試してみました。一日経っても3epochしか進まない。。。少し足の動きが取れるようになったかな。フルスクリーンで見るとメールの通知が写り込んでしまってるけど、楽天ブックスだから気にしないw<br><br>アクション：ミソジサラリーマン様(<a href="https://twitter.com/keriwaza?ref_src=twsrc%5Etfw">@keriwaza</a><br>) <a href="https://t.co/GDNsKiaGyB">pic.twitter.com/GDNsKiaGyB</a></p>&mdash; Yukihiko Aoyagi (@yukihiko_a) <a href="https://twitter.com/yukihiko_a/status/1104559971985895424?ref_src=twsrc%5Etfw">2019年3月10日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 構成
学習環境は、Windows10でVS Code上でPyTorch0.4。
実行環境は、iPhone XSMaxを使用しています。XSMaxを使用しているのはA12プロセッサが非常に強力だからです。ですのでXSでもXRでも良いと思いますが、iPhone8等のA11プロセッサではCoreMLの実行速度が半分以下に落ちてしまうようです。コーディングでもっと速度を稼ぐことができるのかも知れませんが、今の所それに挑戦はしていません。


## Model
モデルはMobileNetV1をベースにしています。層が深い方が精度が上がるという話を聞いたので何層か追加して試してみました。目に見える結果はほとんどなかったのですが、なんとなくそのまま使っています。時間があれば色々比較してみたいんですが時間もないのでそのままです。
推定はSinglePersonに限定しています。MultiPersonは出力から座標を求めるのが結構面倒なのでまずはSingleのみです。
入力は224サイズの画像です。出力は2Dと3Dの両方を出力するようにしています。2Dは14x14x関節数のHeatMapと14x14x関節数x2（x,y座標）のオフセット値の出力です。このあたりはPoseNetを参考にしました。3Dも同様の発想で、14x14x14x関節数のHeatMapと14x14x14x関節数x3（x,y,z座標）のオフセットとなっています。3Dも回帰での推定です。今は関節数が24なのでかなり大きな出力になってます。3Dの姿勢の推定には、14x14の2DのHeatMapからx,y,zのオフセットで補正をかけて推定した方が出力サイズも小さく後の処理も楽で良いのですが、どうにも奥行き方向の感度が上がらなかったので、3DのHeatMapを使用するようにしました。

## Dataset
データセットも2Dと3Dのデータセットを用意しました。2DのデータセットはLeeds Sports Pose Dataset, Leeds Sports Pose Extended Training Dataset, MPII Human Pose Dataset, Microsoft COCOです。3Dのデータセットはオリジナルです。Unityで３Dのキャラクターのアニメーションを作り、カメラオブジェクトに写っているキャラクタの画像とボーンの各座標（肩・肘・手首・親指・中指の付根・足・膝・足首・爪先・耳・目・鼻・身体の中心（へそ位置））を出力するようにしました。キャラクタは十数体用意しました。フリーの物やAssetStoreで購入したデータ、もちろんUnityChanも含まれています。ライセンス的にややこしくなるのでこのデータセットは公開できません。UnityChanは問題ないと思うので下のような画像です。
![im07078.png](https://qiita-image-store.s3.amazonaws.com/0/374813/6f2b043e-32cc-e704-50b9-756a116318f4.png)

## 学習
CGなのでキャラクタのテクスチャやポーズはかなり自由に変えれます。初めは各エポック毎にデータセットの内容が変わるようにすれば汎化性能が上がるかなと期待していたのですが、全然効果がなかった（毎回変わるとテストもしずらい）ので10万枚くらいの画像を作りその画像を学習に使用しています。
この3Dの画像でもそれなりには学習するのですが、どうしても似通った画像になってしまい期待した性能が得られません。”なんとか学習”という２つの学習をすると性能が上がるという話を聞いたので2Dの姿勢推定の学習も同時に行うようにしました。2Dのデータセットはその為に使っています。
学習はPyTorchを使っています。オプティマイザはAdam、なんかAdaBoundなんてものがあるそうなのでそのうち試してみたいです。

## 実装
PyTorchで学習したモデルをonnxにexportして、coremltoolsでCoreMLのモデルに変換します。この時点で同じ画像の推定を行っても結果が異なっているので、精度がどこまで出ているのかわかりません（よくなってるって事はないと思う）。これをMacに持って行ってXCodeでiPhone用のアプリに実装します。バックカメラの画像をリアルタイムにキャプチャして3Dの推定を行うと、私のXSMaxでは40fpsくらいで動かせるくらいの速度で実行できます。ですが、しばらく使っていると本体があったかくなってきて30fpsくらいに落ちてきます。流石に重いんだと思います。2Dのみ学習したモデルだと100fps近くで動作しています。
3Dで表示する際にカメラから見えない部位も表示したい為、ヒートマップの判定の閾値をほぼ0に下げています。どういうことかと言うと、例えば普通に腕が見えている場合であればヒートマップの一番大きいところは0.5以上になります（Maxは1.0）。そして流石に腕が見えていないとそこにありそうと思っても0.2とか0.1になってしまいます。その為閾値を下げていますが、結果的にどこを見ても人がいると判定している状態になります。

## 今後
動画を見ていただいたらわかると思いますが、それっぽく動いてますが細かい所は全然残念な感じです。もう少し精度をあげたいので、もう少し大きいモデルを使う、人間ぽさを出す為にGANを使うなどを考えています。あと、1epoch計算するのに7,8時間かかるのをなんとかしたい。これにGANとか入れたら何時間になるのか。。。
それとどこかで落ち着いたらswiftのコードだけでもGitにあげたいと思います。

## 参考
[Real-time Human Pose Estimation in the Browser with TensorFlow.js](https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5)
PoseNetのBlog記事。これならできんじゃね？と思って機械学習の勉強を始めたので個人的に思い入れがあるページ。

[PyTorchでDeepPoseを実装してみた](https://qiita.com/ynaka81/items/85659dff4d1c2c593f21)
そもそも機械学習どころかPythonでどう書くねんと言うときに参考にさせていただいたページ。なので必然的に今もPyTorchを使ってます。この記事がなかったら挑戦してなかったかも。

[PoseNetをマルチプラットフォームで実装してみた](https://qiita.com/otmb/items/561a62d3413295cc744e)
この記事を読んで、ソースまで上げてくれてるしこれなら俺でもiPhoneで動かせるんじゃね？と思って試して見たページ。穴が開くほど読みました。

[Microsoft/human-pose-estimation.pytorch](https://github.com/Microsoft/human-pose-estimation.pytorch)
Microsoftによる、PyTorchを使用した姿勢推定のコード。MPIIとCOCO用。とても参考になりました。

[DeepPose: Human Pose Estimation via Deep Neural Networks](https://www.slideshare.net/mitmul/deeppose-human-pose-estimation-via-deep-neural-networks)
DeepPoseの論文の説明。日本語なので論文読むよりわかりやすいです。

[apple/coremltools](https://github.com/apple/coremltools)
Appleのcoremltoolsです。これがないとCoreMLのモデルになりません。願わくばもっと簡単に変換できるようになるとうれしいです。

[ミソジサラリーマンMisozi-Salaryman-game style action-](https://www.youtube.com/user/tanahiro814)
ミソジサラリーマン様のアクションの動画。フリー素材として使用可と言うことでありがたく使用させていただいています。アクションのキレが良すぎて姿勢推定にはなかなかハードルが高い。