カメラからの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];
}
簡単な問題を回避するには:
setImage:theImage
をimageViewに送信するのではなく、Webから任意の画像を読み込むと、画像は正しく読み込まれます(NSLogへの2回目の呼び出しでnil以外のオブジェクトが報告されます)。imageFromSampleBuffer:
から取得した画像は問題ありません。これは、NSLogがサイズを予想したサイズである360x480と報告しているためです。私が使用しているコードは、Apple available here から最近投稿されたAVFoundation
スニペットです。
特に、これは私が使用するコードであり、AVCaptureSession
オブジェクトとその仲間(私はほとんど理解していません)をセットアップし、コアビデオバッファー(つまりimageFromSampleBuffer
)からUIImageオブジェクトを作成します。方法)。
最後に、UIImage
によって返されたimageFromSamplerBuffer
を使用してdrawInRect:
をプレーンなUIViewサブクラスに送信しようとすると、アプリケーションがクラッシュする可能性がありますが、使用してもクラッシュしません。上記のURLからのUIImage
。クラッシュ内のデバッガーからのスタックトレースは次のとおりです(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に関するいくつかの詳細情報があります。
説明されている方法を使用して ここ 、ピクセルに到達してそれらを印刷することができ、それらは一見問題ないように見えます(たとえば、アルファチャネルのすべての値は255です)。ただし、バッファサイズには少しずれがあります。そのURLからFlickrから取得した画像は375x500であり、その[pixelData length]
は期待値である750000 = 375*500*4
を提供します。ただし、imageFromSampleBuffer:
から返される画像のピクセルデータのサイズは691208 = 360*480*4 + 8
であるため、ピクセルデータには8バイト余分にあります。 CVPixelBufferGetDataSize
自体は、この8単位の値を返します。メモリ内の整列した位置にバッファを割り当てたことが原因である可能性があると少し考えましたが、691200は256の倍数であるため、それも説明できません。このサイズの不一致は、2つのUIImage間で私が知ることができる唯一の違いであり、問題を引き起こしている可能性があります。それでも、バッファにextraメモリを割り当てると、EXC_BAD_ACCESS違反が発生する理由はありません。
ご協力いただきありがとうございます。さらに情報が必要な場合はお知らせください。
同じ問題がありました...しかし、この古い投稿を見つけました。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;
}
ビデオフレームのライブキャプチャは、AppleのテクニカルQ&AQA1702で十分に説明されています。
https://developer.Apple.com/library/ios/#qa/qa1702/_index.html
正しい出力フォーマットを設定することも重要です。デフォルトのフォーマット設定を使用すると、画像のキャプチャに問題が発生しました。そのはず:
[videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]];
Ben Loulierは、これを行う方法について 良い記事 を持っています。
私は 彼のサンプルアプリ を出発点として使用していますが、それは私のために働いています。 imageFromSamplerBuffer
関数をCGBitmapContextCreateでCGImageRefを作成するものに置き換えることに加えて、彼は出力サンプルバッファーデリゲートを設定するときにメインディスパッチキューを(dispatch_get_main_queue()
経由で)使用しています。これはシリアルキューが必要であり、メインキューはシリアルキューではないことを理解しているため、これは最善の解決策ではありません。したがって、フレームを正しい順序で取得することは保証されていませんが、これまでのところうまくいくようです:)
もう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);
}];
}