HTML5でビデオのリアルタイムヒストグラムを作ってみた

デジカメを使っていると、撮影直後にヒストグラムを見て黒潰れや白飛びなどをチェックする癖がちょっとは身についたが、デジカメはもちろんのこと、今時のテレビにもヒストグラムをリアルタイムで表示する機能が付いているものもある。今回、HTML5のvideoタグの勉強がてら、再生中のビデオのヒストグラムをリアルタイムで表示するJavaScriptコードを書いてみた。

リアルタイムといってもJavaScriptのパフォーマンスに依存するので全部のフレームがヒストグラム表示されるとは限らない。計算自体は単純だが、画素数が多いとそれなりに負荷がかかる。ちなみに、某社のテレビでヒストグラム表示機能があるので確認したら、ゆったりとした動きだった。

HTML5のビデオからそのヒストグラムを生成する方法はとても簡単だ。まず、再生中のビデオのピクセル情報を取得するため、一旦、canvas上にdrawImage()でビデオフレームを描画する。そして、そのcanvas上のデータをgetImageData()でRGBAのピクセル情報を取得すればよい。このcanvasは画面上に表示する必要はないので、styleのdisplayプロパティをnoneに設定しておく。

その際、canvas上にビデオをdrawImage()するときに縮小しておくのがポイント。その後のJavaScriptによるヒストグラム計算も軽くなる。フルHDビデオのピクセルを全部JavaSccriptでスキャンするのはさすがに重い。縮小したことによるデータ欠損はあるが、ヒストグラムが大きく変わることはないだろう。

RGBそれぞれのヒストグラム表示とモノクロにしたときの輝度のヒストグラムを表示できるようにした。下記はRGBと輝度を同時に重ねて表示したときの様子。ちょと見づらいが、リアルタイムでヒストグラムがうねうね動くのを見るのはなかなか面白い。ヒストグラム自身はvideo上に重ねたcanvasに描画している。

RGBをそれぞれ分離して表示したときは下のようになる。

輝度はRGBからYUV変換のY成分を利用している。RGBからY成分を求めるのはいろいろ方法があるようだが、ここでは以下の式を用いた。

Y = 0.298912 * R + 0.586611 * G + 0.114478 * B

黄色で表示したものが輝度に相当する。上とシーンを合わせればよかったと今思うが面倒なのでこのままにしておく。

下は実際にY成分だけを画像にしてcanvasに表示したもの。これもリアルタイムでビデオに合わせて表示される。


ヒストグラムの表示計算周期は、setInterval()でdurationを1に設定。サンプルで使ったビデオは24fpsなので、必要以上の更新をしないように、ビデオのcurrentTimeと前回計算した時刻を比較して同じならば何もしないようにしている。それなりに高速なPCでは320x240程度の画素ならば軽くフレームレートより高速に計算できるようだ。

CPU使用率をパフォーマンスモニタで見たところ、単純にビデオを再生しているときと比較して、ヒストグラムを表示しているときは、負荷が少しは高くなるが、思ったほどではなかった。

Firefox4.0でビデオだけを単純に再生したときの様子。僕の手元のノートPC(Dynabook RX3; Intel Core i5 2.67GHz)ではだいたい、こんな感じで落ち着いている。

ヒストグラムを表示しているときの様子。多少上がった程度。

Chromeでもだいたい同じだったが、多少Chromeの方がCPU usageが高くなっていた。いずれにしても、CPUが遊んでいるので、実は、ヒストグラムの計算をもっと軽く高速にできるようにWebWorkerを使って実行するコードも書いたけど、ビデオのカクカクして、まともに見れない再生となってしまった。僕の使い方がいけないのだと思うがよくわからない。改めてWebWorkerの振る舞いについて詳細に調査してみる積り。

実際の動作とJavaScriptコードは以下のURLから見ることができます。ビデオはよく知られたクリエティブ・コモンズのも。解像度はSintelは1024x436、Big Buck Bunnyは854x480。どちらもOggフォーマット。Firefox4.0とChrome10.0.648.204でしか動作確認していない。また、遅いPCや環境だと再生までちょっと時間がかかるようだ。YouTubeフルHDがサクサク再生できないようなPCだと厳しいかもしれない。

再生までにかなりの時間がかかるが、もしかしたら、ストリーミング再生に適していないエンコードをしているのかも。再生中にネットワークを切断したところ、FirefoxChromeもかなりのところまで再生が継続した。キャッシュに十分ためないと再生できないということか?しかも、Chromeはエラーがでてタブが死んでしまった。調べてると面白いものがありそうだ。

JavaScriptAPIを使ってvideoやcanvasでビデオのアプリケーションを作るのはそれほど難しいことじゃないけれど、はやり、ビデオのコンテナやコーデックについてちゃんと理解しておいた方がよさそうだ。Sintelのビデオの方には字幕が六ヶ国語も入っている。VLCメディアプレイヤーでは字幕も表示されるが、HTML5のvideoタグでは表示できるのだろうか。複数トラックもどこまで対応しているのか。勉強すべきことはたくさんある。