OpenPose はどこまでの画像に耐えられるのか ~ Pose Estimation の紹介~

[CyberAgent Developers Advent Calendar 2018](https://adventar.org/calendars/2951)の12日目の投稿となります。
前日はmarty-suzukiさんの「[バンクシーのシュレッダーを1時間で実装して申請した話とその後](https://qiita.com/marty-suzuki/items/5e7ef43b61cb13d4ac42)」でした。

12日目は、[AI Lab](https://adtech.cyberagent.io/ailab/) の接客対話グループの馬場([@baba5246](https://twitter.com/baba5246))が担当します。
この研究グループでは、[接客ロボット](https://www.cyberagent.co.jp/news/detail/id=21561)の研究開発を進めていますが、ロボットの認識能力を高めるために、ロボットに付属しているセンサーだけでなく、外部カメラ・センサーを設置して周辺状況の認識をしています。

<img src="https://qiita-image-store.s3.amazonaws.com/0/29717/4d0aac33-6f06-aef1-74eb-489d2ad4b1b9.png" width=50%>

その周辺状況のひとつである「周りの人の状況」を認識するための、Pose Estimation（骨格検出）について既存手法を紹介しながら、実際にいくつかの画像でどう動くかを調べていきたいと思います。


# Pose Estimation （骨格検出）とは

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29717/4b3c6976-ef6e-fe86-613c-9c9168566c9c.png)

(OpenPose 紹介動画から引用)

Pose Estimation は画像から人の骨格（身体部位とその繋がり）を検出するタスクで、深層学習による画像処理タスクの精度向上のおかげで、近年、飛躍的にその精度が上がってきています。Pose Estimation の手法にも様々なものがあり、2D画像内での骨格の座標を特定するものや、2D画像から骨格の3D座標を推定するもの、また、複数人に対応しているものやリアルタイム処理を売りにしているものまで、多種多様な手法が提案されています。

今回は2D画像内での骨格検出手法の中でも、独断と偏見で有名どころをピックアップしてご紹介します。


## 手法1：[OpenPose](http://openaccess.thecvf.com/content_cvpr_2017/papers/Cao_Realtime_Multi-Person_2D_CVPR_2017_paper.pdf)

<img width="787" alt="スクリーンショット 2018-12-12 22.15.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/29717/96aae664-3cfe-38c2-194b-d84cd8a71d82.png">
["Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields", Z. Cao, et al., CVPR 2017](http://openaccess.thecvf.com/content_cvpr_2017/papers/Cao_Realtime_Multi-Person_2D_CVPR_2017_paper.pdf)

ここ数年でみると、一般に最も広く利用されている Pose 推定手法です。それまで主流だった、人全体の範囲を検知してからその内部の関節パーツを推定するトップダウンのアプローチではなく、先に関節部位を推定してそれらを繋げるボトムアップな方法をとりながら、当時のコンペ（2016 MSCOCO Keypoint Challenge）で優勝するほどの精度を叩き出しました。
非商用で使う分には[無料でライブラリを公開](https://github.com/CMU-Perceptual-Computing-Lab/openpose)しており、簡単に試すことができるのも素晴らしいですね。
特徴は、部位同士や複数人が重なり合っていてもそれらを正確に分離できる点と、それを非常に高速にリアルタイムで処理できる点です。使ってみるとわかりますが、非常に頑健で、少々画素が荒くとも画像がとぎれていようとも正確に検出することができます。
<img width="1100" alt="スクリーンショット 2018-12-12 22.14.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/29717/6c97ebee-9c2e-1df6-d596-e0a284ae6dbe.png">
([OpenPose 論文](http://openaccess.thecvf.com/content_cvpr_2017/papers/Cao_Realtime_Multi-Person_2D_CVPR_2017_paper.pdf)から引用)

この手法のキーアイデアは「Part Affinity Fields」という部位間のベクトルの学習にあります。部位と部位の間にあたる画素すべてに下図のようなベクトルを教師として付与し、それを学習しています。$L^{*}_{c,k}(p)$ はピクセル $p$ につけるべき教師ベクトルで、人 $k$ の肢 $c$ 上にあるピクセル $p$ では、ベクトル $v$ を教師として付与します。なんという力技… :innocent: 

![図1.png](https://qiita-image-store.s3.amazonaws.com/0/29717/893b4f24-79a4-3846-b5b8-3d3f9f5bdddf.png)

しかし、そのおかげで部位間のベクトルを推定でき、ある部位に繋げられる他の部位が複数あっても、そのベクトルによって正確に繋がるべき部位のペアを見つけ出すことができます。この教師データは非常に貴重で作成に手間がかかるので、独自にデータセットを作るのが困難で、商用利用を$25,000に設定するのもうなずけます。

実装も、各深層学習ライブラリ用にいくつも展開されていて、ひとまず試しに入れてみることが簡単にできるので、気が向いた方は以下からお試しください。

* 本家：https://github.com/CMU-Perceptual-Computing-Lab/openpose
    * ここにもまとまってます：https://github.com/ZheC/Realtime_Multi-Person_Pose_Estimation#other-implementations
* TensorFlow：https://github.com/ildoonet/tf-pose-estimation
* Keras：https://github.com/michalfaber/keras_Realtime_Multi-Person_Pose_Estimation
* Swift：https://github.com/infocom-tpo/SwiftOpenPose


## 手法2：[PoseNet](https://arxiv.org/pdf/1803.08225.pdf)

<img width="780" alt="スクリーンショット 2018-12-12 23.02.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/29717/b090c1a4-ccc8-bbdd-bdb4-570a957bfedc.png">
["PersonLab: Person Pose Estimation and Instance Segmentation with a Bottom-Up, Part-Based, Geometric Embedding Model", G. Papandreou, et al., ECCV 2018](https://arxiv.org/pdf/1803.08225.pdf)

もう一つここでご紹介するのは Google から発表された PoseNet です。PoseNet も OpenPose と同様、ボトムアップアプローチな手法ですが、特徴としては Web ブラウザでも動作するよう作られています。時代と逆行して Python 実装をしないあたりにこだわりを感じますが、[こちらから非常に簡単にデモを試す](https://storage.googleapis.com/tfjs-models/demos/posenet/camera.html)ことができます。

こちらの手法も部位間のベクトルを利用する点で、発想は OpenPose に似ていますが、OpenPose のような強力な教師データを使わずに、OpenPose よりも高い精度を出しています。下図のように、部位のヒートマップを推定するのと同時に、部位周辺画素から部位への小さなベクトル（図中の"Short-range offsets"）と、部位間の大きなベクトル（図中の "Mid-range offsets"）を学習しています。

<img width="818" alt="スクリーンショット 2018-12-12 23.07.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/29717/0cb0f719-b289-4d46-7db8-e14b6e0ca9ba.png">
（[PoseNet 論文](https://arxiv.org/pdf/1803.08225.pdf)より引用）

この小さなベクトルがいい役割をしていて、推定する部位のピクセル座標をより正確にするために使われる一方で、部位間のベクトルを正確につなげることにも使われます。これにより、強力な教師ベクトルがなくとも、正確に骨格を検出できるようです。

ただ、OpenPose より少し面倒な点として、検出できる最大人数を設定しないといけません。この最大人数分、検出しようとするアルゴリズムなため、3人しか写っていないのに6人分（重複している）の Pose が返ってきたりします。まあ、ある程度はルールで重複を取り除けるので、なんとかなります。

実は、PoseNet ではセグメンテーション（領域分割）を行うことも含まれていて、その部分に興味ある方はぜひ論文をご参照ください。
<img width="608" alt="スクリーンショット 2018-12-12 23.26.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/29717/5897a07a-d623-640e-33e3-00b7282f1f5c.png">


# いろんなポーズを推定してみた

※ 時間の関係で、いったん OpenPose の結果を載せています。時間があれば、poseNetの結果も追記します。
※ 今回はデフォルトのパラメータで利用しています。パラメータチューニングや実装ライブラリの変更などをすれば結果は変わるので、参考程度にご覧ください。
※ 画像は[ぱくたそ](https://www.pakutaso.com/)さんから拝借してます。

## 正面、横、後ろ

![out1.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/4b3e8a5d-17e7-b06c-a3f2-63a77d672ede.jpeg)
![out2.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/5287124a-eb47-2166-ed5a-dfd44c610e7c.jpeg)
![out3.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/30baed5a-8cac-a6ca-e062-b17473205c7a.jpeg)
いずれも問題なく推定できていますね。
特に最後の画像は、実際に耳が写っていないのに耳を推定できています。

## 切れてる、逆さ、真横

![out4.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/ed3dc79c-c811-89a3-5c7e-561cc10f84e9.jpeg)
![out5.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/a4ccfe45-33a8-5fcf-75bb-d367b5f44eff.jpeg)
![out6.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/e7905238-14ff-fb71-eb7b-1d11987e7ae5.jpeg)
いずれもうまく検出できず。。
とくに逆さや真横のような画像は学習データに含まれてなさそうので、それらを含めて再学習をすれば結果が変わりそうです。

## いろんな体勢

![out13.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/4c2df7b6-fc89-215e-1ff6-f0e10db513d4.jpeg)
![out14.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/b0bf63b7-d569-f91b-d436-1b5e65467b1c.jpeg)
![out15.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/b16e68ee-095a-8f0c-1c9f-b59662a9b5dd.jpeg)
休日のお父さんポーズはなんとか善戦しています。
が、やはり寝そべると難しさが増す感じ。

## ロボットと2次元

![out10.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/558f5b77-38e2-e14a-175e-7f91efd8ff87.jpeg)
![out11.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/8e36e0a5-ac22-cbb2-91a0-affd908ea577.jpeg)
（https://shingeki.tv/season3/ より引用）
![out12.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/23a3c25b-08e5-3aca-6004-7daf3390d8af.jpeg)
（https://anime.granbluefantasy.jp/ より引用）

より人間に近い方のロボット（CommU）は骨格をきれいにとれています。
また、進撃の巨人も善戦しているように見えますね。画風によってはチューニングなしできれいにとれそう。

## ゴリラとチンパンジー

![out16.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/c53ebdba-f8d2-a8ad-c724-194e7327a2b6.jpeg)
![out17.jpg](https://qiita-image-store.s3.amazonaws.com/0/29717/6263fa14-6467-40af-2cd9-981ef0ce4c76.jpeg)

いくらDNAが近いからって見た目が遠ければ無理でしたね。。

# おわりに

今回利用した画像は、被写体が大きかったり陰影をつけていたりそもそも学習データとかけ離れていたりと、非常に学習モデル側に不利な画像が多かったのですが、それでもきれいに骨格を出せていて面白かったですね。時間があれば、PoseNetや他の手法も追記していこうと思います。

おわり。