Iphone 是否从CMSampleBufferRef创建的UIImage未显示在UIImageView中?

Iphone 是否从CMSampleBufferRef创建的UIImage未显示在UIImageView中?,iphone,uiimageview,uiimage,ios4,avfoundation,Iphone,Uiimageview,Uiimage,Ios4,Avfoundation,我试图实时显示来自相机的UIImage,但我的UIImageView似乎没有正确显示图像。这是AVCaptureVideoDataOutputSampleBufferDelegate必须实现的方法 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnecti

我试图实时显示来自相机的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时报告非nil对象)
  • 至少在基本程度上,我从
    imageFromSampleBuffer:
    获得的图像很好,因为NSLog报告的大小是360x480,这是我期望的大小
我使用的代码是最近发布的苹果公司的
AVFoundation
代码片段

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

最后,如果我尝试将
drawInRect:
发送到带有
imageFromSamplerBuffer
返回的
UIImage
的普通UIView子类,我可以使应用程序崩溃,而如果我使用上述URL中的
UIImage
,它不会崩溃。以下是来自崩溃内部调试器的堆栈跟踪(我得到一个EXC\u BAD\u访问信号):

编辑:下面是关于该位代码返回的UIImage的更多信息

使用所描述的方法,我可以得到像素并打印它们,乍一看它们看起来还行(例如,alpha通道中的每个值都是255)。然而,缓冲区大小有点不一致。我从该URL从Flickr获得的图像是375x500,其
[pixelData length]
给出了
750000=375*500*4
,这是预期值。但是,从
imageFromSampleBuffer:
返回的图像像素数据的大小
691208=360*480*4+8
,因此像素数据中有8个额外字节
CVPixelBufferGetDataSize
本身返回这个off-by-8值。我曾一度认为这可能是因为在内存中的对齐位置分配了缓冲区,但691200是256的倍数,所以这也不能解释它。这个尺寸差异是我能分辨出的两幅UIImages之间的唯一差异,这可能是造成问题的原因。不过,没有理由为缓冲区分配额外内存会导致EXC\u BAD\u访问冲突


非常感谢您的帮助,如果您需要更多信息,请告诉我。

我也遇到了同样的问题。。。但是我发现了这篇老文章,以及它创建CGImageRef作品的方法

以下是一个工作示例:

app has a member UIImage theImage;

// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer  fromConnection:(AVCaptureConnection *)connection
{
        //... just an example of how to get an image out of this ...

    CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];
    theImage.image =     [UIImage imageWithCGImage: cgImage ];
    CGImageRelease( cgImage );
}

- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer,0);        // Lock the image buffer 

    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);   // Get information of the image 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 
    CGContextRelease(newContext); 

    CGColorSpaceRelease(colorSpace); 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 
    /* CVBufferRelease(imageBuffer); */  // do not call this!

    return newImage;
}
Ben Loulier对如何做到这一点有自己的见解


我把它作为一个起点,它对我很有用。除了将
imageFromSamplerBuffer
函数替换为使用CGBitmapContextCreate创建CGImageRef的函数外,在设置输出示例缓冲区委托时,他还使用了主调度队列(通过
dispatch\u get\u main\u queue()
)。这不是最好的解决方案,因为它需要一个串行队列,而且据我所知,主队列不是串行队列。因此,虽然你不能保证获得正确的帧顺序,但到目前为止,它似乎对我有效:)

苹果公司的技术问答QA1702很好地解释了视频帧的实时捕获:


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

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

另一件需要注意的事情是,您是否正在更新主线程上的
UIImageView
:如果您没有,它很可能不会反映任何更改

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

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

我遇到了同样的问题。令人恼火的是,正是苹果自己为我们提供了定制的“imageFromSampleBuffer”功能。更奇怪的是,图像内容本身是正确的。我将原始像素通过一个插座推送到另一台机器上,它正是从相机中以正确的格式捕获的视频帧。很好-我会在完成另一件事后立即尝试,然后我会回复你。因此,说到iOS API,我是个十足的白痴,我不知道如何将像素推进到实际的CG图像中。这似乎是通过
memcpy(mImageData,…)
调用来实现的,但是mImageData实际上并没有在该代码段的任何地方声明。你知道我如何使用iOS api访问这样的指针吗?啊,是的,我甚至不需要它-CGImageRef已经创建好了。谢谢你。您是否介意编辑您的答案以内联添加代码位,以供将来在StackOverflow中参考?在任何情况下,我都会给你奖金,但我认为这会非常有帮助。出于某种原因,我得到:CGBitmapContextCreateImage:invalid context 0x0。有什么想法吗?请注意。通过CGContextRef传输像素不会传输样本缓冲区元数据(即EXIF数据)。即使没有不同的队列,这也可以工作。我现在怀疑苹果在他们的代码片段中调用了
CGDataProviderCreateWithData
,这是代码中唯一剩下的区别。这是一个很好的例子。但是,在iPad3上构建图像需要大约两分钟,非常奇怪。好吧,我是个骗子。用于设置UIImageView的performSelectorOnMainThread提供了近乎即时的结果。
- (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);
    }];
}