43
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RUNTEQAdvent Calendar 2021

Day 6

TensorFlow.js + MoveNetでジョジョ立ちの姿勢推定を行う

Last updated at Posted at 2021-12-05

はじめに

TesorFlow.jsとMoveNetを用いて姿勢推定を行う方法について解説します。
以下のツイート画像のようにブラウザ上で姿勢推定ができるようになります。

姿勢推定とは

姿勢推定とは画像や映像中の人物の関節(=キーポイント)を座標データで検出する技術です。
近年ではディープラーニングを用いた姿勢推定が行われています。

TensolFlow.jsとは

  • TensorFlow.jsはブラウザやNode.js上で機械学習モデルのトレーニングやデプロイを行うためのJavaScriptライブラリです。
  • 学習済みモデルが公開されており、それらを利用して物体検出や姿勢推定を行うことができます。
  • 今回はMoveNetという学習済みモデルを利用して姿勢推定を行います。

MoveNetとは

  • MoveNetはTensorFlow公式によって公開されている姿勢推定用のモデルです。
  • 画像や動画を入力として身体の17のキーポイントを高速かつ正確に検出することができます。
  • LightningとThunderの2種類が用意されています。
    • 前者はレイテンシーが重要なアプリケーションを対象としています。
    • 後者は高精度を必要とするアプリケーションを対象としています。
  • 詳細な仕様はTensolFlowの公式ブログを参照してください。
  • https://blog.tensorflow.org/2021/05/next-generation-pose-detection-with-movenet-and-tensorflowjs.html

MoveNetの導入

今回はCDNを用いてMoveNetを導入します。

html
<html>
  <head>
   <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/pose-detection"></script>
  </head>
</html>

姿勢推定の実行

姿勢推定の対象となるimg要素を用意します。

html
<html>
  <body>
    <img id="img" src="./xxx.png" width="300px" />
  </body>
<html>

用意したimg要素に対して姿勢推定を実行します。

javascript
document.addEventListener('DOMContentLoaded', async() => {
  
  // 検出器を生成
  const detector = await poseDetection.createDetector(poseDetection.SupportedModels.MoveNet)
 
  // img要素を取得する 
  const imageElement = document.getElementById('img')
  
  // 姿勢推定を実行する
  const poses = await detector.estimatePoses(imageElement)
  
  // 姿勢推定結果を出力する
  console.log(poses[0].keypoints)
  
})

コンソール上に以下のような姿勢推定結果が出力されました!

[
  {
    "y": 107.0774033665657,
    "x": 170.3357219696045,
    "score": 0.672927975654602,
    "name": "nose"
  },
  {
    "y": 108.07683914899826,
    "x": 179.52157258987427,
    "score": 0.5579419136047363,
    "name": "left_eye"
  },
  {
    "y": 103.95387262105942,
    "x": 176.25893354415894,
    "score": 0.44960924983024597,
    "name": "right_eye"
  },
  {
    "y": 127.98979580402374,
    "x": 193.1210696697235,
    "score": 0.5600013136863708,
    "name": "left_ear"
  },
  {
    "y": 117.00804084539413,
    "x": 183.74462127685547,
    "score": 0.38861674070358276,
    "name": "right_ear"
  },
  {
    "y": 170.36895751953125,
    "x": 197.4759578704834,
    "score": 0.5543217658996582,
    "name": "left_shoulder"
  },
  {
    "y": 140.3295934200287,
    "x": 146.19784355163574,
    "score": 0.4909017086029053,
    "name": "right_shoulder"
  },
  {
    "y": 212.7191960811615,
    "x": 221.3673233985901,
    "score": 0.5364058613777161,
    "name": "left_elbow"
  },
  {
    "y": 128.0842810869217,
    "x": 94.87710893154144,
    "score": 0.7699795365333557,
    "name": "right_elbow"
  },
  {
    "y": 234.46808457374573,
    "x": 183.3907663822174,
    "score": 0.6135048866271973,
    "name": "left_wrist"
  },
  {
    "y": 140.4405564069748,
    "x": 127.41105258464813,
    "score": 0.39565128087997437,
    "name": "right_wrist"
  },
  {
    "y": 254.5135736465454,
    "x": 152.78272032737732,
    "score": 0.4956844747066498,
    "name": "left_hip"
  },
  {
    "y": 248.24934601783752,
    "x": 142.50272512435913,
    "score": 0.6491193175315857,
    "name": "right_hip"
  },
  {
    "y": 315.92750549316406,
    "x": 125.6029486656189,
    "score": 0.4037594497203827,
    "name": "left_knee"
  },
  {
    "y": 309.03390645980835,
    "x": 165.5895173549652,
    "score": 0.5492032766342163,
    "name": "right_knee"
  },
  {
    "y": 346.2807238101959,
    "x": 122.5919097661972,
    "score": 0.055652350187301636,
    "name": "left_ankle"
  },
  {
    "y": 343.7341272830963,
    "x": 181.74968361854553,
    "score": 0.08864666521549225,
    "name": "right_ankle"
  }
]

姿勢推定の結果はオブジェクトの配列として出力されます。
各オブジェクトは以下ような構成になります。

プロパティ名
name 検出部位の名前
x 検出部位のx座標
y 検出部位のy座標
score 検出結果の確からしさ

出力された姿勢推定結果を用いれば、以下のようなことができます。
姿勢推定結果を表示する
画像中の人物に骨格線を重畳する
特定のポーズをとっているか判断する

今回は姿勢推定結果をテーブルに表示してみようと思います。

姿勢推定結果をテーブルに表示する

結果を表示するためのtable要素を用意します。

html
    <table>
      <tr>
        <th>部位</th>
        <th>スコア(%)</th>
        <th>x座標</th>
        <th>y座標</th>
      </tr>
      <tr>
        <td></td>
        <td id="nose-score">-</td>
        <td id="nose-x">-</td>
        <td id="nose-y">-</td>
      </tr>
      <tr>
        <td>左目</td>
        <td id="left-eye-score">-</td>
        <td id="left-eye-x">-</td>
        <td id="left-eye-y">-</td>
      </tr>
      <tr>
        <td>右目</td>
        <td id="right-eye-score">-</td>
        <td id="right-eye-x">-</td>
        <td id="right-eye-y">-</td>
      </tr>
      <tr>
        <td>左耳</td>
        <td id="left-ear-score">-</td>
        <td id="left-ear-x">-</td>
        <td id="left-ear-y">-</td>
      </tr>
      <tr>
        <td>右耳</td>
        <td id="right-ear-score">-</td>
        <td id="right-ear-x">-</td>
        <td id="right-ear-y">-</td>
      </tr>
      <tr>
        <td>左肩</td>
        <td id="left-shoulder-score">-</td>
        <td id="left-shoulder-x">-</td>
        <td id="left-shoulder-y">-</td>
      </tr>
      <tr>
        <td>右肩</td>
        <td id="right-shoulder-score">-</td>
        <td id="right-shoulder-x">-</td>
        <td id="right-shoulder-y">-</td>
      </tr>
      <tr>
        <td>左肘</td>
        <td id="left-elbow-score">-</td>
        <td id="left-elbow-x">-</td>
        <td id="left-elbow-y">-</td>
      </tr>
      <tr>
        <td>右肘</td>
        <td id="right-elbow-score">-</td>
        <td id="right-elbow-x">-</td>
        <td id="right-elbow-y">-</td>
      </tr>
      <tr>
        <td>左手首</td>
        <td id="left-wrist-score">-</td>
        <td id="left-wrist-x">-</td>
        <td id="left-wrist-y">-</td>
      </tr>
      <tr>
        <td>右手首</td>
        <td id="right-wirst-score">-</td>
        <td id="right-wirst-x">-</td>
        <td id="right-wirst-y">-</td>
      </tr>
      <tr>
        <td>左腰</td>
        <td id="left-hip-score">-</td>
        <td id="left-hip-x">-</td>
        <td id="left-hip-y">-</td>
      </tr>
      <tr>
        <td>右腰</td>
        <td id="right-hip-score">-</td>
        <td id="right-hip-x">-</td>
        <td id="right-hip-y">-</td>
      </tr>
      <tr>
        <td>左膝</td>
        <td id="left-knee-score">-</td>
        <td id="left-knee-x">-</td>
        <td id="left-knee-y">-</td>
      </tr>
      <tr>
        <td>右膝</td>
        <td id="right-knee-score">-</td>
        <td id="right-knee-x">-</td>
        <td id="right-knee-y">-</td>
      </tr>
      <tr>
        <td>左足首</td>
        <td id="left-ankle-score">-</td>
        <td id="left-ankle-x">-</td>
        <td id="left-ankle-y">-</td>
      </tr>
      <tr>
        <td>右足首</td>
        <td id="right-ankle-score">-</td>
        <td id="right-ankle-x">-</td>
        <td id="right-ankle-y">-</td>
      </tr>
    </table>

テーブルのスタイルを定義します。

css
img {
  float: left;
}

table {
  text-align: center;
  border-collapse: collapse;
}

table th, table td {
  border: solid 1px black;
}

テーブルに検出結果を描画するための処理を追加します。

javascript
const keypointIds = ['nose', 'left-eye', 'right-eye', 'left-ear', 'right-ear',
                  'left-shoulder', 'right-shoulder', 'left-elbow', 'right-elbow',
                  'left-wrist', 'right-wirst', 'left-hip', 'right-hip',
                 'left-knee', 'right-knee', 'left-ankle', 'right-ankle']

keypointIds.forEach(function(item, index){
  keypoint = poses[0].keypoints[index]
  document.getElementById(item + '-score').innerHTML = Math.floor(keypoint.score * 100)
  document.getElementById(item + '-x').innerHTML = Math.floor(keypoint.x)
  document.getElementById(item + '-y').innerHTML = Math.floor(keypoint.y)
})

以下のように、姿勢推定結果がテーブルに表示されました!
77e2c2b6-577d-d654-7404-b2b527ecd474.png

実装した内容については、CodePen上でも公開しています。
必要に応じてご参照ください!

おわりに

最後までご覧いただきありがとうございました!

現在、姿勢推定を用いたジョジョ立ちの検定アプリを開発中です。
Twitterにて開発の進捗を発信していますので、もし良ければフォローお願いいたします!
https://twitter.com/koheiyamamoto26

参考サイト

43
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?