PoseNetをマルチプラットフォームで実装してみた


# 目的

今月(2018/5/8)公開された[PoseNet](https://github.com/tensorflow/tfjs-models/tree/master/posenet)を対象に[TensorFlowJS](https://js.tensorflow.org/)で使われているモデルのウエイトは再利用可能か？について調査と、[iOS](https://github.com/infocom-tpo/PoseNet-CoreML)と[Unity](https://github.com/infocom-tpo/PoseNet-Unity)での実装を試したのでまとめました。   

# PoseNetについて

最近になって、リアルタイムな姿勢推定が盛り上がっています。

- [OpenPose](https://github.com/CMU-Perceptual-Computing-Lab/openpose)(2017/4) .. GPUでリアルタイム
- [PoseNet](https://github.com/tensorflow/tfjs-models/tree/master/posenet)(2018/5) .. ウェブでリアルタイム

アプローチは違いますが、どちらもディープラーニングで姿勢推定を実現しています。
PoseNetのコンセプトとしては、他の姿勢推定ライブラリと比較し、特殊なハードウェアやカメラ、システムのセットアップが不要な点です。 
ウェブカメラとブラウザを持っていれば手元で実行が可能です。  

元々の[論文](https://arxiv.org/abs/1803.08225)ではCOCOのテストにあるOpenPoseよりも高精度を達成したそうです。
表のResNet152(single-scale)=0.665,ResNet152(multi-scale)=0.687でそれぞれCMU-Poseの0.618を超えるスコアになっています。  

![pose_score.png](https://qiita-image-store.s3.amazonaws.com/0/35973/a144f512-0f17-bf45-f116-968176e89941.png)


また、PoseNetはブログでも紹介がありますので参考ください。
[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モデル

モデルの構造は図のようになります。 
ここでは、入力画像を513pxで処理しています。  

![model.png](https://qiita-image-store.s3.amazonaws.com/0/35973/db026c24-7bfb-be07-ec72-bbb595a3d4ed.png)


# PoseNetウエイトの変換

[mobilenet.ts](https://github.com/tensorflow/tfjs-models/blob/master/posenet/src/mobilenet.ts)を参考にして、Pythonでモデルを実装しCoreMLに変換しました。  

1. ウエイトをモデルに設定
1. checkpointと、graphを保存
2. 2.を使い、freeze_graphの作成
3. [tfcoreml](https://github.com/tf-coreml/tf-coreml)でCoreMLを作成

変換に使ったコードは[こちら](https://github.com/infocom-tpo/PoseNet-CoreML/tree/master/converter)にあげています。  

# PoseNet(TypeScript) から iOS(Swift)への移植

静的型付け言語同士ですので、割と楽に移植できました。
numpyで使われれるような行列関数が使われないのも移植が楽なポイントです。 
多次元配列については[qoncept/TensorSwift](https://github.com/qoncept/TensorSwift)を利用しました。  

## 端末@iPhone7でのパフォーマンス

![tennis_result.png](https://qiita-image-store.s3.amazonaws.com/0/35973/83d2d848-6801-1c56-21cd-7c3d8b749930.png)


画像サイズ毎に精度、計算量が違うので、処理速度が変わります。
フレームレートをあげて、無理をさせると端末の熱で処理が遅くなりましたので適度な速度で制限をかけるのがよさそうです。  

# Unityで動かす

iOS以外ではUnityでも実装しました。
推論環境のライブラリとしては、先日[ml-agents](https://github.com/Unity-Technologies/ml-agents)のDevelopにアップされたTensorFlowSharpのv1.7.1のPluginを試しました。
Win/Macで動作することは確認できましたが、iOSでは`new TFGraph()`をコールした時点でクラッシュします。
また、WindowsでDevelop Buildにチェックやブレークポイントを利用する場合にエラーになります。 
これは、DotNetのバージョンが異なるのが問題ですので、[TensorFlowSharp](https://github.com/migueldeicaza/TensorFlowSharp)のv1.7.0を取得し、[TensorFlowSharp.csproj](https://github.com/migueldeicaza/TensorFlowSharp/blob/master/TensorFlowSharp/TensorFlowSharp.csproj)の`<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>`を`net46`に変更し、出来上がったTensorFlowSharp.dllで上書きすることで回避できました。
iOSも再ビルドすることで回避できるかもしれませんが未調査です。
他には、UWP対応はまだのようです。
  
[TensorFlowSharp](https://github.com/migueldeicaza/TensorFlowSharp)はTensorFlowの正式なプロジェクトではなく、有志(Monoの有名な人)のプロジェクトです。
ml-agentsの推論環境としてTensorFlowSharpが使われていますが、Unityの為にTensorFlowSharpは作られていませんので、Unityでの不具合はどうしようもないところがあります。
iOSの不具合やUWP対応については、Issueで議論がありますので近いうちに解消すればいいなぁと思っています。  

# 結論

PoseNetのウエイトの移植から、iOSのCoreML、UnityのTensorFlowSharpへの実装についてまとめました。
TensorFlowJSのウエイトはマルチプラットフォームで動くことが確認できました。
半年前の調査に比べAIのエコシステムの相互補完がいい感じに機能していることが感じれました。

今回SwiftやC#でPoseNetを実装をしました。
各プラットフォームへのローカライズは割と手間です。
また、UnityのAI環境は安定版でないことも確認できました。

以上です。この情報が参考になればと思います。

# 宣伝

以前にOpenPoseのモバイル対応についても記事を書いています。
よければご覧ください。

- [AI初心者がOpenPoseのモバイル実装に取り組んだ話](https://qiita.com/otmb/items/b924b5f600db1ce11037)

# コードの公開先

- [PoseNet-CoreML](https://github.com/infocom-tpo/PoseNet-CoreML)
- [PoseNet-Unity](https://github.com/infocom-tpo/PoseNet-Unity)
- [SwiftOpenPose](https://github.com/infocom-tpo/SwiftOpenPose)

## References

- [PersonLab: Person Pose Estimation and Instance Segmentation with a Bottom-Up, Part-Based, Geometric Embedding Model](https://arxiv.org/abs/1803.08225)
- [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)