【Hands/第1回】

Hands

今回はHandsに触れてみようと思います。

MoveNetと似ている箇所が多いので、安心して取り組んでください。

Handsとは

Handsは、手の位置を検出し、各関節のランドマーク(指の位置)をリアルタイムで追跡するための機械学習モデルです。これは、カメラ映像や画像から手の形状や動きを捉え、最大で21個のランドマークポイントを特定します。

サイトにHandsを組み込む

最初に「Hands-1」というフォルダを作成しましょう。ここが作業場になります。そしてその配下に「index.html」「script.js」を作成します。

次にそれぞれのファイルのコードを記述していきます。下にあるコードをコピペして動作確認をします。

index.html

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <!-- 3つのライブラリを読み込む -->
    <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js"></script>

    <style>
        /*ビデオとキャンバスを重ねるためのスタイル*/
        #input, #output {
            position: absolute;
            top: 0;
            left: 0;
        }
        #input {
            z-index: 1;
        }
        #output {
            z-index: 2;
        }
    </style>
</head>

<body>
    <div class="container">
        <!-- Webカメラの映像(入力) -->
        <video id="input"autoplay"></video>
        <!-- 認識した手の形状を可視化した映像(出力) -->
        <canvas id="output" width="600" height="400"></canvas>
    </div>
    <script src="script.js"></script>
</body>

</html>

script.js

const video = document.getElementById('input');
const canvas = document.getElementById('output');
const ctx = canvas.getContext('2d');
    
const config = {
    locateFile: file => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`
};

const hands = new Hands(config);
    
const camera = new Camera(video, {
    onFrame: async () => {
        await hands.send({ image: video });
    },
    width: 600,
    height: 400
});
    
camera.start();
    
hands.setOptions({
    maxNumHands: 5,
    modelComplexity: 1,
    minDetectionConfidence: 0.5,
    minTrackingConfidence: 0.5
});
    
hands.onResults(results => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    if (results.multiHandLandmarks) {
        results.multiHandLandmarks.forEach(marks => {
            // 緑色の線で骨組みを可視化
            drawConnectors(ctx, marks, HAND_CONNECTIONS, { color: '#0f0' });
            // 赤色でランドマークを可視化
            drawLandmarks(ctx, marks, { color: '#f00' });
        })
    }
});

index.htmlを開いてください。うまくいくと、自分の手の上に赤い点と緑の線が表示されているはずです。

コード解説

hands.onResults(results => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    if (results.multiHandLandmarks) {
        results.multiHandLandmarks.forEach(marks => {
            // 緑色の線で骨組みを可視化
            drawConnectors(ctx, marks, HAND_CONNECTIONS, { color: '#0f0' });
            // 赤色でランドマークを可視化
            drawLandmarks(ctx, marks, { color: '#f00' });
        })
    }
});

script.jsのresultsには画像を解析をした結果が入っています。results.multiHandLandmarksは手が認識できた場合にデータが入っています。

では、どのようなデータが入っているか見てみましょう。drawLandmarksの下に以下のコードを追加してください。

console.log(marks);

console.logはコンソール(出力画面)に出力します。print文と同じようなものです。F12を押し(右クリックをした後に検証を押し)Elementsの横にあるConsoleを押すと出力を見ることができます。

それぞれの数字は以下の画像に対応しています。

座標は0~1で取得されます。

課題

今回の課題は気づくことができれば簡単です。よく観察してみましょう。

  1. 緑の線を赤色に、赤色の点を緑色に変更しなさい。
  2. 人差し指の指先の座標によってコンソールに以下の文章を表示しなさい。
    y座標が0.5以下の場合「上から来るぞ!気をつけろ!」
    y座標が0.5より大きい場合「なんだこの階段はぁ!」
    デスクリムゾンネタです。分からない人はすみません。
  1. marksは配列です。
  2. x、y、zはオブジェクトです。
drawConnectors(ctx, marks, HAND_CONNECTIONS, { color: '#f00' });
drawLandmarks(ctx, marks, { color: '#0f0' });

人差し指の指先は8です。なので、marks[8]と入力すると人差し指の指先の座標を取得することができます。また、y座標のみを取得するのでmarks[8].yと入力すると人差し指の指先のy座標を取得することができます。

if(marks[8].y<=0.5){
    console.log("上から来るぞ!気をつけろ!");
}else{
    console.log("なんだこの階段はぁ!");
}