3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

動画生成技術。「動きを捉えるアルゴリズム」

Last updated at Posted at 2024-10-13

ショートストーリー: 「時間の流れを捉えるアルゴリズム」

東京の夜、渋谷のオフィスビルに明かりが灯る。プログラマーの田中は、ディスプレイの前で一心に作業していた。彼の目の前には、彼が開発中のニューラルネットワークモデルが映し出されている。フレーム数の違いが、モデルに与える影響を検証している最中だ。

「フレーム数が増えれば、動きの連続性がもっと捉えられるはずだ...」

田中は1フレームの入力でモデルを実行する。結果は想像通り、動きは途切れ途切れで滑らかさに欠けていた。まるで連続したスナップショットが次々と映し出されるかのようだ。

「やっぱり、1フレームでは瞬間的な情報しか捉えられない...」

彼は次に、30フレームをモデルに入力した。今度は違う。連続的な動作の流れが、滑らかに画面に映し出される。物体が自然に動き、時間の経過が明確に再現されていた。

image.png

「これだ...フレームを増やすことで、時間の流れがしっかりと捉えられている。」

田中は満足げにうなずいた。しかし、すべてが完璧というわけではなかった。30フレーム分のデータを扱うことで、処理に時間がかかり、モデルは部分的に情報を取りこぼしていた。モデルが重要な特徴を抽出し損ね、損失関数の数値が跳ね上がっていたのだ。

「もっと情報を詰め込めば、確かに動きの再現は滑らかになる。でも、その分失われる情報も多い。損失と特徴のバランスが重要だな...」

田中は実験を続ける中で気づき始めていた。問題の本質は単純ではない。フレームを増やせば増やすほど、捉えるべき情報が増え、処理の複雑さが増していく。だが、それは単なるデータ量の問題ではなく、モデルのパフォーマンスに大きく依存していた。内臓の GPU では パワーも足りない。

そこで、田中は新たなアイデアを思いついた。

「動きを捉えることが可能であるならば、あとは簡単だ...」

田中の頭の中で、解決策が徐々にクリアになっていく。次のステップは、モデル自体をより大きくすることだ。もっと多くのパラメータを持つ、より複雑なモデル。処理能力が不足することもない。強力なマシンで、それを動かせばいい。

image.png

「モデルサイズを大きくして、パラメータを増やす。それに対応できる強力なマシンを用意すれば、すべての問題は解決する。」

彼は笑みを浮かべた。現代のコンピューティングパワーをフルに活用することが、この課題の突破口になるだろう。もっと強力なGPUを使い、モデルをスケールさせることで、時間の流れを完全に捉える映像が生成できると確信していた。

彼の目の前に広がる未来は、無限の可能性に満ちていた。動画生成技術の進化、そしてその中で重要な役割を果たすニューラルネットワークの成長。それはすべて、より強力なマシンと、大きなモデルによって支えられている。

「今までは試行錯誤だったが、これで未来が見えてきた。」

田中はすぐに、クラウドサーバーを借りる準備を始めた。今度は、その強大な計算能力を存分に活用するつもりだ。画面上で、重厚なモデルが滑らかに動き出す姿を想像し、彼は高揚感に包まれていた。

「強力なマシンさえあれば、どんな複雑な動きも捉えられる。次は、もっと大規模なシミュレーションを試そう。」

彼は再びキーボードに手を伸ばし、スケールアップされた新しいモデルをセットアップし始めた。夜の渋谷は静寂に包まれ、田中の集中力はピークに達していた。彼が描く未来は、今この瞬間、彼の指先から形作られていた。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Webカメラとニューラルネットワークのアニメーション</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <style>
        canvas {
            border: 1px solid black;
        }
        .slider-container {
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <h1>Webカメラとニューラルネットワークの出力</h1>
    <div style="display: flex; gap: 20px;">
        <!-- 左側にWebカメラの映像を表示 -->
        <video id="webcam" autoplay playsinline width="128" height="128"></video>

        <!-- 右側にニューラルネットワークの出力を表示(拡大版) -->
        <canvas id="outputCanvas" width="512" height="512"></canvas>
    </div>

    <div class="slider-container">
        <label for="frameSlider">フレーム数: <span id="frameCount">1</span></label>
        <input type="range" id="frameSlider" min="1" max="30" value="1">
    </div>

    <script>
        // Webカメラのセットアップ関数
        async function setupWebcam() {
            const webcamElement = document.getElementById('webcam');
            return new Promise((resolve, reject) => {
                const constraints = {
                    video: { width: 128, height: 128 } // 128×128の解像度でWebカメラを設定
                };
                navigator.mediaDevices.getUserMedia(constraints)
                    .then((stream) => {
                        webcamElement.srcObject = stream; // Webカメラの映像をvideo要素にセット
                        webcamElement.addEventListener('loadeddata', () => resolve(webcamElement)); // 映像がロードされたら解決
                    })
                    .catch(reject); // エラー時にはreject
            });
        }

        async function run() {
            const webcam = await setupWebcam(); // Webカメラをセットアップ

            // シンプルなニューラルネットワークモデルの作成
            const model = tf.sequential();
            model.add(tf.layers.dense({
                units: 64,
                inputShape: [128 * 128 * 3], // 入力は128×128ピクセル×3チャンネル
                activation: 'relu' // 活性化関数にReLUを使用
            }));
            model.add(tf.layers.dense({
                units: 128 * 128 * 3, // 出力も128×128ピクセル×3チャンネル
                activation: 'sigmoid' // 活性化関数にSigmoidを使用(出力は0~1にスケール)
            }));
            model.compile({
                optimizer: 'adam', // Adamオプティマイザ
                loss: 'meanSquaredError' // 損失関数は平均二乗誤差
            });

            const frameSlider = document.getElementById('frameSlider');
            const frameCountLabel = document.getElementById('frameCount');

            frameSlider.addEventListener('input', () => {
                frameCountLabel.textContent = frameSlider.value;
            });

            // Webカメラの現在のフレームをキャプチャする関数
            function captureFrame() {
                const canvas = document.createElement('canvas');
                canvas.width = 128;
                canvas.height = 128;
                const ctx = canvas.getContext('2d');
                ctx.drawImage(webcam, 0, 0, 128, 128); // Webカメラのフレームをキャンバスに描画
                const imageData = ctx.getImageData(0, 0, 128, 128); // 画像データを取得

                // RGBAデータをRGBに変換(アルファチャンネルは無視)
                const rgbData = new Uint8Array(128 * 128 * 3);
                for (let i = 0; i < 128 * 128; i++) {
                    rgbData[i * 3] = imageData.data[i * 4];       // 赤チャンネル
                    rgbData[i * 3 + 1] = imageData.data[i * 4 + 1]; // 緑チャンネル
                    rgbData[i * 3 + 2] = imageData.data[i * 4 + 2]; // 青チャンネル
                }

                // データをテンソルに変換し、0-1の範囲にスケーリング
                return tf.tensor(rgbData, [1, 128 * 128 * 3]).div(255);
            }

            // 複数のフレームをキャプチャしてバッチデータを作成する関数
            async function captureBatch(numFrames) {
                const frames = [];
                for (let i = 0; i < numFrames; i++) {
                    frames.push(captureFrame());
                    await new Promise(resolve => setTimeout(resolve, 100)); // 各フレームのキャプチャ間隔
                }
                return tf.concat(frames); // フレームをバッチに結合
            }

            // モデルをトレーニングし、結果を表示する関数
            async function trainAndDisplay() {
                const numFrames = parseInt(frameSlider.value); // 現在のフレーム数を取得
                const inputFrames = await captureBatch(numFrames); // バッチを取得

                // モデルをトレーニング(1エポック)
                await model.fit(inputFrames, inputFrames, { epochs: 1 });

                // モデルの出力を予測
                const output = model.predict(inputFrames.slice([0, 0], [1, 128 * 128 * 3])).reshape([128, 128, 3]); // 出力を128x128x3にリシェイプ

                // 出力キャンバスにモデルの出力を描画
                const outputCanvas = document.getElementById('outputCanvas');
                const outputCtx = outputCanvas.getContext('2d');
                const outputImageData = outputCtx.createImageData(128, 128); // 128x128の空のイメージデータを作成
                const data = await output.data(); // モデルの出力データを取得

                // RGBデータをImageData形式に変換
                for (let i = 0; i < 128 * 128; i++) {
                    outputImageData.data[i * 4] = data[i * 3] * 255;     // 赤チャンネル
                    outputImageData.data[i * 4 + 1] = data[i * 3 + 1] * 255; // 緑チャンネル
                    outputImageData.data[i * 4 + 2] = data[i * 3 + 2] * 255; // 青チャンネル
                    outputImageData.data[i * 4 + 3] = 255; // アルファチャンネル(完全不透明)
                }

                // 128x128のデータを一度キャンバスに描画
                const tempCanvas = document.createElement('canvas');
                tempCanvas.width = 128;
                tempCanvas.height = 128;
                const tempCtx = tempCanvas.getContext('2d');
                tempCtx.putImageData(outputImageData, 0, 0); // 一度元のサイズで描画

                // その後、512x512に拡大して表示
                outputCtx.drawImage(tempCanvas, 0, 0, 128, 128, 0, 0, 512, 512); // 拡大描画

                // 継続的にトレーニングと描画を行う
                requestAnimationFrame(trainAndDisplay);
            }

            // トレーニングと表示のループを開始
            trainAndDisplay();
        }

        // プログラムを実行
        run();
    </script>
</body>
</html>

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?