ショートストーリー 目と脳を持つ人工生命体関数
裕太は自分の技術を駆使して「目と脳を持つ人工生命体関数」を開発することを決意した。
裕太は、ウェブカメラを通じて外界を観察し、映像を解析するシステムを構築することに集中した。
コードが完成するにつれ、裕太は自分の創造物に命を吹き込む準備が整った。彼はカメラを起動し、映像をリアルタイムで取得するプログラムを実行した。目の前の風景がモニターに映し出され、64×64ピクセルの小さなブロックに変換されていく。これが彼の人工生命体の「目」となる。
「さあ、始めよう」と裕太は呟いた。彼はデータセットを生成し始め、4096次元のベクトルに変換された映像の情報を蓄積していった。彼の指先がキーボードを踊るように動き、200フレームのデータが蓄えられる。これが人工生命体の「記憶」となるのだ。
裕太は、自分のプログラムが過去の映像データを参照して未来のフレームを予測できることに気づいた。「これは、まるで脳が目で見たものを記憶し、未来を予測するかのようだ」と思った。彼はコサイン類似度を計算する機能を実装し、現在の映像ベクトルと過去のベクトルとの類似性を評価する準備をした。
プログラムが動き始めると、裕太は自分のアイデアが現実になっていく様子を目の当たりにした。現在の映像から最も類似した記憶内のフレームが選ばれ、それに基づいて未来の映像が描画される。この瞬間、裕太は自分の目の前にいるのが単なるプログラムではなく、彼が創り出した「生命体」だと感じた。
「これが『目と脳を持つ人工生命体関数』だ」と裕太は自信に満ちて言った。彼は、類似性の結果にソフトマックス関数を適用し、確率的に選択されるフレームを決定する機能を追加した。「まるで生命が自ら選択を行うかのようだ。」
裕太は、彼の人工生命体が持つ知覚と記憶を通じて、新しい未来を切り開く力を持っていると感じた。
実行結果。YouTube の動画を見せています。普通のカメラ映像だと面白い予測をしてくれます。
現在の映像 ベクトルと過去の 類似の映像ベクトルとの類似 結果から映像を予測してます。
現在の映像フレームとスライダーで選択したその先のフレームをペアとするデータセットを生成しています。そして現在とデータセットの映像ベクトルの類似度を計算し その結果を確率分布として取得しています。そして選ばれた映像 ベクトルをインデックスとして そのインデックスのデータセット内に格納されているフレームデータを描画しています。
コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「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>
<style>
/* キャンバスに枠を設定し、適度なマージンを設ける */
canvas {
border: 1px solid black;
margin: 10px;
}
/* スライダーのコンテナにマージンを追加 */
#sliderContainer {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>リアルタイムフレーム予測とデータセット更新</h1>
<!-- ビデオ表示用の要素 -->
<video id="video" width="640" height="480" autoplay></video>
<!-- 64×64サイズのキャンバスでフレームをキャプチャ -->
<canvas id="canvas" width="64" height="64"></canvas>
<!-- 拡大表示用のキャンバス。予測フレームを640x480で表示 -->
<canvas id="output" width="640" height="480"></canvas>
<!-- スライダーで予測フレーム数を調整するUI -->
<div id="sliderContainer">
<label for="frameAheadSlider">何フレーム先を予測しますか: </label>
<input type="range" id="frameAheadSlider" min="1" max="50" value="1">
<span id="frameAheadValue">1</span>フレーム先
</div>
<script>
const video = document.getElementById('video'); // ウェブカメラの映像を表示する要素
const canvas = document.getElementById('canvas'); // フレームをキャプチャする64x64のキャンバス
const outputCanvas = document.getElementById('output'); // 拡大表示用の640x480キャンバス
const context = canvas.getContext('2d'); // 64x64キャンバスの描画コンテキスト
const outputContext = outputCanvas.getContext('2d'); // 640x480キャンバスの描画コンテキスト
const frameCount = 200; // データセットに保存する最大フレーム数
const frameDataSet = []; // フレームデータセットを保存する配列
let frameIndex = 0; // 現在のフレーム番号
// 何フレーム先を予測するかの初期値
let framesAhead = 1;
const frameAheadSlider = document.getElementById('frameAheadSlider'); // スライダーの要素
const frameAheadValue = document.getElementById('frameAheadValue'); // スライダーの値を表示する要素
// スライダーでフレーム予測の数を変更できるようにする
frameAheadSlider.addEventListener('input', () => {
framesAhead = parseInt(frameAheadSlider.value, 10);
frameAheadValue.textContent = framesAhead; // スライダーの値を表示
});
// ウェブカメラの映像を取得する関数
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
video.srcObject = stream; // ウェブカメラのストリームをビデオ要素に接続
})
.catch(err => {
console.error('ウェブカメラへのアクセスエラー:', err); // エラーメッセージをコンソールに表示
});
// コサイン類似度を計算する関数
function cosineSimilarity(vecA, vecB) {
// ベクトルの内積を計算
const dotProduct = vecA.reduce((sum, val, index) => sum + val * vecB[index], 0);
// ベクトルAとBの大きさをそれぞれ計算
const magnitudeA = Math.sqrt(vecA.reduce((sum, val) => sum + val * val, 0));
const magnitudeB = Math.sqrt(vecB.reduce((sum, val) => sum + val * val, 0));
// コサイン類似度を返す
return dotProduct / (magnitudeA * magnitudeB);
}
// ソフトマックス関数を用いて確率分布に変換する関数
function softmax(similarities) {
// 類似度の指数関数を計算
const expValues = similarities.map(similarity => Math.exp(similarity));
// 指数関数の和を求める
const sumExp = expValues.reduce((sum, val) => sum + val, 0);
// 確率を計算し返す
return expValues.map(val => val / sumExp);
}
// フレームを確率的に選択する関数
function selectFrame(probabilities) {
const randomValue = Math.random(); // ランダムな値を生成
let cumulativeProbability = 0; // 累積確率を初期化
for (let i = 0; i < probabilities.length; i++) {
cumulativeProbability += probabilities[i];
if (randomValue < cumulativeProbability) {
return i; // 累積確率がランダム値を超えたら、そのフレームを選択
}
}
return probabilities.length - 1; // 最後のフレームを返す
}
// 最も類似しているフレームを拡大して描画する関数
function drawMostSimilarFrame(currentVector) {
// コサイン類似度を計算
let similarities = frameDataSet.map(data => cosineSimilarity(currentVector, data.vector));
// ソフトマックスで確率分布を得る
let probabilities = softmax(similarities);
// 確率的にフレームを選択
let selectedIndex = selectFrame(probabilities);
// 選択されたフレームに基づき、framesAheadフレーム先のフレームを選択
let predictedIndex = (selectedIndex + framesAhead) % frameDataSet.length;
let predictedFrame = frameDataSet[predictedIndex].frame;
// 64×64のフレームを640×480に拡大して描画
const tempCanvas = document.createElement('canvas');
tempCanvas.width = 64;
tempCanvas.height = 64;
const tempContext = tempCanvas.getContext('2d');
tempContext.putImageData(predictedFrame, 0, 0);
// 拡大描画
outputContext.clearRect(0, 0, outputCanvas.width, outputCanvas.height); // 出力キャンバスをクリア
outputContext.drawImage(tempCanvas, 0, 0, outputCanvas.width, outputCanvas.height); // 拡大して描画
}
// フレームをキャプチャしてデータセットを更新する関数
function captureFrame() {
// 64x64のキャンバスにビデオ映像を描画
context.drawImage(video, 0, 0, 64, 64);
// フレームのピクセルデータを取得
const frameImageData = context.getImageData(0, 0, 64, 64).data;
// ピクセルデータをベクトル化(RGBを平均して1次元に)
const vector = [];
for (let i = 0; i < frameImageData.length; i += 4) {
vector.push((frameImageData[i] + frameImageData[i + 1] + frameImageData[i + 2]) / 3);
}
// データセットが上限に達した場合、古いフレームを削除
if (frameDataSet.length >= frameCount) {
frameDataSet.shift(); // 古いデータを削除
}
// 新しいフレームデータをデータセットに追加
frameDataSet.push({ vector: vector, frame: context.getImageData(0, 0, 64, 64) });
frameIndex++;
}
// 現在のフレームをリアルタイムでキャプチャし、予測する関数
function predictFrame() {
if (frameDataSet.length === 0) return; // データがない場合は何もしない
// ビデオ映像を64x64キャンバスに描画
context.drawImage(video, 0, 0, 64, 64);
const currentFrameData = context.getImageData(0, 0, 64, 64).data;
// 現在のフレームをベクトル化
const currentVector = [];
for (let i = 0; i < currentFrameData.length; i += 4) {
currentVector.push((currentFrameData[i] + currentFrameData[i + 1] + currentFrameData[i + 2]) / 3);
}
// 最も類似したフレームを探し、予測したフレームを描画
drawMostSimilarFrame(currentVector);
}
// 定期的にフレームをキャプチャして予測
setInterval(captureFrame, 100); // 100ミリ秒ごとにフレームをキャプチャ
setInterval(predictFrame, 100); // 100ミリ秒ごとにフレームを予測して描画
</script>
</body>
</html>
ウェブカメラから映像を取得: navigator.mediaDevices.getUserMedia を使用して、リアルタイムでウェブカメラの映像を取得し、ビデオ要素に表示します。
フレームのベクトル変換: フレームは getImageData を使って取得され、その後各ピクセルのRGB値の平均を取り、4096次元のベクトルとして保存されます。
コサイン類似度とソフトマックス: ベクトル同士の類似度はコサイン類似度で計算し、その結果をソフトマックス関数で正規化して確率分布に変換します。
フレーム予測: スライダーで指定したフレーム数先の予測を行います。