AIを活用したWebアプリ③・ハンドポーズの検出

Image from Gyazo

CodePenの準備

CodePenにアクセスして,Penを作成し,タイトルを設定しましょう. Penのタイトルは「Chapter12」に設定しましょう.

https://codepen.io/

Image from Gyazo

前回と同様にp5.jsとml5.jsを導入しましょう.

https://unpkg.com/ml5@latest/dist/ml5.min.js

Image from Gyazo

ハンドポーズの検出

ml5.jsp5.jsを 組み合わせて ハンドポーズの検出 にチャレンジします. 前回は画像から顔を検出しましたが,今回はWebカメラの映像からハンドポーズを検出します. Webカメラが利用可能なPC環境で実装してください.

HTMLに下記のコードを入力してください. 今回はp5.jsを利用するため,h1でタイトルのみを記述します.

<h1>ハンドポーズ認識</h1>

まずはWebカメラで撮影された映像を,createCapture() でキャプチャします. キャプチャされたフレームは通常の画像と同じように扱うことができます.

let video;

function setup(){
  createCanvas(640, 480);

  // ビデオのキャプチャ
  video = createCapture(VIDEO);
  video.size(width, height);
  video.hide();

}

function draw() {
  // キャプチャした画像を表示
  image(video, 0, 0, width, height);
}

ml5.jsのハンドポーズ検出は MdiaPipe Handpose から移植されています. 手の指や関節の21点のランドマークを検出できます.

ハンドポーズ検出器を生成するには,ml5.handpose()を利用します. 第一引数は,キャプチャされたフレームvideo, 第二引数はモデルがロードされたタイミングで実行される無名関数です.

ハンドポーズのイベントはhandpose.on()で検出します. 第一引数は"predict", 第二引数は検出結果を処理する無名関数を設定します. ここでは,検出結果が格納されたresultsを,predictionsに代入します. このpredictionsは配列であることに注意してください.

let video;
let handpose;
let predictions = [];

function setup(){
  createCanvas(640, 480);

  // ビデオのキャプチャ
  video = createCapture(VIDEO);
  video.size(width, height);
  video.hide();

  // ハンドポーズの検出器
  handpose = ml5.handpose(video, () => {
    console.log("Model Loaded");
  });

  // ハンドポーズの検出
  handpose.on("predict", results => {
    predictions = results;
  });

}

function draw() {
  // キャプチャした画像を表示
  image(video, 0, 0, width, height);

  // ランドマークの描画
  drawLandmarks();

  // ライトセーバの描画
  drawLightSaber();
}

検出結果を描画する関数として,drawLandmarks()drawLightSaber()を定義します. drawLandmarks()は,検出された全ての21点のランドマークを円とテキストで描画します. テキストは配列のインデックスです(インデックスは0〜20). 例えば親指の先端のインデックスは4です.

function drawLandmarks(){
  for(let prediction of predictions){
    for(let i=0; i<prediction.landmarks.length; i++){
      let point = prediction.landmarks[i];
      fill("#00ff00"); // 塗りつぶしの色
      noStroke(); // 線の色
      text(str(i), point[0], point[1]); // テキストを描画
      circle(point[0], point[1], 10); // 円を描画
    }
  }
}

Image from Gyazo

drawLightSaber()は検出された手の状態に合わせてライトセーバを描画します. 親指の第1関節の座標と,小指の先端の座標から,ライトセーバの傾きを計算しています. ライトセーバの長さと傾きから,ライトセーバーの先端の座標を求め, line()で線の太さが'30’の直線を描画しています.

Image from Gyazo

function drawLightSaber(){
  for(let prediction of predictions){
    for(let i=0; i<prediction.landmarks.length; i++){

      let [head_x, head_y] = prediction.landmarks[3]; // 親指の第1関節
      let [tail_x, tail_y] = prediction.landmarks[20]; // 小指の先端

	  let length = 300; // ライトセーバの長さ
      let angle = atan2((tail_y - head_y), (tail_x - head_x)); // ライトセーバの傾き

      let target_x = head_x - (length * cos(angle)); // ライトセーバの先端のx座標
      let target_y = head_y - (length * sin(angle)); // ライトセーバの先端のy座標

      stroke("#00ffff"); // 線の色
      strokeWeight(30); // 線の太さ
      line(head_x, head_y, target_x, target_y); // ライトセーバを描画

    }
  }
}

Image from Gyazo

アプリの確認

See the Pen Chapter12 by Naoto Mukai (@nmukai) on CodePen.

課題

ライトセーバを自由に改良してみてください. 課題を完成させたら,Penの ZIPファイルリンク を提出してください. 提出方法は初回のWebアプリの開発を参考にしてください.

愛知県名古屋市にある椙山女学園大学 文化情報学部 向研究室の公式サイトです. 専門は情報科学であり,人工知能やデータベースなどの技術要素を指導しています. この公式サイトでは,授業で使用している教材を公開すると共に, ベールに包まれた女子大教員のミステリアスな日常を4コマ漫画でお伝えしていきます. サイトに関するご意見やご質問はFacebookまたはTwitterでお問い合わせください.