2012-01-12 33 views
7

私は、kCVPixelFormatType_420YpCbCr8BiPlanarFullRangeフォーマットでライブビデオをキャプチャしてYチャンネルを処理するアプリを持っています。kCVPixelFormatType_420YpCbCr8BiPlanarFullRangeフレームからUIImageへの変換

kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 2平面コンポーネントY'CbCr 8ビット4:2:0、フルレンジ(輝度= [0,255]彩度= [1255])、Appleのマニュアルに従って。 baseAddrは、ビッグエンディアンのCVPlanarPixelBufferInfo_YCbCrBiPlanar構造体を指します。

これらのフレームの一部をUIViewControllerに表示したいのですが、kCVPixelFormatType_32BGRA形式に変換するAPIはありますか? Appleが提供するこの方法を調整するヒントを教えてください。

// Create a UIImage from sample buffer data 
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer { 
    // Get a CMSampleBuffer's Core Video image buffer for the media data 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    // Lock the base address of the pixel buffer 
    CVPixelBufferLockBaseAddress(imageBuffer, 0); 

    // Get the number of bytes per row for the pixel buffer 
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 

    // Get the number of bytes per row for the pixel buffer 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    // Create a bitmap graphics context with the sample buffer data 
    CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, 
               bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    // Create a Quartz image from the pixel data in the bitmap graphics context 
    CGImageRef quartzImage = CGBitmapContextCreateImage(context); 
    // Unlock the pixel buffer 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 

    // Free up the context and color space 
    CGContextRelease(context); 
    CGColorSpaceRelease(colorSpace); 

    // Create an image object from the Quartz image 
    UIImage *image = [UIImage imageWithCGImage:quartzImage]; 

    // Release the Quartz image 
    CGImageRelease(quartzImage); 

    return (image); 
} 

ありがとうございました!

答えて

14

私は、biplanar Y/CbCr画像をiOSのRGBに変換するためのアクセス可能な組み込みの方法については気づきません。しかし、あなた自身がソフトウェアで変換を実行できるはずです。

uint8_t clamp(int16_t input) 
{ 
    // clamp negative numbers to 0; assumes signed shifts 
    // (a valid assumption on iOS) 
    input &= ~(num >> 16); 

    // clamp numbers greater than 255 to 255; the accumulation 
    // of the mask looks odd but is an attempt to avoid 
    // pipeline stalls 
    uint8_t saturationMask = num >> 8; 
    saturationMask |= saturationMask << 4; 
    saturationMask |= saturationMask << 2; 
    saturationMask |= saturationMask << 1; 
    num |= saturationMask; 

    return num&0xff; 
} 

... 

CVPixelBufferLockBaseAddress(imageBuffer, 0); 

size_t width = CVPixelBufferGetWidth(imageBuffer); 
size_t height = CVPixelBufferGetHeight(imageBuffer); 

uint8_t *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 
CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress; 

NSUInteger yOffset = EndianU32_BtoN(bufferInfo->componentInfoY.offset); 
NSUInteger yPitch = EndianU32_BtoN(bufferInfo->componentInfoY.rowBytes); 

NSUInteger cbCrOffset = EndianU32_BtoN(bufferInfo->componentInfoCbCr.offset); 
NSUInteger cbCrPitch = EndianU32_BtoN(bufferInfo->componentInfoCbCr.rowBytes); 

uint8_t *rgbBuffer = malloc(width * height * 3); 
uint8_t *yBuffer = baseAddress + yOffset; 
uint8_t *cbCrBuffer = baseAddress + cbCrOffset; 

for(int y = 0; y < height; y++) 
{ 
    uint8_t *rgbBufferLine = &rgbBuffer[y * width * 3]; 
    uint8_t *yBufferLine = &yBuffer[y * yPitch]; 
    uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch]; 

    for(int x = 0; x < width; x++) 
    { 
     // from ITU-R BT.601, rounded to integers 
     uint8_t y = yBufferLine[x] - 16; 
     uint8_t cb = cbCrBufferLine[x & ~1] - 128; 
     uint8_t cr = cbCrBufferLine[x | 1] - 128; 

     uint8_t *rgbOutput = &rgbBufferLine[x*3]; 

     rgbOutput[0] = clamp(((298 * y + 409 * cr - 223) >> 8) - 223); 
     rgbOutput[1] = clamp(((298 * y - 100 * cb - 208 * cr + 136) >> 8) + 136); 
     rgbOutput[2] = clamp(((298 * y + 516 * cb - 277) >> 8) - 277); 
    } 

} 

ただ、このボックスに直接書かれており、テストされていない、私は私が正しいCB/CR抽出を持っていると思います。その後、CGBitmapContextCreateをと入力してCGImageを作成し、UIImageを作成します。

+0

RGBへの変換はちょうど中間段階です。あなたのソリューションの問題は、このAPIがiOS 5.0で利用できないということです。私が代わりに使うことができるimageWithCVPixelBufferというメソッドがありますが、私の元のフォーマットがbiplanarなので、私は2つのピクセルバッファを持っています。 :-(ありがとうと思います) – zapador

+0

申し訳ありませんが、Safariで間違ったページを読み込んでしまって、iOSではなくCIImageのOS Xのドキュメントを見ていました。新しい回答を書きます。 – Tommy

+0

Tommy、あなたのコードをありがとう – zapador

10

あなたはAVCaptureConnectionvideoOrientationを変更した場合(ここでは前の回答を含む)私が見つけたほとんどの実装では動作しません(私は完全には理解していない何らかの理由で、CVPlanarPixelBufferInfo_YCbCrBiPlanar構造体が、その場合には空になります)ので、私(ほとんどのコードはthis answerに基づいていました)。また、RGBバッファに空のアルファチャンネルを追加し、kCGImageAlphaNoneSkipLastフラグを使用してを作成します(アルファデータはありませんが、iOSには1ピクセルあたり4バイトが必要です)。ここにあります:

#define clamp(a) (a>255?255:(a<0?0:a)) 

- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer { 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer,0); 

    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 
    uint8_t *yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); 
    size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0); 
    uint8_t *cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1); 
    size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1); 

    int bytesPerPixel = 4; 
    uint8_t *rgbBuffer = malloc(width * height * bytesPerPixel); 

    for(int y = 0; y < height; y++) { 
     uint8_t *rgbBufferLine = &rgbBuffer[y * width * bytesPerPixel]; 
     uint8_t *yBufferLine = &yBuffer[y * yPitch]; 
     uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch]; 

     for(int x = 0; x < width; x++) { 
      int16_t y = yBufferLine[x]; 
      int16_t cb = cbCrBufferLine[x & ~1] - 128; 
      int16_t cr = cbCrBufferLine[x | 1] - 128; 

      uint8_t *rgbOutput = &rgbBufferLine[x*bytesPerPixel]; 

      int16_t r = (int16_t)roundf(y + cr * 1.4); 
      int16_t g = (int16_t)roundf(y + cb * -0.343 + cr * -0.711); 
      int16_t b = (int16_t)roundf(y + cb * 1.765); 

      rgbOutput[0] = 0xff; 
      rgbOutput[1] = clamp(b); 
      rgbOutput[2] = clamp(g); 
      rgbOutput[3] = clamp(r); 
     } 
    } 

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef context = CGBitmapContextCreate(rgbBuffer, width, height, 8, width * bytesPerPixel, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); 
    CGImageRef quartzImage = CGBitmapContextCreateImage(context); 
    UIImage *image = [UIImage imageWithCGImage:quartzImage]; 

    CGContextRelease(context); 
    CGColorSpaceRelease(colorSpace); 
    CGImageRelease(quartzImage); 
    free(rgbBuffer); 

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0); 

    return image; 
} 
+0

ありがとう!!!!!!!!!!! –

+0

これは私の問題を解決してくれてありがとう! – Linjie

+0

これは素晴らしいです、私は簡単にフレームからUIImageを得ることができますが、下のコードでCGImageRefからビットマップピクセルデータを取得中に問題に直面しています。しかし、あなたのコードから取得したUIImageは、ビットマップデータを取得した後の唯一の問題です。 'CGDataProviderCopyData(CGImageGetDataProvider(quartzImage));' 私のオリジナル画像は白色です。コードの結果画像は紫色です。問題を起こす? – Trident

関連する問題