SSブログ

QTKit Captureを使ってみる - その8 [プログラミング - QTKit Capture]

ムービー入力のためのフレームワークであるQTKit Catpureをちょこっと使ってみた話の続き。仕事で夜遅いのに、一日置くとすぐ何をやっていたか忘れてしまうので反芻する必要がある。
前回は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を使うだけならあまり考えなくても済むようになっている。ガイドには処理のパスとかの絵があるけど抽象的で良くわからない。コードの例を見てみると

  1. CIContextを作る
  2. 表示したい画像をCIImageで取ってくる
  3. 画像フィルタのインスタンスを作る
  4. パラメータを設定する
  5. 画像をフィルタに対して指定する
  6. フィルタの結果をCIContextに指定して描画させる
ということになるみたい。CIContextというのは描画対象となるなにかを表す抽象的なオブジェクトで、NSViewなんかの描画対象とそのときの状態を一緒に保持していると言うイメージで、Quartzでもこの名前(CGContextという)が使われている。AppKitだけを使っていたら全く気にしないけど、QuartzではどのContextに描くか、というのを必ず指定するようになっている。これはCore Imageでも同じらしい。

フィルタは数珠つなぎ(カスケード)にすることができる。フィルタを使わなければ作った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"]; 
として受ける。この結果の画像を次のフィルタの入力として設定することでフィルタの数珠つなぎができる。簡単。

さて、実際にコードを書いてみたい。でも、その前にもう寝る。


nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立09/02献立0903 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。