動画の各フレームをリアルタイムで深度推定した
動画ファイルを読み込んで、フレームごとにMiDaS深度推定を実行。オリジナル、深度マップ、オーバーレイの3つを同時表示。
リアルタイム処理。約20fps。動画の奥行きが見える。
やりたかったこと
動画に深度推定を適用したかった。
これまでは静止画だった(midori264, 265, 266)。でも、動画の方が面白い。動きのある映像の奥行きが見えるはず。
動画の各フレームを読み込んで、リアルタイムで深度推定する。
動画フレームの読み込み
HTMLの<video>要素から、フレームをCanvasに描画。
// 動画要素を作成
const video = document.createElement('video');
video.src = 'video/sample.mp4';
video.loop = true;
video.muted = true;
await video.play();
// フレームをCanvasに描画
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext('2d');
function captureFrame() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
return imageData;
}
requestAnimationFrameで、毎フレームcaptureFrameを呼び出す。
リアルタイム深度推定
フレームごとに、MiDaS深度推定を実行。
const inferenceManager = new GPUInferenceManager("/ai_models/midas_model_DPT_Hybrid_512.onnx");
async function processFrame() {
// フレームをキャプチャ
const imageData = captureFrame();
// 深度推定
const result = await inferenceManager.runInference(imageData);
const depthData = result.outputData;
// 深度マップをCanvasに描画
renderDepthMap(depthData);
// 次のフレームを処理
requestAnimationFrame(processFrame);
}
processFrame();
毎フレーム推論。約20fps。
最初は、30fpsを目指してた。でも、推論に約50msかかる。20fpsが限界。
3つの表示モード
オリジナル、深度マップ、オーバーレイの3つを同時表示。
function render() {
// 1. オリジナル
ctxOriginal.drawImage(video, 0, 0, canvasOriginal.width, canvasOriginal.height);
// 2. 深度マップ
const depthImageData = ctxDepth.createImageData(canvasDepth.width, canvasDepth.height);
for (let i = 0; i < depthData.length; i++) {
const value = Math.floor(depthData[i] * 255);
depthImageData.data[i * 4] = value;
depthImageData.data[i * 4 + 1] = value;
depthImageData.data[i * 4 + 2] = value;
depthImageData.data[i * 4 + 3] = 255;
}
ctxDepth.putImageData(depthImageData, 0, 0);
// 3. オーバーレイ(オリジナル + 深度マップ)
ctxOverlay.drawImage(canvasOriginal, 0, 0);
ctxOverlay.globalAlpha = 0.5;
ctxOverlay.drawImage(canvasDepth, 0, 0);
ctxOverlay.globalAlpha = 1.0;
}
オーバーレイは、元の映像に深度マップを半透明で重ねる。奥行きが直感的に分かる。
ハマったところ
フレームレートの調整
最初は、requestAnimationFrameで毎フレーム処理してた。でも、推論が間に合わない。
フレームが溜まって、遅延が増えた。約2秒の遅延。
推論が終わるまで待つようにした。
let isProcessing = false;
async function processFrame() {
if (isProcessing) return;
isProcessing = true;
try {
const imageData = captureFrame();
const result = await inferenceManager.runInference(imageData);
render(result.outputData);
} finally {
isProcessing = false;
}
requestAnimationFrame(processFrame);
}
遅延が消えた。約20fpsで安定。
メモリリーク
最初は、Canvasを毎回作成してた。メモリが増え続けて、約30秒でブラウザがクラッシュ。
Canvasを再利用するようにした。
// NG: 毎回作成
function captureFrame() {
const canvas = document.createElement('canvas'); // メモリリーク
// ...
}
// OK: 再利用
const canvas = document.createElement('canvas');
function captureFrame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// ...
}
メモリ使用量が安定した。
動画の解像度
動画の解像度が高いと、推論が遅くなる。
最初は、フルHD(1920×1080)の動画を使ってた。推論に約100ms。10fpsしか出ない。
512×512にリサイズしたら、約50ms。20fpsで動いた。
でも、512×512だと、画質が粗い。細かい部分が見えない。
768×768にしたら、約70ms。約14fps。画質は良いけど、fpsが低い。
結局、512×512にした。fpsを優先。
パフォーマンス
| 項目 | 値 |
|------|------|
| 解像度 | 512×512 |
| 推論時間 | 約50ms |
| FPS | 約20fps |
| メモリ使用量 | 約500MB(安定) |
20fpsは、動画としてはギリギリ滑らか。
スマホだと、推論に約150ms。約6fps。カクカクする。
スマホは、256×256にリサイズした。推論約80ms。約12fps。まあ許容範囲。
結果
動画の各フレームをリアルタイムで深度推定できた。
- 動画ファイルを読み込み、フレームをCanvasにキャプチャ
- MiDaS深度推定(512×512、約50ms)
- オリジナル、深度マップ、オーバーレイの3つを同時表示
- 約20fpsで安定動作
- メモリリーク対策でCanvas再利用
動きのある映像の奥行きが見える。面白い。
人が歩いてる動画だと、人の動きに合わせて深度マップも変化する。リアルタイムで深度が分かる。
まとめ
今回は、動画の各フレームをリアルタイムで深度推定する実験を行いました。
ポイントは以下の3つ:
- 動画フレームをCanvasにキャプチャ(512×512)
- フレームごとにMiDaS深度推定(約50ms、約20fps)
- メモリリーク対策でCanvas再利用
フレームレートとメモリ管理が重要だった。
動画の深度推定に興味がある方の参考になれば嬉しいです。
さらに深く学ぶには
この記事で興味を持った方におすすめのリンク:
自分の関連記事:
最後まで読んでくださり、ありがとうございました。