web-dev-qa-db-ja.com

AVCaptureSessionが間違った方向を出力するのはなぜですか?

そこで、Appleの指示に従ってAVCaptureSessionを使用してビデオセッションをキャプチャしました: http://developer.Apple.com/iphone/library/qa/qa2010/qa1702.html 。私が直面している問題の1つは、カメラ/ iPhoneデバイスの向きが垂直(およびAVCaptureVideoPreviewLayerが垂直カメラストリームを表示)しているにもかかわらず、出力画像が横向きモードになっているように見えることです。サンプルコードのimageFromSampleBuffer:内のimageBufferの幅と高さを確認し、それぞれ640pxと480pxを取得しました。なぜそうなのか誰もが知っていますか?

ありがとう!

47
Peter

ヘッダーAVCaptureSession.hを見てください。さまざまなビデオの向きを定義するAVCaptureVideoOrientationという列挙型の定義があります。 AVCaptureConnectionオブジェクトには、AVCaptureVideoOrientationであるvideoOrientationというプロパティがあります。これを設定して、ビデオの向きを変更できるはずです。おそらくAVCaptureVideoOrientationLandscapeRightまたはAVCaptureVideoOrientationLandscapeLeftが必要でしょう。

セッションの出力を見ると、セッションのAVCaptureConnectionsを見つけることができます。出力には、その出力の接続の配列であるconnectionsプロパティがあります。

40
Jon Steinmetz

オリエンテーションの問題を修正するために、imageFromSampleBufferに簡単な1行の変更を加えました(「I modified ...」の下のコードのコメントを参照)。私がこれに時間をかけすぎたので、それが誰かを助けることを願っています。

// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer  {
    // Get a CMSampleBuffer's Core Video image buffer for the media data
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0); 

    // Get the number of bytes per row for the pixel buffer
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 

    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    // Create a bitmap graphics context with the sample buffer data
    CGContextRef context1 = CGBitmapContextCreate(baseAddress, width, height, 8, 
                                                 bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);

    // Create a Quartz image from the pixel data in the bitmap graphics context
    CGImageRef quartzImage = CGBitmapContextCreateImage(context1); 
    // Unlock the pixel buffer
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);

    // Free up the context and color space
    CGContextRelease(context1); 
    CGColorSpaceRelease(colorSpace);

    // Create an image object from the Quartz image
    //I modified this line: [UIImage imageWithCGImage:quartzImage]; to the following to correct the orientation:
    UIImage *image =  [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight]; 

    // Release the Quartz image
    CGImageRelease(quartzImage);

    return (image);
}
21
RawMean

あなたはこれを難しくしています。

DidOutputSampleBufferで、画像を取得する前に向きを変更するだけです。モノですが、あなたは持っています

    public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {    
        public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
        {
            try {
                connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;

objCでは、このメソッドです

- ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput
   didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer
      fromConnection: ( AVCaptureConnection * ) connection
19
Nick Turner

これが正しいシーケンスです。

AVCaptureVideoDataOutput *videoCaptureOutput = [[AVCaptureVideoDataOutput alloc] init];

if([self.captureSession canAddOutput:self.videoCaptureOutput]){
    [self.captureSession addOutput:self.videoCaptureOutput];
}else{
    NSLog(@"cantAddOutput");
}

// set portrait orientation
AVCaptureConnection *conn = [self.videoCaptureOutput connectionWithMediaType:AVMediaTypeVideo];
[conn setVideoOrientation:AVCaptureVideoOrientationPortrait];
15
Rubycon

例えば:

AVCaptureConnection *captureConnection = <a capture connection>;
if ([captureConnection isVideoOrientationSupported]) {
    captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
}

デフォルトはAVCaptureVideoOrientationLandscapeRightのようです。

QA1744:AV Foundationを使用したビデオの方向の設定 も参照してください。

11
ja'

CIImageを使用する必要があり、バッファーからの方向が間違っている場合は、この修正を使用しました。

それと同じくらい簡単。ところで、3、1、6、8の数字はここから https://developer.Apple.com/reference/imageio/kcgimagepropertyorientation

そして、なぜ3,1,6,8が正しい組み合わせなのかを聞かないでください。私はそれを見つけるためにブルートフォース法を使用しました。理由を知っている場合は、コメントで説明してください...

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

    // common way to get CIImage

    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);

    CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer
                                                      options:(__bridge NSDictionary *)attachments];

    if (attachments) {
       CFRelease(attachments);
    }

    // fixing the orientation of the CIImage

    UIInterfaceOrientation curOrientation = [[UIApplication sharedApplication] statusBarOrientation];

    if (curOrientation == UIInterfaceOrientationLandscapeLeft){
        ciImage = [ciImage imageByApplyingOrientation:3];
    } else if (curOrientation == UIInterfaceOrientationLandscapeRight){
        ciImage = [ciImage imageByApplyingOrientation:1];
    } else if (curOrientation == UIInterfaceOrientationPortrait){
        ciImage = [ciImage imageByApplyingOrientation:6];
    } else if (curOrientation == UIInterfaceOrientationPortraitUpsideDown){
        ciImage = [ciImage imageByApplyingOrientation:8];
    }



    // ....

}
7
Marek Manduch

AVCaptureVideoPreviewLayerの向きが正しい場合、画像をキャプチャする前に向きを簡単に設定できます。

AVCaptureStillImageOutput *stillImageOutput;
AVCaptureVideoPreviewLayer *previewLayer;
NSData *capturedImageData;

AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoOrientationSupported]) {
    [videoConnection setVideoOrientation:previewLayer.connection.videoOrientation];
}
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
    CFDictionaryRef exifAttachments =
            CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
    if (exifAttachments) {
        // Do something with the attachments.
    }
    // TODO need to manually add GPS data to the image captured
    capturedImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
    UIImage *image = [UIImage imageWithData:capturedImageData];
}];

また、UIImageOrientationAVCaptureVideoOrientationは異なることに注意することが重要です。 UIImageOrientationUpは、地面に向かってボリュームコントロールdownを備えたランドスケープモードを指します(考えれば、not upボリュームコントロールをシャッターボタンとして使用します)。

したがって、空(AVCaptureVideoOrientationPortrait)を指す電源ボタンを使用した縦向きは、実際にはUIImageOrientationLeftです。

5
mikeho

向きの問題は前面カメラにあるため、デバイスの種類を確認して新しい画像を生成すると、間違いなく向きの問題が解決されます。

-(void)capture:(void(^)(UIImage *))handler{

AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in self.stillImageOutput.connections)
{
    for (AVCaptureInputPort *port in [connection inputPorts])
    {
        if ([[port mediaType] isEqual:AVMediaTypeVideo] )
        {
            videoConnection = connection;
            break;
        }
    }
    if (videoConnection) { break; }
}

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {

    if (imageSampleBuffer != NULL) {
        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
        **UIImage *capturedImage = [UIImage imageWithData:imageData];
        if (self.captureDevice == [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][1]) {
            capturedImage = [[UIImage alloc] initWithCGImage:capturedImage.CGImage scale:1.0f orientation:UIImageOrientationLeftMirrored];
        }**

        handler(capturedImage);
    }
}];
}
2
Sandip

まず、ビデオ出力の構成で次の行を入力します。

guard let connection = videoOutput.connection(withMediaType: 
AVFoundation.AVMediaTypeVideo) else { return }
guard connection.isVideoOrientationSupported else { return }
guard connection.isVideoMirroringSupported else { return }
connection.videoOrientation = .portrait
connection.isVideoMirrored = position == .front

次に、一般設定でランドスケープモードのチェックを外して、Portaitのみをサポートするようにターゲットを設定します。

ソース

1
Laura Corssac