web-dev-qa-db-fra.com

Pourquoi AVCaptureSession affiche une mauvaise orientation?

J'ai donc suivi les instructions d'Apple pour capturer une session vidéo en utilisant AVCaptureSession: http://developer.Apple.com/iphone/library/qa/qa2010/qa1702.html . Un problème auquel je suis confronté est que même si l'orientation de l'appareil photo/appareil iPhone est verticale (et que le AVCaptureVideoPreviewLayer montre un flux de caméra vertical), l'image de sortie semble être en mode paysage. J'ai vérifié la largeur et la hauteur de l'imageBuffer à l'intérieur imageFromSampleBuffer: de l'exemple de code, et j'ai obtenu respectivement 640 px et 480 px. Est-ce que quelqu'un sait pourquoi c'est le cas?

Merci!

47
Peter

Jetez un œil à l'en-tête AVCaptureSession.h. Il existe une définition pour une énumération appelée AVCaptureVideoOrientation qui définit diverses orientations vidéo. Sur l'objet AVCaptureConnection, il existe une propriété appelée videoOrientation qui est un AVCaptureVideoOrientation. Vous devriez pouvoir le régler pour changer l'orientation de la vidéo. Vous voulez probablement AVCaptureVideoOrientationLandscapeRight ou AVCaptureVideoOrientationLandscapeLeft.

Vous pouvez trouver les AVCaptureConnections pour la session en regardant les sorties de la session. Les sorties ont une propriété de connexions qui est un tableau de connexions pour cette sortie.

40
Jon Steinmetz

J'ai apporté une simple modification d'une ligne au imageFromSampleBuffer pour corriger le problème d'orientation (voir mon commentaire dans le code sous "J'ai modifié ..."). J'espère que ça aide quelqu'un parce que j'ai passé trop de temps là-dessus.

// 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

Vous rendez tout cela difficile.

Dans le DidOutputSampleBuffer, changez simplement l'orientation avant de saisir l'image. C'est mono, mais vous avez

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

en objC c'est cette méthode

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

Voici une bonne séquence:

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

Par exemple:

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

La valeur par défaut semble être AVCaptureVideoOrientationLandscapeRight.

Voir aussi QA1744: Définition de l'orientation de la vidéo avec AV Foundation .

11
ja'

Pour les personnes qui ont besoin de travailler avec CIImage et l'orientation du tampon est incorrecte, j'ai utilisé cette correction.

Aussi simple que ça. BTW les nombres 3,1,6,8 sont d'ici https://developer.Apple.com/reference/imageio/kcgimagepropertyorientation

Et ne me demandez pas pourquoi 3,1,6,8 est la bonne combinaison. J'ai utilisé la méthode de la force brute pour le trouver. Si vous savez pourquoi laisser l'explication dans un commentaire s'il vous plaît ...

- (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

Si l'orientation AVCaptureVideoPreviewLayer est correcte, vous pouvez simplement définir l'orientation avant de capturer l'image.

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

Il est également important de noter que UIImageOrientation et AVCaptureVideoOrientation sont différents. UIImageOrientationUp fait référence au mode paysage avec les commandes de volume vers le bas vers le sol (pas vers le haut si vous pensez à en utilisant les commandes de volume comme déclencheur).

Ainsi, l'orientation portrait avec le bouton d'alimentation pointant vers le ciel (AVCaptureVideoOrientationPortrait) est en fait UIImageOrientationLeft.

5
mikeho

Le problème d'orientation est lié à la caméra avant, alors vérifiez le type d'appareil et générez une nouvelle image, cela résoudra certainement le problème d'orientation:

-(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

Tout d'abord, dans la configuration de votre sortie vidéo, mettez ces lignes:

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

Ensuite, configurez votre cible pour prendre en charge uniquement Portait, en décochant les modes Paysage dans la configuration générale.

( Source )

1
Laura Corssac