2012-01-12 16 views
7

Y kanalını işlemek için kCVPixelFormatType_420YpCbCr8BiPlanarFullRange biçiminde canlı video çeken bir uygulamanız var.kCVPixelFormatType_420YpCbCr8BiPlanarFullRisge frame to UIImage dönüşümü

kCVPixelFormatType_420YpCbCr8BiPlanarFullRange çift düzlemsel Bileşen Y'CbCr 8-bit 4: 2: Apple belgelerine göre 0, tam aralığı (luma = [0255] kroma = [1.255]). baseAddr, büyük bir CVPlanarPixelBufferInfo_YCbCrBiPlanar yapısına işaret ediyor.

Bu karelerden bazılarını bir UIViewController'da sunmak istiyorum, kCVPixelFormatType_32BGRA biçimine dönüştürmeyi yapmak için herhangi bir API var mı? Apple tarafından sağlanan bu yöntemi ayarlamak için biraz ipucu verebilir misiniz?

// 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); 
} 

Teşekkür ederiz!

cevap

14

iOS'ta çift planar Y/CbCr görüntüsünü RGB'ye dönüştürmek için erişilebilen yerleşik bir yolun farkında değilim. Ancak, dönüştürme işlemini kendiniz yazılımda gerçekleştirebilmelisiniz, ör. Sadece bu kutu ve denenmemiş doğrudan yazılı

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); 
    } 

} 

, ben cb/cr çıkarma doğru buldum. Daha sonra CGImage ve dolayısıyla UIImage oluşturmak için CGBitmapContextCreate ile rgbBuffer kullanın.

+0

RGB'ye dönüştürme, yalnızca bir ara adımdır, evet. Çözümünüzdeki sorun, bu API'nın iOS 5.0'da mevcut olmamasıdır. Bunun yerine kullanabileceğim imageWithCVPixelBuffer adlı bir yöntem var, ancak orijinal formatım biplanar olduğu için iki piksel arabellek var. :-(Yine de teşekkürler! – zapador

+0

Üzgünüz, ben yanlış bir şekilde Safari'de yanlış sayfayı yükledim ve orada oturdum, OS yerine CIImage belgelerine bakıyordum. Yeni bir cevap yazacağım. – Tommy

+0

Tommy, kodun için teşekkürler. Birkaç değişiklikle bir şampiyon gibi çalışır.Benim için oy vermek istiyorum ama yeterli bir itibarım yok – zapador

10

Çoğu uygulamalar Sana (ben tam, CVPlanarPixelBufferInfo_YCbCrBiPlanar yapı bu durumda boş olacak anlamıyorum nedense) AVCaptureConnection içinde videoOrientation değiştirirseniz çalışmaz (burada önceki cevabı dahil) buldum, o yüzden (Bu kodun çoğu this answer dayanıyordu) bir yazdı. Uygulamam ayrıca RGB arabelleğine boş bir alfa kanalı ekler ve kCGImageAlphaNoneSkipLast bayrağını kullanarak CGBitmapContext'u oluşturur (alfa verisi yoktur, ancak iOS piksel başına 4 bayta ihtiyaç duyar). İşte bu:

#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

Bunun için çok teşekkürler !!!!!!!!!! –

+0

Teşekkürler Bu benim sorunumu çözer! – Linjie

+0

Bu harika çalışıyor, kolayca UIImage çerçevelerden alabilirsiniz, ancak aşağıdaki kodla CGImageRef'in bitmap piksel verilerini alırken bir sorunla karşılaşıyorum. ama kodunuzdan elde edilen UIImage iyi görünüyor, bitmap verisini aldıktan sonra onun tek sorunu '' '' '' '''' CGDataProviderCopyData (CGImageGetDataProvider (quartzImage)); ' Orijinal görüntüm beyaz renktir, kodun sonucu olan görüntüler mor renktedir. sorunu al – Trident