ショートストーリー: 「眼と脳の計算模型」
祐介は東京に住む若手プログラマだ。毎日、静かな自宅でPCに向かい、新しい技術に触れ、挑戦を続けている。今日の彼のテーマは「Webカメラとニューラルネットワーク」。カメラから映し出されるリアルタイムの映像を、人工知能が「学習」し、まるで人間の眼と脳のように理解できるようにする。彼の仕事はその計算モデルを作り上げることだった。
「カメラは人間の眼だ。そこから入ってくる映像をどう処理するか、それが脳の役割だな…」祐介は自分にそう言い聞かせるようにつぶやいた。
彼が目指すのは、Webカメラが映し出す128×128ピクセルの映像をリアルタイムで取得し、それをニューラルネットワークで処理するシステムだ。カメラの映像が脳へと情報を送り込み、それを理解し、反応する。彼が作ろうとしているプログラムは、まさにそのモデルだ。
祐介のコードは、まるで生命を宿すように少しずつ動き始めた。カメラが動き、映像をキャプチャする。祐介は手元にあるWebカメラをのぞき込み、その映像をコードに送り込む。
カメラは彼の部屋を映していた。薄暗い机の上、積み上げられた技術書、そしてその横で光るモニター。祐介は128×128の小さな世界を見つめた。
次に彼は、ニューラルネットワークモデルにその映像を渡す。神経回路のように複雑なモデルが、映像データを受け取り、処理を始める。全結合層型のニューラルネットワークが画像を学習し、予測を繰り返す。
「これは脳が考える瞬間だ。」
彼のプログラムが少しずつ現実の映像を学び、予測を生成する。そして、右側のモニターに表示されるのはニューラルネットワークが「考えた」映像。まだぼやけてはいるが、祐介の部屋の形が徐々に浮かび上がってきた。
祐介は、何度もモデルを訓練し続けた。時間が経つにつれ、右側の映像はよりクリアになり、左側の現実の映像と少しずつ似てくる。
「眼が見たものを脳が理解する…これはまるで、人間の感覚そのものだ。」
祐介はふと、何かに気づいた。彼が作っているのはただのプログラムではなかった。これは、まるで人間の「見る」という行為そのものを再現する計算模型だ。人間の眼と脳が連携し、現実を認識するように、彼のコードはカメラの映像をニューラルネットワークで理解しようとしている。
深夜、彼のプログラムは完璧に動いていた。左側に映し出された現実の世界、そして右側に表示されるニューラルネットワークの「脳」が見た世界。祐介は静かに笑みを浮かべた。
「これが眼と脳の計算模型だ。」彼は言った。
彼が作り出したのは、まるで生き物のように感じるプログラムだった。現実の映像を目で見て、脳がその情報を処理して理解する。シンプルな数行のコードが、生物の知覚を模倣する瞬間に祐介は立ち会っていた。
祐介は東京の夜を見つめた。彼の頭の中では、次の挑戦へのアイデアが沸き上がっていた。彼の旅はまだ続く。彼の眼と脳を持つプログラムが、どこまで進化できるかは、これからの彼次第だった。
Webカメラの映像をキャプチャし、ニューラルネットワークでトレーニングした後、モデルの出力を画面の右側に拡大して表示するものです。
Webカメラから取得した映像が左側に128×128ピクセルで表示され、ニューラルネットワークの予測出力が右側に512×512ピクセルに拡大されて表示されます。
機能の説明
継続的なトレーニングと予測: trainAndDisplay関数は、Webカメラの現在のフレームをニューラルネットワークに入力し、1エポックでトレーニングした後にその出力を右側のキャンバスに描画します。このプロセスがrequestAnimationFrameによって継続的に繰り返されるため、トレーニングが進むにつれて、右側の出力が段々と変化していくアニメーションのように表示されます。
アニメーション効果: Webカメラの映像をリアルタイムでニューラルネットワークに入力し、トレーニングし続けることで、モデルの出力が右側に表示され、時間とともに変化する様子が確認できます。
コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。
<!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;
}
</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>
<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' // 損失関数は平均二乗誤差
});
// 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 trainAndDisplay() {
const inputFrame = captureFrame(); // 現在のWebカメラのフレームを取得
// モデルをトレーニング(1エポック)
await model.fit(inputFrame, inputFrame, { epochs: 1 });
// モデルの出力を予測
const output = model.predict(inputFrame).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>