将UIImage转换为CVImageBufferRef

16

这段代码大部分工作正常,但生成的数据似乎丢失了一种颜色通道(这是我想的),因为当显示生成的图像数据时会带有蓝色色调!

以下是代码:

UIImage* myImage=[UIImage imageNamed:@"sample1.png"];
CGImageRef imageRef=[myImage CGImage];
CVImageBufferRef pixelBuffer = [self pixelBufferFromCGImage:imageRef];

方法pixelBufferFromCGIImage是从stackoverflow的另一篇帖子中获取的,链接在此处:How do I export UIImage array as a movie?(尽管这个应用与我所尝试做的无关),它就是

+ (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image
{
    CGSize frameSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
    NSDictionary *options = @{
                              (__bridge NSString *)kCVPixelBufferCGImageCompatibilityKey: @(NO),
                              (__bridge NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey: @(NO)
                              };
    CVPixelBufferRef pixelBuffer;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width,
                                          frameSize.height,  kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
                                          &pixelBuffer);
    if (status != kCVReturnSuccess) {
        return NULL;
    }

    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    void *data = CVPixelBufferGetBaseAddress(pixelBuffer);
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(data, frameSize.width, frameSize.height,
                                                 8, CVPixelBufferGetBytesPerRow(pixelBuffer), rgbColorSpace,
                                                 (CGBitmapInfo) kCGImageAlphaNoneSkipLast);
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
                                           CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

    return pixelBuffer;
}

我认为这与kCVPixelFormatType_32ARGB和kCGImageAlphaNoneSkipLast之间的关系有关,尽管我已经尝试了所有组合,但要么得到相同的结果,要么应用程序崩溃。再次强调,这将UIImage数据转换为CVImageBufferRef,但是当我在屏幕上显示图像时,它似乎失去了一个颜色通道,并且呈现出蓝色色调。该图像是png格式。

6个回答

5

这段代码按照预期完美地运行了。 :) 问题出在使用数据创建OpenGL纹理时,而与此代码无关。如果有人正在寻找如何将UIImage转换为CVImageBufferRef的解决方案,请参考上面的代码!


不,它绝对不起作用。您需要使用kCVPixelFormatType_32BGRA,并将(CGBitmapInfo) kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst用作context的最后一个变量。此外,我认为NSDictionary *options是无用的。 - Maxi Mus

1

对我来说这个方法可行!只是为了让大家看到代码部分,CGContextRef上下文需要将传入的值从kCGImageAlphaNoneSkipLast更改为kCGImageAlphaPremultipliedFirst。 - Charlie

1

这是真正有效的方法:

+ (CVPixelBufferRef)pixelBufferFromImage:(CGImageRef)image {
    CGSize frameSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)); // Not sure why this is even necessary, using CGImageGetWidth/Height in status/context seems to work fine too

    CVPixelBufferRef pixelBuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width, frameSize.height, kCVPixelFormatType_32BGRA, nil, &pixelBuffer);
    if (status != kCVReturnSuccess) {
        return NULL;
    }

    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    void *data = CVPixelBufferGetBaseAddress(pixelBuffer);
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(data, frameSize.width, frameSize.height, 8, CVPixelBufferGetBytesPerRow(pixelBuffer), rgbColorSpace, (CGBitmapInfo) kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);

    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

    return pixelBuffer;
}

你可以将像素缓冲区转换回UIImage(然后显示或保存它),以确认它使用此方法有效:
+ (UIImage *)imageFromPixelBuffer:(CVPixelBufferRef)pixelBuffer {
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef myImage = [context createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer))];
    UIImage *image = [UIImage imageWithCGImage:myImage];

    // Uncomment the following lines to say the image to your application's document directory
    //NSString *imageSavePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"myImageFromPixelBuffer.png"]];
    //[UIImagePNGRepresentation(image) writeToFile:imageSavePath atomically:YES];
    return image;
}

1
如果有人仍在寻找解决此问题的方法,我通过切换像素缓冲器选项中的BOOL值来解决了它。
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                     [NSNumber numberWithBool:NO], kCVPixelBufferCGImageCompatibilityKey,
                     [NSNumber numberWithBool:NO], kCVPixelBufferCGBitmapContextCompatibilityKey,
                     nil];

从NO到YES:

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                     [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                     [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                     nil];

0

仅为澄清上面的答案:我遇到了相同的问题,因为我的着色器代码期望在图像缓冲区中有两个分层样本,而我使用了单层缓冲区。

这行代码从一个样本中获取RGB值并将其传递给(我不知道是什么),但最终结果是完整的彩色图像。

 gl_FragColor = vec4(texture2D(SamplerY, texCoordVarying).rgb, 1);

-1

听起来可能是那个关系。也许将它设置为jpg和RGB,而不是带有png的索引颜色?


2
感谢您的回答。这段代码完美地实现了预期的功能。问题在于使用数据创建OpenGL纹理时出现了问题,与这段代码完全无关。如果有人正在寻找如何将UIImage转换为CVImageBufferRef的方法,那么您的答案就在上面的代码中! - Eric
参考:PNG 不一定是索引的(像 GIF 一样),它还支持无损全彩 RGB 图像(显然这在线描绘和实色块方面效果更好)。 - Ethan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接