LoginSignup
50

More than 5 years have passed since last update.

簡単にできるCMSampleBufferRef→UIImage変換(420vもOK)

Posted at

AVFoundationで頻繁にお世話になるCMSampleBufferRef型のサンプルバッファを、UIImageに変換したい場合の高速&低負荷&シンプルな方法が見つかったので、メモとして残します。

// サンプルバッファからピクセルバッファを取り出す
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// ピクセルバッファをベースにCoreImageのCIImageオブジェクトを作成
CIImage *ciimage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
// CIImageからUIImageを作成
UIImage *image = [UIImage imageWithCIImage:ciimage];

なんとたったの3ステップ。iPhone 6(iOS 8.1.3)だと、720pの420vなサンプルバッファを僅か1ms程度(※後注参照)でUIImageに変換してくれます。いったんUIImageになってしまえばUIImageJPEGRepresentation()でJPEG画像にするのも簡単ですね。

もう、Appleのこのサンプルコードが使いたいがためにビデオフレームをBGRAフォーマットにしてパフォーマンスダウンに悩まされたり、シェーダーでやらせようとしたらglReadPixels()が遅いのでOpenGL ES3.0に手を出したり、GPUImageがやっているようなIOサーフェスを使った方法に手を出したりして返り討ちにあうこともありません。

ちなみに、サンプルバッファの一部だけ取り出したい場合はCIContextクラスのcreateCGImage:fromRect:メソッドを使うのがお手軽です。いったんCGImageを作ってから、UIImageを作成します。

// ピクセルバッファからCIImageオブジェクトを作成
CIImage *ciimage = [CIImage imageWithCVPixelBuffer:pixelBuffer];

// レンダリングコンテキストを作成して、レンダリング
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef cgimage = \
    [context createCGImage:ciimage
                  fromRect:CGRectMake(0, 0,
                               CVPixelBufferGetWidth(pixelBuffer),
                               CVPixelBufferGetHeight(pixelBuffer))];
// UIImageを作成
UIImage *uiimage = [UIImage imageWithCGImage:cgimage];

この方法は、CIFilterクラスでピクセルバッファにエフェクトをかけたい場合にも使えます。

スケール・回転させたい場合は、imageWithCIImage:scale:orientation:メソッドを使うとよいでしょう。

注意

ピクセルバッファを使ってCIImage化するときの注意点として、CIImageの画像はただちに作られるのではなく、必要になったタイミングでレンダリングされる遅延評価の仕組みで作られていることは注意してください。
CIImage化したまましばらく保持しておくと、pixelBufferはいつまでも保持されたまま(フレームワークが使えないまま)になってしまいますし、場合によっては保持している内容が書き換わってしまうかもしれません。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50