TensorFlow.jsを使用して骨格推定。骨格のライン推定 with TensorFlow.js
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TensorFlow.js 骨格推定 - 画像ファイル処理</title>
<!-- TensorFlow.jsとPoseNetモデルの読み込み -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/posenet"></script>
<style>
/* ページ全体を中央揃えにするスタイル */
body { text-align: center; margin-top: 20px; }
/* 画像とcanvasのサイズ指定 */
img, canvas {
max-width: 640px;
max-height: 480px;
}
</style>
</head>
<body>
<h1>TensorFlow.js 骨格推定 - 画像ファイル処理</h1>
<!-- 画像ファイルを選択するための入力フォーム -->
<input type="file" id="fileInput" accept="image/*"><br><br>
<!-- 選択された画像を表示するためのimgタグ -->
<img id="image" style="display:none;">
<!-- 骨格推定結果を描画するためのcanvasタグ -->
<canvas id="output"></canvas>
<script>
// HTMLからimgとcanvas要素を取得
const img = document.getElementById('image');
const canvas = document.getElementById('output');
const ctx = canvas.getContext('2d');
const fileInput = document.getElementById('fileInput');
// 画像ファイルが選択されたときの処理
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
// 画像を読み込み、表示する
const reader = new FileReader();
reader.onload = (e) => {
img.src = e.target.result;
img.onload = () => {
// 画像が読み込まれた後にcanvasのサイズを画像に合わせる
canvas.width = img.width;
canvas.height = img.height;
// 画像を描画してから骨格推定を行う
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
estimatePose();
};
};
reader.readAsDataURL(file); // 画像ファイルをData URLとして読み込む
});
// 骨格推定を行う関数
async function estimatePose() {
// PoseNetモデルをロード
const net = await posenet.load();
// 画像に対してポーズを推定
const pose = await net.estimateSinglePose(img, {
flipHorizontal: false // 鏡像反転しない設定
});
// キーポイントを描画
drawKeypoints(pose.keypoints, ctx);
// 骨格のラインを描画
drawSkeleton(pose.keypoints, ctx);
}
// キーポイント(関節)を描画する関数
function drawKeypoints(keypoints, ctx) {
// 推定されたすべてのキーポイントを順番に処理
keypoints.forEach(keypoint => {
// 信頼度が高いキーポイント(0.5以上)だけ描画
if (keypoint.score > 0.5) {
const { y, x } = keypoint.position; // 座標を取得
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI); // 円を描画
ctx.fillStyle = 'red'; // 赤色の円
ctx.fill();
}
});
}
// キーポイント同士を線で結んで骨格を描画する関数
function drawSkeleton(keypoints, ctx) {
// 関節間の推定ペア(隣接キーポイント)を取得
const adjacentKeyPoints = posenet.getAdjacentKeyPoints(keypoints, 0.5);
// 各ペアの関節間に線を引く
adjacentKeyPoints.forEach(keypoints => {
const [start, end] = keypoints; // 線を引く開始点と終了点
ctx.beginPath();
ctx.moveTo(start.position.x, start.position.y); // 開始点に移動
ctx.lineTo(end.position.x, end.position.y); // 終了点まで線を引く
ctx.lineWidth = 2; // 線の太さ
ctx.strokeStyle = 'blue'; // 線の色は青
ctx.stroke();
});
}
</script>
</body>
</html>
- HTMLファイルの構成
: ユーザーが画像ファイルを選択できるようにするための入力フィールド。accept="image/*" は画像ファイルだけを選択可能にするための指定です。
: 選択された画像を表示し、PoseNetモデルが処理できるようにします。スタイルとして display:none を指定して、画像自体は表示しないようにしています。
: 骨格推定結果を描画する領域。画像に重ねて結果が表示されます。 - JavaScriptコードの詳細
fileInput.addEventListener('change', async (event)):
画像ファイルが選択されると、このイベントハンドラーが呼び出されます。
FileReader オブジェクトを使って選択された画像ファイルを読み込み、img 要素に画像を表示します。
画像が読み込まれると、canvas のサイズを画像に合わせて調整し、骨格推定を実行します。
estimatePose():
PoseNetモデルをロードして、画像に対して骨格推定を行います。
骨格推定が完了すると、キーポイントと骨格ラインを canvas に描画します。
drawKeypoints():
キーポイント(関節の位置)を赤い円で描画します。信頼度が 0.5 以上のものだけを描画対象にしています。
drawSkeleton():
PoseNetの getAdjacentKeyPoints() を使って、隣接するキーポイントを線で結び、青い線で骨格のラインを描画します。
3. デザインとスタイル
画像ファイルのサイズに合わせて のサイズを動的に変更しているため、選択した画像に対して正確に骨格推定結果が表示されます。
使い方
このHTMLファイルをローカルで開くと、ファイル選択ダイアログが表示されます。
画像ファイルを選択すると、その画像に対してリアルタイムで骨格推定が行われ、推定結果が表示されます。