23

カメラからリアルタイムで 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 宣言が正しく設定されていることはわかっています。上記のコメント付きのコードを使用して、単純setImage:theImageに imageView に送信するのではなく、Web から任意の画像を読み込むと、画像は正しく読み込まれます (NSLog への 2 回目の呼び出しで nil 以外のオブジェクトが報告されます)。
  • imageFromSampleBuffer:NSLog がサイズを 360x480 と報告しているため、少なくとも基本的な範囲では、取得した画像は問題ありません。これは私が予想したサイズです。

私が使用しているコードは、AVFoundationApple から最近投稿されたスニペットで、こちらから入手できます。

特に、これは私が使用するコードで、AVCaptureSessionオブジェクトとフレンド (私はほとんど理解していません) をセットアップし、Core Video バッファーから UIImage オブジェクトを作成します (これがimageFromSampleBufferメソッドです)。

最後に、返されたを使用drawInRect:してプレーンな UIView サブクラスに送信しようとするとアプリケーションがクラッシュする可能性がありますが、上記のように URL からを使用してもクラッシュしません。クラッシュ時のデバッガーからのスタック トレースを次に示します (EXC_BAD_ACCESS シグナルを受け取ります)。UIImageimageFromSamplerBufferUIImage

#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で説明されている方法を使用して、ピクセルを取得して印刷できます。一見問題ないように見えます (たとえば、アルファ チャネルのすべての値は 255 です)。ただし、バッファ サイズには若干の違いがあります。その URL から Flickr から取得した画像は 375x500 で、期待値である が[pixelData length]得られます。750000 = 375*500*4ただし、から返される image のピクセル データimageFromSampleBuffer:は size691208 = 360*480*4 + 8であるため、ピクセル データには 8 バイト余分に含まれています。CVPixelBufferGetDataSizeそれ自体が、この 8 ずつ離れた値を返します。メモリ内のアラインされた位置にバッファを割り当てることが原因ではないかと一瞬思いましたが、691200 は 256 の倍数なので、それも説明できません。このサイズの不一致は、2 つの UIImage の間で確認できる唯一の違いであり、問​​題の原因となっている可能性があります。それでも、バッファーに余分なメモリを割り当てることで EXC_BAD_ACCESS 違反が発生する理由はありません。

ご協力いただきありがとうございます。さらに情報が必要な場合はお知らせください。

4

5 に答える 5

41

同じ問題が発生しました...しかし、この古い投稿を見つけました。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;
}
于 2010-07-26T13:47:32.900 に答える
10

ビデオ フレームのライブ キャプチャは、Apple の Technical Q&A QA1702 でよく説明されています。

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

于 2012-02-02T04:17:04.350 に答える
7

正しい出力形式を設定することも重要です。デフォルトのフォーマット設定を使用すると、画像のキャプチャに問題がありました。そのはず:

[videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]];
于 2012-10-19T11:30:53.940 に答える
5

Ben Loulier は、これを行う方法について良い記事を書いています。

彼のサンプルアプリを出発点として使用していますが、うまく機能しています。imageFromSamplerBuffer関数を CGBitmapContextCreate で CGImageRef を作成するものに置き換えるとともにdispatch_get_main_queue()、出力サンプル バッファ デリゲートを設定するときに ( 経由で) メイン ディスパッチ キューを使用しています。シリアル キューが必要なため、これは最適なソリューションではありません。また、メイン キューはシリアル キューではないことを理解しています。したがって、フレームを正しい順序で取得できるとは限りませんが、これまでのところうまくいくようです:)

于 2010-07-27T01:48:25.623 に答える
1

もう 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);
    }];
}
于 2015-03-27T14:58:35.213 に答える