2011-11-09 18 views
23

CVPixelBufferからUIIMageを取得する際に問題が発生します。CVPixelBufferをUIImageにするには?

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(imageDataSampleBuffer); 
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer options:(NSDictionary *)attachments]; 
if (attachments) 
    CFRelease(attachments); 
size_t width = CVPixelBufferGetWidth(pixelBuffer); 
size_t height = CVPixelBufferGetHeight(pixelBuffer); 
if (width && height) { // test to make sure we have valid dimensions 
    UIImage *image = [[UIImage alloc] initWithCIImage:ciImage]; 

    UIImageView *lv = [[UIImageView alloc] initWithFrame:self.view.frame]; 
    lv.contentMode = UIViewContentModeScaleAspectFill; 
    self.lockedView = lv; 
    [lv release]; 
    self.lockedView.image = image; 
    [image release]; 
} 
[ciImage release]; 

heightwidthが両方正しくカメラの解像度に設定されています。これは私がしようとしていますものです。 imageが作成されていますが、私はそれが黒(または透明かもしれません)のようです。問題がどこにあるのかよく分かりません。任意のアイデアをいただければ幸いです。

+0

間違いなく、CIImageを間に挿入します。いくつかの中間CIFilterを投げ込むか、CGBitmapContextCreate - > UIImageに行くだけでよいでしょうか? – Tommy

+0

今のところ、私はそれをビューに表示し、私が何を扱っているかを見たいだけです。道を歩いて、私はピクセルで遊んでみたいです。 – mahboudz

答えて

36

まず、あなたの質問に直接関係しない明白なもの:AVCaptureVideoPreviewLayerは、データがどこから来ているのかとは関係なく、いずれかのカメラのビデオを独立したビューにパイプする最も安い方法ですそれを直ちに変更する予定です。プレビューレイヤーはAVCaptureSessionに直接接続され、更新されます。

私は中心的な質問について自信がないことを認めなければなりません。 CIImageとその他の2種類の画像の意味には違いがあります。CIImageは画像のレシピであり、必ずしもピクセルで裏付けされているわけではありません。 「ここからピクセルを取り出し、このように変換し、このフィルタを適用し、このように変換し、この他の画像とマージしてこのフィルタを適用する」のようなものにすることができます。システムは、レンダリングを選択するまで、CIImageの外観を認識しません。また、それをラスタライズする適切な範囲を本質的には知っていません。

UIImageは、単にCIImageをラップすると言います。ピクセルに変換しません。おそらくUIImageViewがそれを達成するはずですが、そうであれば、適切な出力矩形をどこに供給するかを見つけることができません。私はちょうど問題を回避逃れ成功を収めてきた

CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer]; 

CIContext *temporaryContext = [CIContext contextWithOptions:nil]; 
CGImageRef videoImage = [temporaryContext 
        createCGImage:ciImage 
        fromRect:CGRectMake(0, 0, 
          CVPixelBufferGetWidth(pixelBuffer), 
          CVPixelBufferGetHeight(pixelBuffer))]; 

UIImage *uiImage = [UIImage imageWithCGImage:videoImage]; 
CGImageRelease(videoImage); 

では、出力矩形を指定するための明白な機会を与えてくれます。 CGImageを仲介者として使用せずに経路があると確信していますので、この解決策がベストプラクティスであるとは思わないでください。

+0

ありがとう、私はそれを試してみましょう。 previewLayerが役に立たない理由は、私はもっと解像度が必要だからです。そして、私がjpegプレゼンテーションの代わりにCIIImageを使う理由は、jpeg圧縮が重要なアーティファクトを追加しているかどうかを確認することです。私は、実際には、アーティファクトが最小限であればjpegで留まることを選択するかもしれません。 – mahboudz

+0

これは機能します。ありがとう! – mahboudz

11

UIImageを取得する別の方法。実行〜10倍の速さ、少なくとも私の場合:

int w = CVPixelBufferGetWidth(pixelBuffer); 
int h = CVPixelBufferGetHeight(pixelBuffer); 
int r = CVPixelBufferGetBytesPerRow(pixelBuffer); 
int bytesPerPixel = r/w; 

unsigned char *buffer = CVPixelBufferGetBaseAddress(pixelBuffer); 

UIGraphicsBeginImageContext(CGSizeMake(w, h)); 

CGContextRef c = UIGraphicsGetCurrentContext(); 

unsigned char* data = CGBitmapContextGetData(c); 
if (data != NULL) { 
    int maxY = h; 
    for(int y = 0; y<maxY; y++) { 
     for(int x = 0; x<w; x++) { 
     int offset = bytesPerPixel*((w*y)+x); 
     data[offset] = buffer[offset];  // R 
     data[offset+1] = buffer[offset+1]; // G 
     data[offset+2] = buffer[offset+2]; // B 
     data[offset+3] = buffer[offset+3]; // A 
     } 
    } 
} 
UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 

UIGraphicsEndImageContext(); 
+0

インクリメントポインタを使用すると、速度向上もわずかに得られます – jjxtra

+6

CVPixelBufferGetBaseAddressの呼び出しの前にCVPixelBufferLockBaseAddressの呼び出しを挿入し、データコピーの後にCVPixelBufferUnlockBaseAddressを呼び出す必要があります。また、CVPixelBufferGetDataSizeとmemcpy()を使用してデータの単一ブロックコピーを実行することも考えてください。 –

8

をお使いの画像データがスウィズルまたは変換が必要、いくつかの異なるフォーマットである場合を除き - 私は何の何のインクリメントをお勧めしないだろう...ちょうどあなたにデータをピシャリコンテキストメモリ領域は次のようになります。

//not here... unsigned char *buffer = CVPixelBufferGetBaseAddress(pixelBuffer); 

UIGraphicsBeginImageContext(CGSizeMake(w, h)); 

CGContextRef c = UIGraphicsGetCurrentContext(); 

void *ctxData = CGBitmapContextGetData(c); 

// MUST READ-WRITE LOCK THE PIXEL BUFFER!!!! 
CVPixelBufferLockBaseAddress(pixelBuffer, 0); 
void *pxData = CVPixelBufferGetBaseAddress(pixelBuffer); 
memcpy(ctxData, pxData, 4 * w * h); 
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); 

... and so on... 
+0

CGImageCreateパスと比較して古いデバイスでは〜50%fpsブーストしています。ありがとう! –

+2

CVPixelBufferの行の最後にはパディングバイトが多いことが多いので注意してください。私。 CVPixelBufferGetBytesPerRowが期待以上に大きいかもしれません。コピーした画像の出力はすべてスラントに見えます。 – Baxissimo

3

以前の方法ではCGラスタデータリークが発生しました。この変換方法は私のために漏れませんでした:

@autoreleasepool { 

    CGImageRef cgImage = NULL; 
    OSStatus res = CreateCGImageFromCVPixelBuffer(pixelBuffer,&cgImage); 
    if (res == noErr){ 
     UIImage *image= [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp]; 

    } 
    CGImageRelease(cgImage); 
} 


    static OSStatus CreateCGImageFromCVPixelBuffer(CVPixelBufferRef pixelBuffer, CGImageRef *imageOut) 
    { 
     OSStatus err = noErr; 
     OSType sourcePixelFormat; 
     size_t width, height, sourceRowBytes; 
     void *sourceBaseAddr = NULL; 
     CGBitmapInfo bitmapInfo; 
     CGColorSpaceRef colorspace = NULL; 
     CGDataProviderRef provider = NULL; 
     CGImageRef image = NULL; 

     sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); 
     if (kCVPixelFormatType_32ARGB == sourcePixelFormat) 
      bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipFirst; 
     else if (kCVPixelFormatType_32BGRA == sourcePixelFormat) 
      bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst; 
     else 
      return -95014; // only uncompressed pixel formats 

     sourceRowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer); 
     width = CVPixelBufferGetWidth(pixelBuffer); 
     height = CVPixelBufferGetHeight(pixelBuffer); 

     CVPixelBufferLockBaseAddress(pixelBuffer, 0); 
     sourceBaseAddr = CVPixelBufferGetBaseAddress(pixelBuffer); 

     colorspace = CGColorSpaceCreateDeviceRGB(); 

     CVPixelBufferRetain(pixelBuffer); 
     provider = CGDataProviderCreateWithData((void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBuffer); 
     image = CGImageCreate(width, height, 8, 32, sourceRowBytes, colorspace, bitmapInfo, provider, NULL, true, kCGRenderingIntentDefault); 

     if (err && image) { 
      CGImageRelease(image); 
      image = NULL; 
     } 
     if (provider) CGDataProviderRelease(provider); 
     if (colorspace) CGColorSpaceRelease(colorspace); 
     *imageOut = image; 
     return err; 
    } 

    static void ReleaseCVPixelBuffer(void *pixel, const void *data, size_t size) 
    { 
     CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)pixel; 
     CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); 
     CVPixelBufferRelease(pixelBuffer); 
    } 
0

これをSwiftで試してください。

extension UIImage { 
    public convenience init?(pixelBuffer: CVPixelBuffer) { 
     var cgImage: CGImage? 
     VTCreateCGImageFromCVPixelBuffer(pixelBuffer, nil, &cgImage) 

     if let cgImage = cgImage { 
      self.init(cgImage: cgImage) 
     } else { 
      return nil 
     } 
    } 
} 

注:RGBピクセルバッファのではなく、グレースケールでしか動作。

関連する問題