👌
MediaPipe Handpose Javascript版
概要
- Googleの機械学習を用いたライブラリMediaPipeが2023年更新されました
- 以前のサンプルは廃止されるようなので、新しいライブラリの使い方について解説します
- 今回は手を認識できるHandposeのJavascript版を扱います
- 顔認識(Face landmark detection)や全身トラッキング(Pose landmark detection)も今回のサンプルの一部を書き換えると使うことができます
公式サイト
今回のコードはこちらのgithubにアップロードしてあります
またLive Demoで体験することもできます
学習済みのモデルデータを入手する
MediaPipeでは学習済みの.task
ファイルを用意する必要があります
Handposeの場合は下記の場所からダウンロードすることができます
ダウンロードしたファイルはアクセスできる場所にアップロードしておきます
HTMLとJavascriptを用意する
HTML側では必要なライブラリ読み込みを行います
また、Webカメラを使うためのvideoタグと結果を出力するためのcanvasを用意しておきます
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<head>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
<script type="modele" src="https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/vision_bundle.js"crossorigin="anonymous"></script>
</head>
<title>Mediapipe Handpose Example</title>
<!-- The website JavaScript file -->
<script type="module" src="./main.js" defer></script>
</head>
<body>
<video id="input_video" style="display:none"></video>
<canvas id="output_canvas" width="1280px" height="720px"></canvas>
</body>
</html>
Javascript側ではWebカメラの起動し、Webカメラの映像をMediapipeに流し込みます
main.js
import {
HandLandmarker,
FilesetResolver
} from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0";
import Stats from 'https://cdnjs.cloudflare.com/ajax/libs/stats.js/r17/Stats.min.js';
const init = async () =>{
const stats = new Stats();
document.body.appendChild(stats.dom);
const video = document.getElementById("input_video");
const canvasElement = document.getElementById("output_canvas");
const canvasCtx = canvasElement.getContext("2d");
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices
.getUserMedia({ video: true })
.then(function (stream) {
video.srcObject = stream;
video.play();
})
.catch(function (error) {
console.error("Error accessing the camera: ", error);
});
} else {
alert("Sorry, your browser does not support the camera API.");
}
const vision = await FilesetResolver.forVisionTasks(
// path/to/wasm/root
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
);
const handLandmarker = await HandLandmarker.createFromOptions(
vision,
{
baseOptions: {
modelAssetPath: "./hand_landmarker.task", //.taskファイルを指定する
delegate: "GPU" //CPU or GPUで処理するかを指定する
},
numHands: 2 //認識できる手の数
});
await handLandmarker.setOptions({ runningMode: "video" });
let lastVideoTime = -1;
const renderLoop = () => {
canvasElement.width = video.videoWidth;
canvasElement.height = video.videoHeight;
let startTimeMs = performance.now();
if (video.currentTime > 0 && video.currentTime !== lastVideoTime) {
const results = handLandmarker.detectForVideo(video,startTimeMs);
lastVideoTime = video.currentTime;
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
canvasCtx.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
if (results.landmarks) {
for (const landmarks of results.landmarks) {
drawConnectors(canvasCtx, landmarks, HAND_CONNECTIONS, {
color: "#00FF00",
lineWidth: 5
});
drawLandmarks(canvasCtx, landmarks, { color: "#FF0000", lineWidth: 2 });
}
}
canvasCtx.restore();
}
requestAnimationFrame(() => {
stats.begin();
renderLoop();
stats.end();
});
}
renderLoop();
}
init();
renderLoop
関数で毎フレームWebカメラの画像をMediapipeに流し込み結果を取得します
その結果をCanvasに書き込んでいます
const results = handLandmarker.detectForVideo(video,startTimeMs);
のresultをログ出力してみると手の各ランドマークの座標が出力されます
landmarks
に各ランドマークの座標が入っています
配列の何番目がどの部位に当たるかは画像を参照してください
{
"landmarks": [
[
{
"x": 0.7927062511444092,
"y": 0.7218624353408813,
"z": 3.318519077311066e-7
},
...
{
"x": 0.8173002004623413,
"y": 0.34138065576553345,
"z": -0.04457491636276245
}
],
[
{
"x": 0.21455314755439758,
"y": 0.6943681240081787,
"z": 4.5197063514024194e-7
},
...
{
"x": 0.15950265526771545,
"y": 0.3301934003829956,
"z": -0.07225233316421509
}
]
],
"worldLandmarks": [
[
{
"x": 0.0241236574947834,
"y": 0.09063709527254105,
"z": -0.0027828216552734375
},
...
{
"x": 0.02904096245765686,
"y": -0.053392037749290466,
"z": -0.042449951171875
}
],
[
{
"x": -0.02501724101603031,
"y": 0.08835680037736893,
"z": 0.0098114013671875
},
...
{
"x": -0.04410901665687561,
"y": -0.05151066184043884,
"z": -0.034423828125
}
]
],
"handednesses": [
[
{
"score": 0.9588623046875,
"index": 1,
"categoryName": "Right",
"displayName": "Right"
}
],
[
{
"score": 0.98583984375,
"index": 0,
"categoryName": "Left",
"displayName": "Left"
}
]
]
}
サイトにデプロイする
Webカメラを使うのでhttpsが使えるサイトにデプロイする必要があります。
Glitchやnetlifyが便利です
トラブルシューティング
映像がちらつく場合はdelegate: "GPU"
の部分をdelegate: "CPU"
に書き換えてみてください
実行速度は遅くなりますが、安定するようになります
Discussion