ChatGPTの1周年とP5.js+Mediapipeを使用した手の指認識
ChatGPTの1周年について
2022年の11月30日にChatGPTがリリースされて1年が経過しました。この1年であらゆる環境にAIが活躍するようになり、世界が大きく変わった1年だったように思います。勿論このものすごいスピードでの変化は今も続いていますが、個人的にもChatGPTをはじめとするAIツールはいくつも試していますが、やはり1番利用しているのはChatGPTです。月3000円の有料課金は正直高いですが(2000円なら言う事なし!)、毎日使えば3000円以上の価値は十分にあると思います。逆に言うと月3000円を高いと思って使っていない人の方が断然多い訳で、そんな人たちと差を作るのなら今しかないとも思っています。様々な分野で活躍しますし、GPT-4はめちゃくちゃ賢いですから。絶対契約して毎日使うと人生が変わる気がしています。
P5.jsとMediapipeでの手の指認識について
今回はP5.jsとMediapipeで手の指認識をした話をさせてください。ChatGPTを使えば理解がメチャクチャ早くなる事例を含めての話です。
P5.js+Mediapipeを使いたかった訳
画像認識や手の認識には以前から興味があり、今回指の認識をしたいと思いました。高性能画像認識はGoogleが開発したMediapipe!これ本当にすごいですよね。P5.jsの前にRaspberry Pi 4で動かしていたのですが、処理が遅くて実用的ではありませんでした。そこで調べていくとGoogleのサンプル等をブラウザで試すとめちゃ軽い!JavaScriptの方がパフォーマンスが良いとわかったので、試してみる事に!
以下を参考にしました。
参考記事
P5.jsとindex.htmlとsketch.jsを記述する事で描画できるようになります。index.htmlの内容は、p5.jsとMediaPipe Hands APIを使用して、ブラウザでリアルタイムに手の追跡を行うウェブアプリケーションの基盤を構築するためのHTMLドキュメントです。主要な構成要素は以下の通りです:
- p5.jsライブラリの読み込み: クリエイティブコーディングに使われるJavaScriptライブラリで、描画やアニメーションのために利用されます。
- MediaPipeライブラリの読み込み: カメラ操作、UIコントロール、描画機能、そして手の追跡機能を提供するための複数のJavaScriptスクリプトが含まれています。
- スタイルシート: 外部のCSSファイルを参照し、ウェブページのスタイルを定義しています。
- ビデオ要素: カメラの映像を表示し、MediaPipeでの手の追跡に利用されるHTML要素です。
- JavaScriptスクリプト: p5.jsやMediaPipeを使った具体的な処理を記述するための外部スクリプトファイル(sketch.js)を読み込んでいます。
この構造により、ユーザーのブラウザ上でカメラを通じて手の動きを捉え、それをリアルタイムで画面上に描画するアプリケーションを実現しています。
sketch.jsによるMediaPipeの実装
const isFlipped = true;
let keypointsHand = [];
const videoElement = document.getElementsByClassName("input_video")[0];
videoElement.style.display = "none";
function onHandsResults(results) {
keypointsHand = results.multiHandLandmarks;
}
const hands = new Hands({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`;
},
});
hands.setOptions({
selfieMode: isFlipped,
maxNumHands: 1,
modelComplexity: 1,
minDetectionConfidence: 0.5,
minTrackingConfidence: 0.5,
});
hands.onResults(onHandsResults);
const camera = new Camera(videoElement, {
onFrame: async () => {
await hands.send({ image: videoElement });
},
width: 1280,
height: 720,
});
camera.start();
let videoImage;
function setup() {
const canvas = createCanvas(500, 400);
videoImage = createGraphics(320, 180);
}
function draw() {
clear();
background("rgba(100, 100, 255, 0.2)");
videoImage.drawingContext.drawImage(
videoElement,
0,
0,
videoImage.width,
videoImage.height
);
push();
if (isFlipped) {
translate(width, 0);
scale(-1, 1);
}
displayWidth = width;
displayHeight = (width * videoImage.height) / videoImage.width;
image(videoImage, 0, 0, displayWidth, displayHeight);
pop();
if (keypointsHand.length > 0) {
const indexTip = keypointsHand[0][8];
ellipse(indexTip.x * displayWidth, indexTip.y * displayHeight, 10);
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js" crossorigin="anonymous"></script>
<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>
</head>
<body>
<div class="container">
<video class="input_video"></video>
<canvas class="output_canvas" width="1280px" height="720px"></canvas>
</div>
</body>
</html>