如何将CVImageBufferRef转换为UIImage

25

我正在尝试从相机捕获视频。我已经成功触发了captureOutput:didOutputSampleBuffer:回调函数,并获得了一个示例缓冲区,然后将其转换为CVImageBufferRef。然后我试图将该图像转换为UIImage,以便在我的应用程序中查看。

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    /*Lock the image buffer*/
    CVPixelBufferLockBaseAddress(imageBuffer,0); 
    /*Get information about the image*/
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    /*We unlock the  image buffer*/
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);

    /*Create a CGImageRef from the CVImageBufferRef*/
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 

    /*We release some components*/
    CGContextRelease(newContext); 
     CGColorSpaceRelease(colorSpace);

     /*We display the result on the custom layer*/
    /*self.customLayer.contents = (id) newImage;*/

    /*We display the result on the image view (We need to change the orientation of the image so that the video is displayed correctly)*/
    UIImage *image= [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationRight];
    self.capturedView.image = image;

    /*We relase the CGImageRef*/
    CGImageRelease(newImage);
}

代码似乎一直工作得很好,直到调用 CGBitmapContextCreate。它总是返回一个 NULL 指针。因此,函数的其余部分都不起作用。无论我传递什么参数,函数都返回 null。我不知道为什么。

4个回答

21

如果您需要将CVImageBufferRef 转换为 UIImage,那么不幸的是,这似乎比应该更困难。

实际上,您需要先将其转换为 CIImage,然后再转换为 CGImage,最后才能转换为 UIImage。我希望我能告诉您原因 :)

-(void) screenshotOfVideoStream:(CVImageBufferRef)imageBuffer
{
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:imageBuffer];
    CIContext *temporaryContext = [CIContext contextWithOptions:nil];
    CGImageRef videoImage = [temporaryContext
                                 createCGImage:ciImage
                                 fromRect:CGRectMake(0, 0,
                                 CVPixelBufferGetWidth(imageBuffer),
                                 CVPixelBufferGetHeight(imageBuffer))];

    UIImage *image = [[UIImage alloc] initWithCGImage:videoImage];
    [self doSomethingWithOurUIImage:image];
    CGImageRelease(videoImage);
}

在我使用VTDecompressionSession回调函数从H.264视频中转换获得CVImageBufferRef时,这种特定的方法对我有效(但对于任何CVImageBufferRef都适用)。我当时使用的是iOS 8.1,Xcode 6.2。


3
不需要那么多步骤。您可以调用[[UIImage alloc] initWithCIImage ...]。 - Joris Mans
3
我尝试做了那个,但对我无效,只得到了一个纯白色的图像。不知为何我需要使用CIContext来完成此操作(就像这个答案所说的[https://dev59.com/6msz5IYBdhLWcg3wmo9a#7788510)。]或许这只是因为我的特殊情况需要它吧。老实说,我并不完全理解所有这些关于图像格式和上下文的东西。 - Olivia Stork

20
您正在传递baseAddress的方式假定图像数据是以ACCC(其中C是某种颜色分量,R || G || B)的形式呈现。 如果您已经设置了AVCaptureSession以以本机格式捕获视频帧,则很可能会以平面YUV420格式获取视频数据。 (参见:链接文本)。 为了做到您在此尝试的事情,最简单的方法可能是指定您希望以kCVPixelFormatType_32RGBA捕获视频帧。 Apple建议您以kCVPixelFormatType_32BGRA捕获视频帧,如果您根本不以平面格式捕获它,则没有说明其原因,但我可以合理地假设这是由于性能考虑。

注意:我没有做过这个,而是假设像这样访问CVPixelBufferRef内容是构建图像的合理方法。 我无法保证这实际上有效,但我可以告诉您,由于您(可能)以像素格式捕获视频帧,因此您现在正在可靠地进行的事情将不起作用。


8
完全正确的答案,作为评论:在iPhone 4上至少在iOS 4.2.1中,kCVPixelFormatType_32RGBA不可用作捕获格式,但BGRA已经完全实现。 - Tommy

5

您可以直接调用:

self.yourImageView.image=[[UIImage alloc] initWithCIImage:[CIImage imageWithCVPixelBuffer:imageBuffer]];

2
这个程序可以运行,但似乎比Livy Storks的方法慢很多。 - Philipp Otto

2

此博客文章似乎已经消失。 - Brigham

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