将代码粘贴到文本编辑器(例如记事本)中,并将文件名保存为“index.html”。当您在浏览器中打开保存的文件时,代码将运行。
使用网络摄像头捕捉到的人体动作被映射到狗的形状并绘制出来。 狗的头、腿和尾巴根部都经过适当的绘制,以再现狗的身体形状。
您可以使用网络摄像头运动来模拟狗的骨骼。
现在,狗似乎站在地面上,并且根据网络摄像头镜头的骨骼估计来模拟狗形机器人的运动。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>犬型ロボットの骨格描画(前足を地面に近づけて固定)</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>
<!-- Three.jsの読み込み -->
<script src="https://cdn.jsdelivr.net/npm/three@0.135.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.135.0/examples/js/controls/OrbitControls.js"></script>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; background-color: green; }
</style>
</head>
<body>
<!-- カメラ映像を非表示で取得するビデオ要素 -->
<video id="video" width="640" height="480" autoplay style="display:none;"></video>
<script>
let scene, camera, renderer;
// Three.jsの初期化
function initThreeJS() {
// シーンの作成
scene = new THREE.Scene();
// カメラの設定 (視野角, アスペクト比, 近接クリップ距離, 遠方クリップ距離)
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// レンダラーの設定
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// カメラの位置を設定し、犬型ロボットが見えるようにカメラの視点を設定
camera.position.set(0, 3, 5);
camera.lookAt(0, 1, 0);
// 照明の設定
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 5, 5).normalize();
scene.add(light);
// マウスで視点を回転できるようにするコントロールの追加
const controls = new THREE.OrbitControls(camera, renderer.domElement);
}
// 犬型骨格を描画(前足を地面に近づけるように)
function drawDogSkeleton(keypoints) {
// keypoints(関節座標)を部位ごとにアクセスしやすい形式に変換
const keypointsMap = keypoints.reduce((map, kp) => (map[kp.part] = kp, map), {});
// 骨格ラインのペア定義(接続する部位の組み合わせ)
const limbPairs = [
['leftShoulder', 'leftElbow'], ['leftElbow', 'leftWrist'], // 左前足
['rightShoulder', 'rightElbow'], ['rightElbow', 'rightWrist'], // 右前足
['leftHip', 'leftKnee'], ['leftKnee', 'leftAnkle'], // 左後足
['rightHip', 'rightKnee'], ['rightKnee', 'rightAnkle'], // 右後足
['leftShoulder', 'leftHip'], ['rightShoulder', 'rightHip'] // 胴体
];
// スケールとオフセット(全体の表示位置を調整)
const scale = 0.02;
const yOffset = -1.5;
// 前足の手首の位置をさらに下に設定
const frontFootY = -2.0;
// 前足の手首のY位置を地面に近づけるための関数
const adjustYPosition = (point, isFrontLeg) => {
// 前足の場合は固定高さをさらに下げて表示
return isFrontLeg ? frontFootY : yOffset - (point.position.y - video.height / 2) * scale;
};
// 前のラインと点をすべて削除
const existingObjects = scene.children.filter(child => child.type === 'Line' || (child.type === 'Mesh' && child.geometry.type === 'SphereGeometry'));
existingObjects.forEach(obj => scene.remove(obj));
// 各部位間のラインを描画
limbPairs.forEach(([partA, partB]) => {
const start = keypointsMap[partA];
const end = keypointsMap[partB];
if (start && end && start.score > 0.5 && end.score > 0.5) {
// 太い青いラインで描画
const material = new THREE.LineBasicMaterial({ color: 0x0000ff, linewidth: 5 });
// 各ラインの始点と終点を設定
const geometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(
(start.position.x - video.width / 2) * scale,
adjustYPosition(start, partA.includes('Wrist')),
0
),
new THREE.Vector3(
(end.position.x - video.width / 2) * scale,
adjustYPosition(end, partB.includes('Wrist')),
0
)
]);
const line = new THREE.Line(geometry, material);
scene.add(line);
}
});
// 鼻の位置に赤い球を描画(頭の目印)
const nose = keypointsMap['nose'];
if (nose && nose.score > 0.5) {
const { x, y } = nose.position;
const geometry = new THREE.SphereGeometry(0.15, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(geometry, material);
sphere.position.set((x - video.width / 2) * scale, yOffset - (y - video.height / 2) * scale, 0);
scene.add(sphere);
}
}
// アニメーションループ(再描画を行う)
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// カメラの映像を取得してPoseNetで骨格推定を行う
async function startPoseNet() {
const video = document.getElementById('video');
// ユーザーのカメラ映像を取得
navigator.mediaDevices.getUserMedia({
video: true
}).then(stream => {
video.srcObject = stream;
});
// PoseNetモデルの読み込み
const net = await posenet.load();
// カメラ映像の準備ができたらPoseNetで定期的に骨格推定を行う
video.onloadeddata = async () => {
setInterval(async () => {
// 現在のフレームの骨格を推定
const pose = await net.estimateSinglePose(video, {
flipHorizontal: false
});
// 推定結果の骨格を描画
drawDogSkeleton(pose.keypoints);
}, 100); // 0.1秒間隔で更新
};
}
// 初期化とアニメーション開始
initThreeJS();
animate();
startPoseNet();
</script>
</body>
</html>