2

I'm trying to convert a kCVPixelFormatType_420YpCbCr8BiPlanarFullRange to RGB so I can save the image in a JPG file, this is the code I'm using:

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

- (UIImage *)makeUIImage:(uint8_t *)inBaseAddress bufferInfo:(CVPlanarPixelBufferInfo_YCbCrBiPlanar *)inBufferInfo width:(size_t)inWidth height:(size_t)inHeight bytesPerRow:(size_t)inBytesPerRow
{
    NSUInteger yOffset = EndianU32_BtoN(inBufferInfo->componentInfoY.offset);
    NSUInteger yPitch = EndianU32_BtoN(inBufferInfo->componentInfoY.rowBytes);

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

    uint8_t *rgbBuffer = malloc(inWidth * inHeight * 4);
    uint8_t *yBuffer = inBaseAddress + yOffset;
    uint8_t *cbCrBuffer = inBaseAddress + cbCrOffset;

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

        for(int x = 0; x < inWidth; x++)
        {
            uint8_t y = yBufferLine[x];
            uint8_t cb = cbCrBufferLine[x & ~1];
            uint8_t cr = cbCrBufferLine[x | 1];

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

            int r = (int)round( (y - 16) * 1.164 + (cr - 128) *  1.596 );
            int g = (int)round( (y - 16) * 1.164 + (cb - 128) * -0.391 + (cr - 128) * -0.813 );
            int b = (int)round( (y - 16) * 1.164 + (cb - 128) *  2.018 );

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

        }
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(rgbBuffer, yPitch, inHeight, 8,
                                                 yPitch*4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);

    CGImageRef quartzImage = CGBitmapContextCreateImage(context);

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    UIImage *image = [UIImage imageWithCGImage:quartzImage];

    CGImageRelease(quartzImage);
    free(rgbBuffer);
    return  image;
}

-(void)createImg:(CMSampleBufferRef) sampleBuffer index:(int)index
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    //Lock the imagebuffer
    CVPixelBufferLockBaseAddress(imageBuffer,0);

    // Get information about the image
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);

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

    CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;

    baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);


    // convert the image
    UIImage *img = [self makeUIImage:baseAddress bufferInfo:bufferInfo width:width height:height bytesPerRow:bytesPerRow];
    NSString *videoThumbPath = [NSString
                                stringWithFormat:@"%@/img%02d.jpg",
                                burstFolderPath,
                                index];

    NSData *imageData = [NSData dataWithData:UIImageJPEGRepresentation(img,0.8f)];
    [imageData writeToFile:videoThumbPath atomically:YES];
}

The image is generated but the colours are wrong, the picture is bluish,and furthermore I seem to have a break at right side of the picture: enter image description here

What am I doing wrong?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1
    First, it's really hard to tell anything from your picture. We have no frame of reference for what it's supposed to look like. What did the original look like? Second - does [this question and answer](http://stackoverflow.com/questions/7041172/pils-colour-space-conversion-ycbcr-rgb?rq=1) help at all? – user1118321 Jun 09 '14 at 04:35
  • You probably should convert image in some app and include it here as expected result. – Timur Kuchkarov Aug 28 '14 at 09:54

1 Answers1

2
// Hope it can help someone!

int bytesPerPixel = 4;

uint8_t *rgbBuffer = (uint8_t *)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);

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