从CMSampleBufferRef创建的UIImage没有显示在UIImageView中?

时间:2010-07-22 04:54:11

标签: iphone uiimageview uiimage ios4 avfoundation

我正试图从相机中实时显示UIImage,而我的UIImageView似乎没有正确显示图像。这是AVCaptureVideoDataOutputSampleBufferDelegate必须实现的方法

- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection
{ 
    // Create a UIImage from the sample buffer data
    UIImage *theImage = [self imageFromSampleBuffer:sampleBuffer];
//    NSLog(@"Got an image! %f %f", theImage.size.width, theImage.size.height);
//    NSLog(@"The image view is %@", imageView);
//    UIImage *theImage = [[UIImage alloc] initWithData:[NSData 
//        dataWithContentsOfURL:[NSURL 
//        URLWithString:@"http://farm4.static.flickr.com/3092/2915896504_a88b69c9de.jpg"]]];
    [self.session stopRunning];
    [imageView setImage: theImage];
}

为了解决容易出错的问题:

  • 使用UIImagePickerController不是一个选项(最终我们会用图像做事)
  • 我知道正在调用处理程序(进行NSLog调用,我看到输出)
  • 我知道我已正确设置了IBOutlet声明。如果我使用上面注释的代码从Web加载任意图像而不是简单地将setImage:theImage发送到imageView,则图像被正确加载(并且第二次调用NSLog报告非零对象)。
  • 至少在基本范围内,我从imageFromSampleBuffer:获得的图像很好,因为NSLog报告的大小为360x480,这是我预期的大小。

我使用的代码是Apple最近发布的AVFoundation代码段here

特别是,我使用的代码设置AVCaptureSession对象和朋友(我很少了解),并从核心视频缓冲区创建UIImage对象(即{{1}方法)。

最后,如果我尝试将imageFromSampleBuffer发送到drawInRect:返回UIImage的普通UIView子类,我可以让应用程序崩溃,而如果我不会崩溃如上所述,使用来自网址的imageFromSamplerBuffer。这是崩溃中调试器的堆栈跟踪(我得到一个EXC_BAD_ACCESS信号):

UIImage

编辑:这里有关于该位代码返回的UIImage的更多信息。

使用描述here的方法,我可以找到像素并打印它们,乍一看它们看起来很好(例如,alpha通道中的每个值都是255)。但是,缓冲区大小略有偏差。我从Flickr从该网址获得的图片为375x500,其#0 0x34a977ee in decode_swap () #1 0x34a8f80e in decode_data () #2 0x34a8f674 in img_decode_read () #3 0x34a8a76e in img_interpolate_read () #4 0x34a63b46 in img_data_lock () #5 0x34a62302 in CGSImageDataLock () #6 0x351ab812 in ripc_AcquireImage () #7 0x351a8f28 in ripc_DrawImage () #8 0x34a620f6 in CGContextDelegateDrawImage () #9 0x34a61fb4 in CGContextDrawImage () #10 0x321fd0d0 in -[UIImage drawInRect:blendMode:alpha:] () #11 0x321fcc38 in -[UIImage drawInRect:] () 为我提供[pixelData length],这是预期值。但是,从750000 = 375*500*4返回的图像的像素数据的大小为imageFromSampleBuffer:,因此像素数据中有8个额外字节。 691208 = 360*480*4 + 8本身会返回8分之一的值。我想了一下,它可能是由于在内存中的对齐位置分配缓冲区,但691200是256的倍数,所以这也不能解释它。这个尺寸差异是我在两个UIImages之间可以区分的唯一区别,它可能会造成麻烦。但是,没有理由为缓冲区分配额外的内存会导致EXC_BAD_ACCESS违规。

非常感谢您的帮助,如果您需要更多信息,请与我们联系。

5 个答案:

答案 0 :(得分:40)

答案 1 :(得分:10)

Apple的技术问答&QA1702现在可以很好地解释视频帧的实时捕捉:

https://developer.apple.com/library/ios/#qa/qa1702/_index.html

答案 2 :(得分:7)

设置正确的输出格式也很重要。使用默认格式设置时,我遇到了图像捕获问题。它应该是:

[videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]];

答案 3 :(得分:5)

Ben Loulier有good write up如何做到这一点。

我使用his example app作为起点,它对我有用。除了用CGBitmapContextCreate创建CGImageRef的东西替换imageFromSamplerBuffer函数,他在设置输出样本缓冲区委托时使用主调度队列(通过dispatch_get_main_queue())。这不是最好的解决方案,因为它需要一个串行队列,据我所知,主队列不是串行队列。因此,虽然您不能保证以正确的顺序获得帧,但到目前为止似乎对我有效:)

答案 4 :(得分:1)

另一件需要注意的事情是,您是否真的在主线上更新UIImageView:如果您不是,那么它很可能无法反映任何变化。

captureOutput:didOutputSampleBuffer:fromConnection委托方法通常在后台线程上调用。所以你想这样做:

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection
{   
    CFRetain(sampleBuffer);

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        //Now we're definitely on the main thread, so update the imageView:
        UIImage *capturedImage = [self imageFromSampleBuffer:sampleBuffer];

        //Display the image currently being captured:
        imageView.image = capturedImage;

        CFRelease(sampleBuffer);
    }];
}