【MediaPipe ✕ Cubemos SDK】骨格検出した身体と手を対応付ける(その3:方式修正編)

2020.06.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

カフェチームの山本です。

以前の記事で、Cubemos SDKで検出した骨格とMediaPipe Multi Hand Trackingで検出した手の対応づけを動画で試しました。結果として、前回考えた方式ではうまく対応付けできない状況があることがわかりました。

【MediaPipe ✕ Cubemos SDK】骨格検出した身体と手を対応付ける(その2:3D動画編)

前回は、上の記事の問題を解決するための前段階として、画面全体の手を精度よく検出する方式を検討し、検出できることを確認しました。

【MediaPipe】【MediaPipe】画面を分割することでMulti Hand Trackingの検出精度を高める

今回は、1つ目の記事の問題を解決するため、骨格と手を対応付ける方法を修正します。

結論としては、

(MediaPipeに関連する記事はこちらにまとめてあります。)

【MediaPipe】投稿記事まとめ

処理の概要

下図のように、処理します。元となる動画に対して、骨格の検出と手の検出をそれぞれ行い、2つの結果を対応付ける、という方法です。この記事は「対応付け」の部分で、すでに検出された骨格と手がそれぞれどれに対応するかを判定します。

前処理:骨格の検出・手の検出

骨格の検出はCubemos SDKで行い、手の検出はMediaPipe Multi Hand Trackingを改良したプログラムを利用します。これらの内容については、別の記事をご参照ください。

Skeleton Tracking SDKのPython版サンプルのコードを読んでみた[Windows編]

【MediaPipe】画面を分割することでMulti Hand Trackingの検出精度を高める

出力されるデータは以下のとおりです。

骨格データ

18点(身体の関節)の座標と確信度

手データ

バウンディングボックス・21点(手の関節)の座標・右手左手の判定/確信度

本処理:対応付け

仮定:

今回、骨格と手を対応付けるにあたって、以下のようになっていると考えました。

  • 仮定1:対応付けるべき骨格と手は、近い位置に検出されている。遠い位置の骨格と手は対応付けるべきでない。
  • 仮定2:対応付けるべき骨格の腕(前腕)と手は、近い方向を向いている。大きく方向が異なる骨格と手は対応づけるべきでない。
  • 仮定3:対応付けるべき骨格の手首と手は、左手/右手が同じである。左手/右手が異なる骨格の手首と手は対応づけるべきでない。

理想的には、これらが満たされる骨格と手の組み合わせを探索すれば、対応付けできそうです。

しかしながら、実際には、各検出は完全ではありません。例えば、検出した骨格の位置がずれたり、手の方向が大きくずれることもあります。また、手が重なった場合など、上の仮定通りにならない状況もあります。そのため、探索には工夫が必要そうです。

検出データの性質

仮定の3点(位置、方向、左右)を判定する方法を考えるにあたって、データの性質を見てみると以下のようでした。

骨格データ

  • 検出された骨格が間違っている場合はかなり少ない(False Positiveが少ない)
  • 画面に映っている骨格が検出されないことがある(False Negativeがある)
  • 検出した座標は概ね正しいが、間違っている場合がある
  • 確信度は、座標があっている場合、ほとんど高い。座標が間違っている場合、その点の確信度は低いときもあるが、高いときもある。
  • 左右を間違えることはない(調べた限り)

手データ

  • 検出された場合でも、間違っている場合がある(False Positiveがある)
  • 画面に映っている手が検出されないことがある(False Negativeがある)
  • 位置は(手が正しく検出できていれば)正確に検出される
  • 左右の判定はかなり間違いやすい
  • 左右の判定の確信度は、判定が間違っているときに、下がることもあるが、高いときもある

判定方法

上記の性質を考慮し、3点の条件の判定方法は以下のようにしました。それぞれ確信度が高いときのみ判定し、それ以外の場合は、わからないと判定することにしました。

条件1:位置

  • 同じ:骨格の手首の確信度が一定値より高く、手首の座標と手の座標の距離が、一定値より小さい
  • 違う:骨格の手首の確信度が一定値より高く、手首の座標と手の座標の距離が、一定値より大きい
  • わからない:上記以外

条件2:方向

  • 同じ:骨格の手首と肘の確信度が両方一定値より高く、肘→手首のベクトルと、手首のバウンディングボックスの方向のベクトルがなす角度が、一定値より小さい
  • 違う:骨格の手首と肘の確信度が両方一定値より高く、肘→手首のベクトルと、手首のバウンディングボックスの方向のベクトルがなす角度が、一定値より大きい
  • わからない:上記以外

条件3:左右

  • 同じ:左右の判定の確信度高く、手首と手の左右が同じ
  • 違う:左右の判定の確信度高く、手首と手の左右が異なる
  • わからない:上記以外

処理手順

上記の判定方法を利用し、検出された全骨格の手首と全手の組み合わせの中から、対応付けられる組み合わせを探索してきます。

以降、下のように表記します。l_whが最終的な出力です。3値はTrue, False, Noneを指します。

[latex]W:検出された骨格の手首の集合[/latex]

[latex]H :検出された手の集合[/latex]

[latex]j_{pos,w,h}:骨格の手首w( \in W)が手h(\in H)の位置が同じかどうか。3値[/latex]

[latex]j_{dir,w,h}:骨格の手首w(\in W)が手h(\in H)の方向が同じかどうか。3値[/latex]

[latex]j_{lr,w,h}:骨格の手首w(\in W)が手h(\in H)の左右が同じかどうか。3値[/latex]

[latex]l_{w,h}:骨格の手首w(\in W)と手h(\in H)が対応付けられるかどうか。3値[/latex]

ステップ0:初期化

すべての組み合わせを不明とする

[latex]l_{w,h}=None\ \ \forall w \in W\ \ \forall h \in H[/latex]

ステップ1:ありえない組み合わせを除く

3つの条件のうち1つでも「違う」であれば、その骨格と手の組み合わせを除く

[latex]l_{w,h}=False \ \ {\rm if} \ \ j_{pos,w,h}==False \ \ || \ \ j_{dir,w,h}==False \ \ || \ \ j_{lr,w,h}==False[/latex]

[latex]\forall w \in W \ \ \forall h \in H[/latex]

ステップ2:確実な組み合わせを確定する

3つの条件がすべて「同じ」であれば、その骨格と手の組み合わせは確定する

[latex]l_{w,h}=True \ \ {\rm if} \ \ j_{pos,w,h}==True \ \ \&\& \ \ j_{dir,w,h}==True \ \ \&\& \ j_{lr,w,h}==True \ \ [/latex]

[latex]\forall w \in W \forall h \in H[/latex]

ステップ3:不確実な組み合わせをさらに探索する

ステップ3-1:グループ化する

共通しているノードを持つ組み合わせを、グループとしてまとめる

ステップ3-2:組み合わせが満たしている条件の数を数える

3つの条件のうち、骨格の手首と手の組み合わせが満たしてしてる条件の数をカウントする

ステップ3-3:条件を満たしている数が多いものから確定していく

グループ内で条件を満たしているものが多いものから確定させます。

今回は、満たしている条件の数が同じ組み合わせがあったら、最初に取り出されたものを先に確定させました。また、満たしている条件数が1以下の場合は、確定させませんでした。

結果:動画で確認

上記の処理の結果、下の動画のようになりました。対応付けられた手は骨格と同じ色で、対応付けできない手は黒で、不確定なものは灰色で、それぞれ描画しました。

分析:問題点

動画を見てみると、おおよそうまく対応づけできているように見えますが、ところどころ失敗しているところが見て取れます。

  • 問題点1:骨格検出がミスっている

    骨格検出で大きくハズレた位置に検出されているため、対応付けができるか不確定のものとして判定されています。

  • 問題点2:手が重なって検出されている

    手の検出で同じ位置に重なって検出されているため、片方は対応付けられ、もう片方は不確定なものとして判定されています。

  • 問題点3:左右の判定が逆になっている

    骨格の左手首であるにも関わらず、手の左右の判定が右手になっているため、対応付けができないと判定されています。

修正:左右の判定を除く

上記の問題点のうち、問題点1と2に関しては、検出側で解決する方が良さそうです。

問題点3に関しては、逆の判定によって精度が落ちているだけ(正しい判定によって、精度が上がっていることはなさそう)に見えました。そこで、対応付けの手順のうち、左右の判定は除いて同様の処理を行いました。

結果は下の動画のようになりました。

問題点3にあったように、左右の判定が間違っている事によって、対応付けできないという判定が出ることはなくなりました。また、別の判定にも大きな影響は出ていないように見て取れます。こちらの方が良さそうです。

まとめ

今回は、Cubemos SDKとMediaPipeで検出した、骨格と手を対応付ける方法を考え、動画で様子を確認しました。検出精度を考慮すると、対応付けの条件として、位置・方向・左右の3つのうち、左右は除くと良さそうなことがわかりました。

余談

本処理やステップ3は、組み合わせの最適化問題として解くこともできそうだなと思いました。目的関数の設計は少し工夫する必要がありそうです。