どうも、まさとらん(@0310lan)です!
今回は、高度な機械学習により手のカタチはもちろんのこと、指先1本の形状までしっかり認識できる「MediaPipe Hands」をご紹介します。
準備するのはHTMLファイル1つだけです。あとはごく普通のJavaScriptでわずかなコードを書くだけなので、PC・スマホ問わずさまざまなブラウザ上で動作します。
基本的な使い方を覚えると、例えばハンドジェスチャーで処理を実行したり、VR / ARを組み合わせてみたり…など、可能性は無限に広がります。
とにかく手軽に試せる内容なので、ご興味ある方はぜひ参考にしてください!
【 MediaPipe Hands 】
■HTMLファイルを準備する
それでは、まず最初に今回のチュートリアルでベースになるHTMLファイルを1つ準備しましょう。
お好きなテキストエディタで構いませんので、index.htmlという名称で次のようなHTMLを作成してください。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <!-- 3つのライブラリを読み込む --> <script src=""></script> <script src=""></script> <script src=""></script> </head> <body> <div class="container"> <!-- Webカメラの映像(入力) --> <video id="input"></video> <!-- 認識した手の形状を可視化した映像(出力) --> <canvas id="output" width="600" height="400"></canvas> </div> <button id="start">start</button> <button id="stop">stop</button> <script> // ここにプログラムを記述する </script> </body> </html>
説明を簡単にするため、必要最小限のタグで構成しています。
ポイントはvideoタグとcanvasです。カメラに手をかざして形状を認識させるのにvideoタグが必要なのと、その映像を機械学習で認識させた結果をcanvasで可視化します。
この仕組みを手軽に実現させるため、今回はカスタマイズしやすい機械学習ソリューションを提供する「MediaPipe(Hands)」を利用します。そこで、必要になる関連ライブラリを読み込んでおきましょう。
さきほどのHTML内に空っぽのscriptタグが3つあるので、src属性値に以下のURLをそれぞれ挿入してください。
https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js
「camera_utils.js」はJavaScriptでカメラの操作を簡単にしてくれるもので、「drawing_utils.js」は認識した結果を可視化するためのライブラリです。「hands.js」は手の形状認識するための本体ライブラリとなります。
ここまでの作業で事前準備は完了です。
■JavaScriptで初期設定を行う
それでは、JavaScriptで実際の処理をプログラミングしていきましょう。
まずはカメラからの映像と、結果を出力するためのcanvasを取得しておきます。
const video = document.getElementById('input'); const canvas = document.getElementById('output'); const ctx = canvas.getContext('2d');
hands.jsを利用する際に、いくつか関連ファイルを読み込む必要があります。そこで、インスタンスを生成するときに次のようなオプションを記述してください。
const config = { locateFile: file => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}` }; const hands = new Hands(config);
configに記述したURLから自動的にファイルが読み込まれるようになります。
次にカメラの設定をしておきましょう。設定する内容としては、hands.jsからカメラの映像を使えるようにすることです。
const camera = new Camera(video, { onFrame: async () => { await hands.send({image: video}); }, width: 600, height: 400 });
Cameraのインスタンスを作るときにvideoタグの要素を指定し、hands.send()でそのビデオ要素を設定すればOKです。カメラからの映像はcanvasのサイズに合わせて600×400にしましたが、お好みで調整できます。
また、hands.jsの基本的な設定オプションが用意されているので補足しておきます。次のようにsetOptions()から任意の項目をカスタマイズできます。
hands.setOptions({ maxNumHands: 2, modelComplexity: 1, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5 });
他にもありますが、よく使われるのは上記4つだと思います。それぞれの項目は以下のような意味があります。
- maxNumHands:検出する手の最大数
- modelComplexity:ランドマーク検出精度(0か1)
- minDetectionConfidence:手を検出するための信頼値(0.0~1.0)
- minTrackingConfidence:ランドマーク追跡の信頼度(0.0~1.0)
ランドマークを簡単に説明しておきます。「MediaPipe Hands」は1つの手から21箇所のポイントを検出でき、そのポイントを利用して細かい形状を認識できる仕組みになっています。
このポイントをランドマークと呼んでいるわけですが、形状認識の重要な項目なので関連するオプションは慎重に行う必要があります。ただし、上記で設定している値(デフォルト値)は、そのままでも大抵のケースで困ることはないと思います。
最後に、HTMLへ記述していた「スタートボタン」と「ストップボタン」を動作させるため、次のように記述しておきましょう。
document.getElementById('start') .addEventListener('click', () => camera.start()); document.getElementById('stop') .addEventListener('click', () => camera.stop());
それぞれクリックイベント処理を作りました。形状認識のスタート&ストップはstart() / stop()をそれぞれ実行するだけなので簡単ですね。
さて、ここまでのコードで初期設定は完了です。
あとは認識された手の形状を可視化して、canvasに出力する処理をつくってみましょう。
■検出した手の形状を可視化する
まず最初に、手の形状を認識した結果を取得する必要があります。これは、とても簡単な方法が提供されており、onResults()メソッドから結果を取得できる仕組みがあります。
hands.onResults(results => { // 変数resultsに認識結果が格納されている });
引数のresultsに認識された結果がすべて格納されています。そこで、resultsの中身をコンソールログで確認すると、multiHandLandmarksという配列データがあります。
ここに認識された21箇所のランドマークデータが格納されているわけです。
このデータをさらに詳しく見てみると、それぞれのランドマークがある位置をX・Y・Zの座標データで格納しているのが分かります。
この座標データを利用して可視化を行ってみましょう。onResults()メソッドの中身は次のようになります。
hands.onResults(results => { ctx.clearRect(0,0,canvas.width,canvas.height); ctx.drawImage(results.image,0,0,canvas.width,canvas.height); if(results.multiHandLandmarks) { results.multiHandLandmarks.forEach(marks => { // 可視化の処理 }) } });
multiHandLandmarksのデータをforEach()で1つずつ取り出しています。そして、各データを利用して認識した手の形状を骨組みとして可視化し、ランドマークとなるポイントも描画します。
これは提供されている2つの関数を使えば簡単に実現します。drawConnectors()とdrawLandmarks()という関数で、次のように記述します。
if(results.multiHandLandmarks) { results.multiHandLandmarks.forEach(marks => { // 緑色の線で骨組みを可視化 drawConnectors(ctx, marks, HAND_CONNECTIONS, {color: '#0f0'}); // 赤色でランドマークを可視化 drawLandmarks(ctx, marks, {color: '#f00'}); }) }
ここまでプログラムできたら、ブラウザから実行してみましょう。
スタートボタンをクリックしてしばらくすると、手の形状をトラッキングして以下のように可視化してくれます。
PCだけでなく、スマホのブラウザからでも実行できるのでぜひ試してみましょう。(※PC・スマホの推奨ブラウザはこちらのリンクを参照)
今回はHTMLファイル1つだけで作成したので、例えば静的ファイルとしてNetlifyやGitHub Pageなどを利用して公開することも可能です。
最後に、取得した21箇所の座標データについてもう少し補足しておきます。
X・Y座標に関しては、画像に対して正規化されているので0から1の範囲に収まるように調整されています。例えば、人差し指の頂点(8番)のランドマークだけを見ると、以下のようになります。
X方向の場合だと左端が0で右端が1になります。Y方向は上端が0で下端が1という関係です。この仕組みが21箇所のランドマークで調整されているわけです。
例えば、人差し指を親指に近づけるとY方向の座標で変化を検出できます。
JavaScriptから人差し指のY座標だけを取得できるので、この変化をトリガーにして何かイベント処理を実行することも可能なわけです。
このようにMediaPipe Handsを利用すると手軽に形状認識を試せるので、ぜひみなさんもユニークなアイデアを活用してみてください。
本記事で作成したサンプルデモのソースコードは、以下のリンクからすべて閲覧できますので合わせて参考にしてください。
<参考リンク>
SILVER BULLET 戦場のエンジニア 銀の弾丸コードを打て公開中!
paizaでは先日プログラミングゲーム『SILVER BULLET 戦場のエンジニア 銀の弾丸コードを打て』を公開いたしました!
新種の軍事プログラミングで戦場に【銀の弾丸】を打ち込め!
世界中でIT化が加速し、プログラミングも生存のために重要なスキルのひとつとなった近未来の戦場。ミリタリーインテリジェンスから最前線へと飛び出した我々は、軍事プログラミングを駆使して自軍を勝利へと導く任務を負った。
平和をもたらす【銀の弾丸】を見つけることはできるのか!?
プログラミング問題を解くと拠点を制圧できるだけでなく、新たな武器や装備を獲得できます。武器イラストは、エアソフトガンメーカーの東京マルイの製品をベースにデザイン。実在するアイテムを装備することもできます。お気に入りの装備で、新たな拠点制圧に向かいましょう。
各拠点を制圧するには、それぞれに設定されているプログラミング問題をクリアしなければなりません。各拠点には問題の難度に応じてS、A、B、C、Dのランクが付与されているので、自分のスキルに応じて挑戦する拠点を選びましょう。拠点を制圧(クリア)したり、特定のミッションをクリアしたりすると、アイテムを獲得できます。
アイテムを充実させて、自分だけの装備を作り上げ、さらなる拠点の制圧を目指しましょう!
■まとめ
今回は誰でも手軽に高度な手の形状認識を試せるライブラリをご紹介しました。
MediaPipeが提供している機械学習ソリューションは、他にも顔や表情を認識したり、体の骨格やモノの認識ができたりなど、さまざまなツールを提供しています。JavaScriptで実現できることも多いので、これらも合わせて使えるようになると可能性はもっと膨らみます。
ぜひみなさんもこれらのツールを活用して、楽しいWebアプリを開発してみましょう!
「paizaラーニング」では、未経験者でもブラウザさえあれば、今すぐプログラミングの基礎が動画で学べるレッスンを多数公開しております。
詳しくはこちら
そしてpaizaでは、Webサービス開発企業などで求められるコーディング力や、テストケースを想定する力などが問われるプログラミングスキルチェック問題も提供しています。
スキルチェックに挑戦した人は、その結果によってS・A・B・C・D・Eの6段階のランクを取得できます。必要なスキルランクを取得すれば、書類選考なしで企業の求人に応募することも可能です。「自分のプログラミングスキルを客観的に知りたい」「スキルを使って転職したい」という方は、ぜひチャレンジしてみてください。
詳しくはこちら