2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

狗形机器人。一款将网络摄像头捕捉到的人(两条腿行走)的运动映射并绘制到狗(四条腿行走)上的游戏。

Posted at

image.png

image.png

将代码粘贴到文本编辑器(例如记事本)中,并将文件名保存为“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>



2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?