2012-01-12 12 views
7

मेरे पास एक ऐप है जो वाई चैनल को संसाधित करने के लिए केसीवी पिक्सेलफॉर्मैट टाइपइप 2020ppCbCr8BiPlanarFullRange प्रारूप में लाइव वीडियो कैप्चर करता है।kCVPixelFormatType_420YpCbCr8BiPlanarFullRange फ्रेम UIImage रूपांतरण

kCVPixelFormatType_420YpCbCr8BiPlanarFullRange द्वि-Planar घटक Y'CbCr 8 बिट: 4: 2: एप्पल के प्रलेखन के अनुसार 0, पूर्ण सीमा (लुमा = [0255] क्रोमा = [1,255])। baseAddr एक बड़े-एंडियन CVPlanarPixelBufferInfo_YCbCrBiPlanar संरचना को इंगित करता है।

मैं इन फ्रेमों में से कुछ को UIViewController में प्रस्तुत करना चाहता हूं, क्या कोई एपीआई केसीवीपीक्सेलफॉर्मैट टाइप 32_जीबीआरए प्रारूप में रूपांतरण करने के लिए है? क्या आप ऐप्पल द्वारा प्रदान की गई इस विधि को ट्यून करने के लिए कुछ संकेत दे सकते हैं?

// 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

मुझे आईओएस में एक द्विपक्षीय वाई/सीबीसीआर छवि को आरजीबी में बदलने के लिए किसी भी सुलभ अंतर्निहित तरीके से अवगत नहीं है। हालांकि आप अपने आप को सॉफ़्टवेयर में रूपांतरण करने में सक्षम होना चाहिए, उदा।

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

} 

बस इस बॉक्स और untested में प्रत्यक्ष रूप से लिखा है, मैं मैं सीबी/करोड़ निकासी सही मिल गया है लगता है। CGImage और इसलिए UIImage बनाने के लिए आप CGBitmapContextCreatergbBuffer के साथ उपयोग करेंगे।

+0

आरजीबी में रूपांतरण सिर्फ एक मध्यवर्ती कदम है, हां। आपके समाधान के साथ समस्या यह है कि यह एपीआई आईओएस 5.0 में उपलब्ध नहीं है। ImageWithCVPixelBuffer नामक एक विधि है जिसे मैं इसके बजाय उपयोग कर सकता हूं, लेकिन जैसा कि मेरा मूल प्रारूप द्विपक्षीय है, मेरे पास दो पिक्सेल बफर हैं। :-(वैसे भी धन्यवाद! – zapador

+0

क्षमा करें; मैंने सफारी में गलत पृष्ठ को बेवकूफ रूप से लोड किया था और आईओएस की बजाय सीआईएममेज के लिए ओएस एक्स प्रलेखन को देखकर वहां बैठा था। मैं एक नया उत्तर लिखूंगा। – Tommy

+0

टॉमी, आपके कोड के लिए धन्यवाद यह कुछ बदलावों के साथ एक चैंप की तरह काम करता है। मैं आपके लिए वोट देना चाहता हूं लेकिन मेरे पास पर्याप्त प्रतिष्ठा नहीं है। – zapador

10

अधिकांश कार्यान्वयनों मैं (पिछले जवाब यहाँ सहित) पाया काम नहीं करेगा अगर आप (मैं पूरी तरह से समझ में नहीं आता, CVPlanarPixelBufferInfo_YCbCrBiPlanar struct उस मामले में खाली हो जाएगा किसी कारण से) AVCaptureConnection में videoOrientation बदल सकता है इसलिए मैं एक लिखा है जो करता है (अधिकांश कोड this answer पर आधारित था)। मेरा कार्यान्वयन आरजीबी बफर में एक खाली अल्फा चैनल भी जोड़ता है और ध्वज का उपयोग करके CGBitmapContext बनाता है (कोई अल्फा डेटा नहीं है, लेकिन आईओएस को प्रति पिक्सेल 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