2010-07-22 51 views
22

我想實時顯示來自相機的UIImage,而且看起來我的UIImageView沒有正確顯示圖像。這是一個AVCaptureVideoDataOutputSampleBufferDelegate有方法來實現從UIImageView中不顯示CMSampleBufferRef創建的UIImage?

- (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,這是我預期的尺寸。

我正在使用的代碼是最近發佈的AVFoundation片段Apple提供的here

尤其是,我使用的代碼設置了AVCaptureSession對象和朋友(我的理解很少),並從核心視頻緩衝區創建了UIImage對象(即imageFromSampleBuffer方法)。

最後,我可以讓應用程序崩潰,如果我嘗試發送drawInRect:與由imageFromSamplerBuffer返回UIImage一個普通的UIView子類,而它如果我如上使用UIImage從URL不會崩潰。下面是從內部崩潰的調試器堆棧跟蹤(我得到一個EXC_BAD_ACCESS信號):

#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:]() 

編輯:下面是有關的UIImage受到的代碼位返回一些更多的信息。

使用描述的方法here,我可以找到像素並打印它們,乍一看它們看起來不錯(例如,alpha通道中的每個值都是255)。但是,緩衝區大小略有不同。我從Flickr從該URL獲得的圖像是375x500,其[pixelData length]給我750000 = 375*500*4,這是預期值。但是,從imageFromSampleBuffer:返回的圖像的像素數據的大小爲691208 = 360*480*4 + 8,因此像素數據中有8個額外的字節。 CVPixelBufferGetDataSize本身將返回此8值。我想了一下,可能是因爲在內存中的對齊位置分配緩衝區,但691200是256的倍數,所以也不能解釋它。這個大小的差異是我可以告訴兩個UIImages之間唯一的區別,它可能會造成麻煩。但是,沒有理由爲緩衝區分配額外的內存,從而導致EXC_BAD_ACCESS違例。

非常感謝您的幫助,如果您需要更多信息,請告知我們。

+0

我遇到了同樣的問題。令人不快的是,Apple本身爲我們提供了自定義的「imageFromSampleBuffer」功能。 – 2010-07-22 22:36:55

+0

更奇怪的是,圖像內容本身是正確的。我正在通過套接字將原始像素推送到不同的機器,這正是從相機以正確格式捕獲的視頻幀。 – 2010-07-22 23:59:15

回答

40

我有同樣的問題......但我發現這個舊帖子,它的創建CGImageRef的方法的作品!

http://forum.unity3d.com/viewtopic.php?p=300819

這裏的工作示例:

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; 
} 
+0

不錯 - 我會盡快完成一件事情,然後我會盡快回復你。 – 2010-07-26 14:41:53

+1

所以,當我涉及到iOS API時,我是一個完全白癡,我不知道如何將像素推入實際的CGImage。這似乎是通過'memcpy(mImageData,...)'調用實現的,但mImageData實際上並未在該代碼段中的任何位置聲明。你知道我能如何使用iOS API訪問這樣一個指針嗎? – 2010-07-29 13:49:07

+0

啊,是的,我甚至都不需要這個 - CGImageRef已經創建好了。感謝那。您介意編輯您的答案,以添加內聯代碼以供將來在StackOverflow中參考?無論如何,我會給你賞金,但我認爲這將是最有幫助的。 – 2010-07-29 14:26:43

5

奔Loulier對如何做這good write up

我使用his example app作爲起點,它正在爲我工​​作。除了用CGBitmapContextCreate創建CGImageRef來代替imageFromSamplerBuffer函數外,他在設置輸出採樣緩衝區委託時使用主調度隊列(通過dispatch_get_main_queue())。這不是最好的解決方案,因爲它需要一個串行隊列,並且從我所瞭解的主隊列不是一個串行隊列。所以,當你不能保證得到幀它似乎爲我工作至今:)正確的順序

+0

即使沒有不同的隊列,它也能正常工作我現在懷疑Apple調用CGDataProviderCreateWithData在他們的代碼片段中,代碼中唯一的區別 – 2010-07-29 14:22:34

9

視頻幀的實時捕捉,現在深受蘋果的技術問答&一個QA1702解釋說:

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

+1

這是一個很好的例子,然而,在iPad3上構建圖像需要花費大約兩分鐘的時間,非常奇怪 – 2012-08-07 19:15:04

+2

好的,我是個騙子performSelectorOnMainThread for設置提供的UIImageView接近即時結果。 – 2012-08-07 19:23:07

7

設置正確的輸出格式也很重要。使用默認格式設置時,我遇到了圖像捕獲問題。它應該是:

[videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]]; 
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); 
    }]; 
}