将UIImage转换为CVPixelBufferRef

14
我想将UIImage对象转换为CVPixelBufferRef对象,但我完全不知道如何操作。而且我找不到任何类似于这样的示例代码。
有人能帮助我吗?提前感谢!
再见。

请查看 https://dev59.com/elcP5IYBdhLWcg3wY5KJ#44475334 - onmyway133
5个回答

9

有不同的方法来实现这一点,这些函数可以将像素缓冲区从CGImage转换为UImageCGImage的包装器,因此要获取CGImage只需要调用.CGImage方法。
其他方法还包括从缓冲区创建CIImage(已发布)或使用Accelerate框架,后者可能是最快的,但也是最难的。

- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image
{
    NSDictionary *options = @{
                              (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES,
                              (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
                              };

    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, CGImageGetWidth(image),
                        CGImageGetHeight(image), kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
                        &pxbuffer);
    if (status!=kCVReturnSuccess) {
        NSLog(@"Operation failed");
    }
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, CGImageGetWidth(image),
                                                 CGImageGetHeight(image), 8, 4*CGImageGetWidth(image), rgbColorSpace,
                                                 kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);

    CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
    CGAffineTransform flipVertical = CGAffineTransformMake( 1, 0, 0, -1, 0, CGImageGetHeight(image) );
    CGContextConcatCTM(context, flipVertical);
    CGAffineTransform flipHorizontal = CGAffineTransformMake( -1.0, 0.0, 0.0, 1.0, CGImageGetWidth(image), 0.0 );
    CGContextConcatCTM(context, flipHorizontal);

    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
                                           CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
    return pxbuffer;
}

为什么要翻转垂直和水平方向? - Maxi Mus
@MaxiMus 因为UIKit和核心图形具有不同的坐标系统 https://developer.apple.com/library/content/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html - Andrea
Y坐标可以,但X坐标呢? - Maxi Mus
嘿,我使用了你的代码,但是出现了崩溃:- 2019-03-19 12:08:01.233465+0530 YoPlay[3693:866159] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException',原因是:“无效参数不满足:status == kCVReturnSuccess && pxbuffer != NULL”。 你能帮我解决这个问题吗? - Khush

3
你可以使用Core Image从UIImage创建一个CVPixelBuffer。
// 1. Create a CIImage with the underlying CGImage encapsulated by the UIImage (referred to as 'image'):

CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];

// 2. Create a CIContext:

Context *ciContext = [CIContext contextWithCGContext:UIGraphicsGetCurrentContext() options:nil];

// 3. Render the CIImage to a CVPixelBuffer (referred to as 'outputBuffer'):

[self.ciContext render:img toCVPixelBuffer:outputBuffer];

AVFoundation提供了读取视频文件(称为资产)以及从其他处理资产的AVFoundation对象的输出中读取像素缓冲区的类。如果这是您唯一关心的问题,您可以在"示例照片编辑扩展"的示例代码中找到您需要的内容。

如果您的源自于一系列UIImage对象(也许没有源文件,并且您正在从用户生成的内容创建新文件),那么上面提供的示例代码就足够了。

注意:虽然不是将UIImage转换为CVPixelBuffer的最有效方法,但它绝对是最简单的方法。使用Core Graphics将UIImage转换为CVPixelBuffer需要更多的代码来设置属性,例如像素缓冲区大小和颜色空间,而Core Image会为您处理这些问题。


我不确定是什么原因导致了这个问题。但是在多线程环境下创建CIContext会导致错误。奇怪的是,只有在快速处理图像时才会出现这种情况。当我慢慢处理时,似乎就没有这个问题了。 - nnrales
1
我没有问题,但我已经学习了所有可用于iOS的图像处理框架的所有细节。我必须看到你的代码才能确定,但这里有一些尝试的方法:创建一个单一的、共享的CIContext实例以重用每个图像;并确保您正在创建正确的上下文。它们各不相同;而且,虽然编译器可能允许您创建其中任何一个,但并非所有情况下都适用于所有情况。文档已经得到了很好的改进;请查阅更多信息。 - James Bush

0

Google永远是你的好朋友。搜索“CVPixelBufferRef”,第一个结果将引导您访问this snippet,它来自于snipplr

- (CVPixelBufferRef)fastImageFromNSImage:(NSImage *)image{
CVPixelBufferRef buffer = NULL;

// config
size_t width = [image size].width;
size_t height = [image size].height;
size_t bitsPerComponent = 8; // *not* CGImageGetBitsPerComponent(image);
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGBitmapInfo bi = kCGImageAlphaNoneSkipFirst; // *not* CGImageGetBitmapInfo(image);
NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];

// create pixel buffer
CVPixelBufferCreate(kCFAllocatorDefault, width, height, k32ARGBPixelFormat, (CFDictionaryRef)d, &buffer);
CVPixelBufferLockBaseAddress(buffer, 0);
void *rasterData = CVPixelBufferGetBaseAddress(buffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(buffer);

// context to draw in, set to pixel buffer's address
CGContextRef ctxt = CGBitmapContextCreate(rasterData, width, height, bitsPerComponent, bytesPerRow, cs, bi);
if(ctxt == NULL){
    NSLog(@"could not create context");
    return NULL;
}

// draw
NSGraphicsContext *nsctxt = [NSGraphicsContext graphicsContextWithGraphicsPort:ctxt flipped:NO];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:nsctxt];
[image compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];
[NSGraphicsContext restoreGraphicsState];

CVPixelBufferUnlockBaseAddress(buffer, 0);
CFRelease(ctxt);

return buffer;
}

不知道这是否完全有效。(结果可能会有所不同:)


4
问题是关于将UIImage转换为CVPixelBufferRef,而不是NSImage - srgtuszy
1
关于 size_t bytesPerRow = CVPixelBufferGetBytesPerRow(buffer); 这一行是让我成功的关键。所有其他代码示例都使用了一个在此设备上恰巧错误的神奇常数,能够确定真实值是关键。谢谢。 - Brian Trzupek

0

虽然很晚了,但对于需要的人来说还是有用的。

 // call like this
    CVPixelBufferRef videobuffer = [self pixelBufferFromCGImage:yourImage.CGImage]; 

// 转换的方法

-(CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image{

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, videoSize.width, videoSize.height,
                                          kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef)options, &pxbuffer);
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata != NULL);

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, videoSize.width, videoSize.height, 8, 4*videoSize.width, rgbColorSpace, kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextConcatCTM(context, CGAffineTransformIdentity);
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);
    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

我已经尝试过这个。使用它会裁剪图像帧。因此,这里是解决方案CGContextDrawImage(context,CGRectMake(0,0,videoSize.width,videoSize.height),image); - Talha Ahmad Khan

-1

CVPixelBufferRef是核心视频用于相机输入的数据类型。

您可以使用CGBitmapContextCreate创建类似的像素位图,然后将图像绘制到位图上下文中。


1
我想要做的是使用AV Foundation向电影添加单帧。这将使用类AVAssetWriterInputPixelBufferAdaptor完成。但是,该类需要CVPixelBufferRef对象。那么,我该如何将UIImage转换为CVPixelBufferRef对象? - Nuker
这个问题询问如何将UIImagte转换为CVPixelBufferRef。这个答案没有提供解决方案。 - Gene Z. Ragan

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