QTKit Captureを使ってみる - その8 [プログラミング - QTKit Capture]
前回はQTCaptureViewのかわりにQTCaptureDecompressedVideoOutputを使ってNSViewに描画して表示品質をあげることを試した。IIDCカメラではそれで全然問題ないけどDVストリーム(デジタルビデオカメラをiLink経由で繋いだとき)でおきるフレーム落ちとインターレースノイズをなんとかしたい。
DVストリーム表示の改善
もともとDVはどうでもよかったんだけど、うちのiMacで実験するにはDVしかないのでそれでやっているうちに気になり出した。なんとかするにはどうするか?
Core Videoをちょっとだけ
さっきのコードではCIImageで圧縮を解いたフレームデータを取得できた。でもそれをNSViewに描いては消し描いては消し、つまりフレームごとにオブジェクトを生成しては捨てながら表示している。さらにNSViewへの表示はすべてCPUを消費する。これをGPUのパイプラインに流すことができればCPUを消費しないし、メモリの消費も少なくなる。
先に言及したCore Videoはこれをやるフレームワークらしい。ちょっとガイドを見てみた。
Core Videoは最高の表示品質をめざす。それはすなわちディスプレイのリフレッシュレートに一致した画像の更新を最優先することと同義らしい。これはゲームコンソールの表示と同じ思想でいろいろなレイテンシ(入力に対する反応など)もリフレッシュレートを基準にしている。Core Videoではこれを「ディスプレイリンク」という優先順位の高いスレッドに乗ったタイマで実現して管理している。
Core Videoでは動画の表示をOpenGLのテクスチャとしてGPUのパイプラインに流す。従ってOpenGLのいろいろな表示機能をそのまま流用することができる。
逆に、Core Videoでは当然OpenGLを理解していなければいけないし、表示要求が優先されて、それに対してデータを与える、と言う処理のしかたになる。QTKit Captureの表示先とするには、入力フレームと表示要求の調停をする必要が出てくる。たいていの場合リフレッシュレートの方がQTKit Captureで得られるフレームレートよりも速いのが普通なので、表示要求に間に合わないフレームは古いフレームを渡すことになるのだろうけど、よくわからない(あまり勉強していない)。
Core Imageもちょっとだけ
これとは別に、Core Imageというフレームワークもある。こっちは(BezierPathではなく)ビットマップの表示をするためのフレームワークで、特にPhotoshopのプラグインのような画像のフィルタをリアルタイムで行うためのものらしい。
このフレームワークも最終的にはOpenGLを呼び出す、つまりGPUのパイプに流れる。でもCore Videoのようなリフレッシュレートに同期させる機能はない。そのため、OpenGLがあらわに顔を出すことは少ない。
Core Imageの方が今回の用途(何だったか忘れてしまいそうだけど、QTKitCaptureでのプレビューの表示品質を上げるのが目的だった)には合っているような気がする。
ということで、Core Imageを使ってみよう。うまくいけばデインターレースをCore Imageのフィルタを使って実現できるかもしれない(Photoshopには昔からデインターレースというフィルタがあるけどCore Imageには見当たらないな)。
Core Imageをもう少しまじめに
Core Imageを使うだけならあまり考えなくても済むようになっている。ガイドには処理のパスとかの絵があるけど抽象的で良くわからない。コードの例を見てみると
- CIContextを作る
- 表示したい画像をCIImageで取ってくる
- 画像フィルタのインスタンスを作る
- パラメータを設定する
- 画像をフィルタに対して指定する
- フィルタの結果をCIContextに指定して描画させる
フィルタは数珠つなぎ(カスケード)にすることができる。フィルタを使わなければ作ったCIContextにCIImageを指定するだけでいいみたい。
これだとOpenGLを知らなくてもいいし、NSViewのサブクラスの上に描くことができるらしい。
面白いのは、フィルタは最後のフィルタから元画像に向かって後ろ向きに処理されること。表示のために必要なピクセルを一つ指定すると、フィルタを経由してそのピクセルを作るのに必要なデータを元画像までたどることでピクセルの値を計算する。そうすれば数珠つなぎになったフィルタを、必要な部分だけ計算させることができる。
だから、CIContextへも、最後の結果を指定するだけになっている。
フィルタへの設定はNSDictionaryでキーに対する値としてか、キーバリューコーディングのスタイルで渡す。ガイドから引き写すと、デフォルトの設定値を
- (void)setDefaults;と言うメソッドを使ってロードさせて、必要なところだけをキーバリューのスタイルで渡せばよい。例えば
[hueAdjust setValue: [NSNumber numberWithFloat: 2.094] forKey: @"inputAngle"];はフィルタのオブジェクト(hueAdjust)にinputAngleというキーに対して2.094と言う値を設定している。
どの画像を入力に使うか、と言う設定も同じやり方を使う。例えば
[hueAdjust setValue: myCIImage forKey: @"inputImage"];このフィルタにmyCIImageと言うオブジェクトを入力画像として使え、と設定することになる。また、フィルタを適用した後の画像も
result = [hueAdjust valueForKey: @"outputImage"];として受ける。この結果の画像を次のフィルタの入力として設定することでフィルタの数珠つなぎができる。簡単。
さて、実際にコードを書いてみたい。でも、その前にもう寝る。
コメント 0