【问题标题】:Convert UIImage to CVPixelBufferRef将 UIImage 转换为 CVPixelBufferRef
【发布时间】:2011-04-19 19:35:57
【问题描述】:

我想将 UIImage 对象转换为 CVPixelBufferRef 对象,但我完全不知道。而且我找不到任何类似的示例代码。

有人可以帮帮我吗?提前谢谢!

C 雅

【问题讨论】:

标签: iphone objective-c uiimage


【解决方案1】:

有不同的方法可以做到这一点,这些函数从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;
}

【讨论】:

  • 为什么要垂直和水平翻转?
  • @MaxiMus 因为UIKit和core grphics有不同的坐标系developer.apple.com/library/content/documentation/2DDrawing/…
  • y 坐标是,但不是 x...?
  • 嘿,我使用了你的代码,但我正在崩溃:- 2019-03-19 12:08:01.233465+0530 YoPlay[3693:866159] *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序',原因:'无效参数不令人满意:状态 == kCVReturnSuccess && pxbuffer != NULL' 你能帮我解决这个问题吗?
【解决方案2】:

您可以使用 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 对象的输出到像素缓冲区。如果这是您唯一关心的问题,您可以在 Sample Photo Editing Extension 示例代码中找到您要查找的内容。

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

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

【讨论】:

  • 我不确定是什么原因造成的。但是在多线程上下文中创建 CIContext 会导致错误。奇怪的是,这只发生在我快速处理图像时。当我慢慢做时,这似乎不是问题。
  • 我没有问题,但我已经了解了所有适用于 iOS 的图像处理框架的来龙去脉。我必须查看您的代码才能确定它,但这里有一些尝试:创建一个单一的、共享的 CIContext 实例,以便为每个图像重用;并且,确保您正在创建正确的上下文。它们都非常不同;而且,虽然编译器可能允许您创建其中任何一个,但并非所有这些都适用于所有情况。文档完善;更多信息请咨询。
【解决方案3】:

Google 永远是您的朋友。搜索“CVPixelBufferRef”第一个结果从snipplr引出this snippet

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

不过,不知道这是否可行。 (您的里程可能会谨慎:)

【讨论】:

  • 问题是关于将UIImage 转换为CVPixelBufferRef,而不是NSImage
  • 关于 size_t bytesPerRow = CVPixelBufferGetBytesPerRow(buffer); 的行这就是我的诀窍。所有其他代码示例都使用了一个在此设备上碰巧出错的神奇常量,能够确定实际值是关键。谢谢。
【解决方案4】:

虽然很晚,但对于需要的人来说。

 // 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);
【解决方案5】:

CVPixelBufferRef 是用于相机输入的核心视频。

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

【讨论】:

  • 我想做的是使用 AV Foundation 为电影添加单帧。这将使用 AVAssetWriterInputPixelBufferAdaptor 类来完成。但是这个类需要 CVPixelBufferRef 对象。那么,如何将 UIImage 转换为 CVPixelBufferRef 对象?
  • 问题询问如何将 UIImagte 转换为 CVPixelBufferRef。这个答案没有提供任何解决方案。
猜你喜欢
  • 1970-01-01
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 2012-07-15
  • 2013-01-01
相关资源
最近更新 更多