AIを活用したWebアプリ③・ハンドポーズの検出
CodePenの準備
CodePenにアクセスして,Penを作成し,タイトルを設定しましょう. Penのタイトルは「Chapter12」に設定しましょう.
前回と同様にp5.jsとml5.jsを導入しましょう.
https://unpkg.com/ml5@latest/dist/ml5.min.js
ハンドポーズの検出
ml5.jsとp5.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); // 円を描画
}
}
}
drawLightSaber()
は検出された手の状態に合わせてライトセーバを描画します.
親指の第1関節の座標と,小指の先端の座標から,ライトセーバの傾きを計算しています.
ライトセーバの長さと傾きから,ライトセーバーの先端の座標を求め,
line()
で線の太さが'30’の直線を描画しています.
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); // ライトセーバを描画
}
}
}
アプリの確認
See the Pen Chapter12 by Naoto Mukai (@nmukai) on CodePen.
課題
ライトセーバを自由に改良してみてください. 課題を完成させたら,Penの ZIPファイル と リンク を提出してください. 提出方法は初回のWebアプリの開発を参考にしてください.