2
3

3D空間に入ってしまうゲーム。

Last updated at Posted at 2024-09-12

スクリーンショット 2024-09-13 043120.png

スクリーンショット 2024-09-13 042610.png

image.png

人物のセグメンテーションを行い、3D空間に描画するゲーム。

人物画像の3D描画
抽出された人物画像は、PlaneGeometry(平面)にマッピングされ、透明な背景の状態で3D空間に表示

Three.jsの初期化
Three.jsを使って3Dシーンを作成し、カメラとレンダラーを初期化します。BoxGeometryとランダムな色のMeshBasicMaterialを使って、カラフルなキューブをランダムに配置します。

BodyPixでの人物抽出
ローカルの画像を選択すると、BodyPixで人物のセグメンテーションが行われ、背景を透明にした画像がに描画されます。そのcanvasをテクスチャとしてTHREE.Textureに渡し、3D空間に描画します。

人物画像の3D描画
抽出された人物画像は、PlaneGeometry(平面)にマッピングされ、透明な背景の状態で3D空間に表示されます。

アニメーション
animate関数でシーン全体が回転し、キューブと人物が回転する3D空間を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D空間に人物画像を描画</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <input type="file" id="upload" accept="image/*">
    <br>
    <img id="input-image" style="display:none;">
    <canvas id="output-canvas" style="display:none;"></canvas>

    <script>
        let scene, camera, renderer;
        let personTexture;

        // Three.jsのシーンを初期化
        function initThreeJS() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.z = 5;

            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            // カラフルなキューブを作成
            const geometry = new THREE.BoxGeometry(1, 1, 1);
            for (let i = 0; i < 50; i++) {
                const material = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });
                const cube = new THREE.Mesh(geometry, material);
                cube.position.set((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10);
                scene.add(cube);
            }

            animate();
        }

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);
            scene.rotation.x += 0.01;
            scene.rotation.y += 0.01;
            renderer.render(scene, camera);
        }

        // 人物の画像を平面に描画
        function addPersonToScene(texture) {
            const geometry = new THREE.PlaneGeometry(3, 4);
            const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true });
            const plane = new THREE.Mesh(geometry, material);
            scene.add(plane);
        }

        // BodyPixのモデルをロード
        let net;
        async function loadBodyPix() {
            net = await bodyPix.load();
            console.log('BodyPixモデルがロードされました');
        }
        loadBodyPix();

        // 画像が選択されたときの処理
        const upload = document.getElementById('upload');
        const inputImage = document.getElementById('input-image');
        const outputCanvas = document.getElementById('output-canvas');
        const ctx = outputCanvas.getContext('2d');

        upload.addEventListener('change', (event) => {
            const file = event.target.files[0];
            const reader = new FileReader();
            reader.onload = (e) => {
                inputImage.src = e.target.result;
                inputImage.onload = () => {
                    processImage();
                };
            };
            reader.readAsDataURL(file);
        });

        // 画像を処理して人物のみを抽出
        async function processImage() {
            outputCanvas.width = inputImage.width;
            outputCanvas.height = inputImage.height;

            // セグメンテーションを実行
            const segmentation = await net.segmentPerson(inputImage);

            // 画像のピクセルデータを取得
            ctx.drawImage(inputImage, 0, 0);
            const imageData = ctx.getImageData(0, 0, inputImage.width, inputImage.height);
            const data = imageData.data;

            // セグメンテーション結果に基づいて人物部分のみを描画
            for (let i = 0; i < data.length; i += 4) {
                const n = i / 4;
                if (segmentation.data[n] === 0) {
                    data[i + 3] = 0; // 背景部分は透明にする
                }
            }
            ctx.putImageData(imageData, 0, 0);

            // Three.js用のテクスチャとして設定
            personTexture = new THREE.Texture(outputCanvas);
            personTexture.needsUpdate = true;

            // 人物をシーンに追加
            addPersonToScene(personTexture);
        }

        // Three.jsの初期化
        initThreeJS();
    </script>
</body>
</html>

Webカメラの映像を取得バージョン。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D空間に人物画像を描画</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
        #video {
            display: none; /* カメラの映像は隠しますが、内部で使用します */
        }
    </style>
</head>
<body>
    <video id="video" width="640" height="480" autoplay></video>
    <input type="button" id="capture" value="キャプチャ">
    <canvas id="output-canvas" style="display:none;"></canvas>

    <script>
        let scene, camera, renderer;
        let personTexture;
        let net;

        // Three.jsのシーンを初期化
        function initThreeJS() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.z = 5;

            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            // カラフルなキューブを作成
            const geometry = new THREE.BoxGeometry(1, 1, 1);
            for (let i = 0; i < 50; i++) {
                const material = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });
                const cube = new THREE.Mesh(geometry, material);
                cube.position.set((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10);
                scene.add(cube);
            }

            animate();
        }

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);
            scene.rotation.x += 0.01;
            scene.rotation.y += 0.01;
            renderer.render(scene, camera);
        }

        // 人物の画像を平面に描画
        function addPersonToScene(texture) {
            const geometry = new THREE.PlaneGeometry(3, 4);
            const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true });
            const plane = new THREE.Mesh(geometry, material);
            scene.add(plane);
        }

        // BodyPixのモデルをロード
        async function loadBodyPix() {
            net = await bodyPix.load();
            console.log('BodyPixモデルがロードされました');
        }
        loadBodyPix();

        // Webカメラの映像を取得
        async function setupCamera() {
            const video = document.getElementById('video');
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            video.srcObject = stream;
            return new Promise(resolve => {
                video.onloadedmetadata = () => {
                    resolve();
                };
            });
        }

        // 画像を処理して人物のみを抽出
        async function processImage() {
            const video = document.getElementById('video');
            const outputCanvas = document.getElementById('output-canvas');
            const ctx = outputCanvas.getContext('2d');
            
            outputCanvas.width = video.videoWidth;
            outputCanvas.height = video.videoHeight;

            ctx.drawImage(video, 0, 0);

            // セグメンテーションを実行
            const segmentation = await net.segmentPerson(outputCanvas);

            // 画像のピクセルデータを取得
            const imageData = ctx.getImageData(0, 0, outputCanvas.width, outputCanvas.height);
            const data = imageData.data;

            // セグメンテーション結果に基づいて人物部分のみを描画
            for (let i = 0; i < data.length; i += 4) {
                const n = i / 4;
                if (segmentation.data[n] === 0) {
                    data[i + 3] = 0; // 背景部分は透明にする
                }
            }
            ctx.putImageData(imageData, 0, 0);

            // Three.js用のテクスチャとして設定
            personTexture = new THREE.Texture(outputCanvas);
            personTexture.needsUpdate = true;

            // 人物をシーンに追加
            addPersonToScene(personTexture);
        }

        // イベントリスナーの設定
        document.getElementById('capture').addEventListener('click', processImage);

        // Three.jsの初期化とカメラのセットアップ
        async function init() {
            await setupCamera();
            initThreeJS();
        }
        init();
    </script>
</body>
</html>

2
3
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
3