これは旧eviry tech blogから移行した記事です。
fujiwaraです。
HTML5 videoタグにより動画の再生が簡単に行うことができる昨今ですが、もう少し踏み込んでいろんな(変わった)ことをやっていきたいと思います。
今回はクライアントサイドのみ動作するシーンチェンジ検出の処理を実装してみました。
内容
完成品はこちら
videoタグに読み込まれた動画の次/前のシーンの替わり目を動的に検出して、その位置にシークします。
(正確にはシークしながら替わり目を検出します)
解説
videoタグ、canvasタグ、javascript、動画ファイルのみで構成されており、クライアントサイドの動作のみで機能を実現しています。 事前にサーバ側で動画をスキャンしてシーン位置を検出する、といったことは行っていません。
では、どのような形で実装されているか、詳しく見ていきます。
画像の取り出し
videoタグの現在のフレームをcanvasに書き出します。
この処理は以下のサイトを参考にさせて頂きました。
videoタグの動画をcanvasを使って表示する
次に描画したフレームからピクセルデータを取得できるようにします。
function getImage() { return getContext().getImageData(0, 0, 480, 270); }
getImageData() で取得できるImageDataオブジェクトによって各ピクセルの色情報を取得することができます。
前後のフレームの比較
前後のフレームのImageDataの差分をとって、差が大きければシーンチェンジとみなします。 以下の関数でこの比較を行います。
function compareImage(src, target) {
ImageData.data
でピクセルデータ[RGBA]の配列を取得することができます。
前後のフレームでこの値の差分を合計します。
for (var y = 0; y < height; y += distance) { for (var x = 0; x < width; x += distance) { diff += Math.abs(src.data[src_pos++] - target.data[target_pos++]); // R diff += Math.abs(src.data[src_pos++] - target.data[target_pos++]); // G diff += Math.abs(src.data[src_pos++] - target.data[target_pos++]); // B src_pos ++; // alpha target_pos ++; // alpha } }
R,G,Bそれぞれの値の差分を全部合計していきます。
なお、全ピクセルで計算してもいいのですが処理に時間がかかるため、縦横それぞれdistance = 16
ピクセル間隔で間引いて計算しています。
diff_per_pixel = diff / pixel_count / 3; if (diff_per_pixel > threshold) { return true;
差分が閾値を超えた場合、シーンチェンジされた扱いになります。
補足
実際にシークしながら出ないと動作しないため、次のシーンまでコマ送り状態で表示されてしまうのが、ちょっとかっこ悪いですね。
また、シーク〜フレーム取得のタイミングによって正しくImageDataを取得できないことがあるらしく、環境によってはシーン位置以外でも止まってしまうことがあるようです。
最後に
あくまでも実験的に作ってみました。実用に堪えるものではないですが、以下のようなことがわかって頂けたらいいなと思います。 - canvasタグを用いた動画の加工(取得)方法:これを使えばエフェクトなんかも作れるかも。 - 簡単シーンチェンジ検出:こんな単純な実装でもそれなりに検出可能なんです。