2011-10-21 21 views
7

Sto utilizzando le classi AVFoundation per implementare una fotocamera personalizzata nella mia app. Sto solo catturando immagini fisse, non video. Ho tutto funzionante, ma sono perplesso da qualcosa. Prendo in considerazione l'orientamento del dispositivo quando viene catturata un'immagine fissa e impostiamo l'opzione VideoOrientation della connessione video in modo appropriato. Un frammento di codice:Perché le modalità paesaggio AVCaptureVideoOrientation risultano in immagini ferme capovolte?

// set the videoOrientation based on the device orientation to 
    // ensure the pic is right side up for all orientations 
    AVCaptureVideoOrientation videoOrientation; 
    switch ([UIDevice currentDevice].orientation) { 
     case UIDeviceOrientationLandscapeLeft: 
      // Not clear why but the landscape orientations are reversed 
      // if I use AVCaptureVideoOrientationLandscapeLeft here the pic ends up upside down 
      videoOrientation = AVCaptureVideoOrientationLandscapeRight; 
      break; 
     case UIDeviceOrientationLandscapeRight: 
      // Not clear why but the landscape orientations are reversed 
      // if I use AVCaptureVideoOrientationLandscapeRight here the pic ends up upside down 
      videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 
      break; 
     case UIDeviceOrientationPortraitUpsideDown: 
      videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; 
      break; 
     default: 
      videoOrientation = AVCaptureVideoOrientationPortrait; 
      break; 
    } 

    videoConnection.videoOrientation = videoOrientation; 

Nota i miei commenti nei casi di paesaggio. Devo invertire la mappatura dell'orientamento o l'immagine risultante è capovolta. Cattura e salva l'immagine con il seguente codice:

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection 
    completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) 
    { 
     NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 
     self.stillImage = [UIImage imageWithData:imageData]; 
     // notify observers (image gets saved to the camera roll)               
     [[NSNotificationCenter defaultCenter] postNotificationName:CaptureSessionManagerDidCaptureStillImageNotification object:self]; 
     self.stillImage = nil; 
}]; 

Non vi sono altre elaborazioni o manipolazioni di immagini.

La mia app funziona con il codice sopra. Sto solo cercando di capire perché le costanti di orientamento devono essere invertite per gli orientamenti del paesaggio. Grazie!

+0

Non usare l'orientamento UIDevice quando effettivamente desidera utilizzare Orientamento dell'interfaccia utente. – Till

+0

In questo caso non è possibile utilizzare l'orientamento 'UIInterface'. L'orientamento del dispositivo al momento della cattura dell'immagine è sempre corretto. l'orientamento dell'immagine di 'stillImage' dopo che i dati sono stati acquisiti non è corretto. Sono stato in grado di risolvere il problema utilizzando UIDeviceOrientation e di tradurre in UIImageOrientation appropriato durante la rotazione. A quel tempo, nessuno stava intervenendo qui, quindi non ho inserito il codice. Se avrò tempo, aggiungerò il codice. – XJones

+0

@XJones, si prega di aggiornare la risposta con la soluzione esatta. – Hemang

risposta

20

Heh, sembra che nessuno si sia sentito in sintonia con questo. Risulta la risposta è semplice. Immagini catturate tramite il metodo stillImageOutput captureStillImageAsynchronouslyFromConnection:... finiscono sempre con le seguenti proprietà:

  • UIImage orientamento = sempre UIImageOrientationRight indipendentemente dall'orientamento dispositivo
  • dimensioni
  • UIImage = W x H (es ritratto larghezza altezza x ritratto, dipende dalla fotocamera risoluzione)
  • dimensioni CGImage = dipende orientamento del dispositivo (ad es verticale o orizzontale)

Quindi la soluzione per ruotare l'immagine verso l'alto è di usare all'orientamento del dispositivo in combinazione con la dimensione di CGImage applica una trasformazione affine appropriata. Come sto rispondendo alla mia domanda, io non sono la soluzione in codice, ma ho finito per scrivere una routine chiamata:

- (UIImage *)imageRotatedUpForDeviceOrientation:(UIDeviceOrientation)deviceOrientation 

in una categoria UIImage contenente vari miglioramenti di elaborazione delle immagini.

EDIT - Esempio di implementazione

ho ricevuto un numero di richieste di codice funzionale su questo. Ho estratto l'implementazione pertinente da un'app funzionante.

// this method is implemented in your capture session manager (wherever AVCaptureSession is used) 
// capture a still image and save the device orientation 
- (void)captureStillImage 
{ 
    UIDeviceOrientation currentDeviceOrientation = UIDevice.currentDevice.orientation; 
     [self.stillImageOutput 
     captureStillImageAsynchronouslyFromConnection:self.videoConnection 
     completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { 
      NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 
      if (imageData) { 
       UIImage *image = [UIImage imageWithData:imageData]; 
       NSDictionary *captureInfo = { 
        @"image" : image, 
        @"deviceOrientation" : @(currentDeviceOrientation) 
       }; 
       // TODO: send image & orientation to delegate or post notification to observers 
      } 
      else { 
       // TODO: handle image capture error 
      } 
    }]; 
} 

// this method rotates the UIImage captured by the capture session manager based on the 
// device orientation when the image was captured 
- (UIImage *)imageRotatedUpFromCaptureInfo:(NSDictionary *)captureInfo 
{ 
    UIImage *image = [captureInfo objectForKey:@"image"]; 
    UIDeviceOrientation deviceOrientation = [[captureInfo objectForKey:@"deviceOrientation"] integerValue]; 
    UIImageOrientation rotationOrientation = [self rotationNeededForImageCapturedWithDeviceOrientation:deviceOrientation]; 
    // TODO: scale the image if desired 
    CGSize newSize = image.size; 
    return [imageScaledToSize:newSize andRotatedByOrientation:rotationOrientation]; 
} 

// return a scaled and rotated an image 
- (UIImage *)imageScaledToSize:(CGSize)newSize andRotatedByOrientation:(UIImageOrientation)orientation 
{ 
    CGImageRef imageRef = self.CGImage;  
    CGRect imageRect = CGRectMake(0.0, 0.0, newSize.width, newSize.height); 
    CGRect contextRect = imageRect; 
    CGAffineTransform transform = CGAffineTransformIdentity; 

    switch (orientation) 
    { 
     case UIImageOrientationDown: { // rotate 180 deg 
      transform = CGAffineTransformTranslate(transform, imageRect.size.width, imageRect.size.height); 
      transform = CGAffineTransformRotate(transform, M_PI); 
     } break; 

     case UIImageOrientationLeft: { // rotate 90 deg left 
      contextRect = CGRectTranspose(contextRect); 
      transform = CGAffineTransformTranslate(transform, imageRect.size.height, 0.0); 
      transform = CGAffineTransformRotate(transform, M_PI/2.0); 
     } break; 

     case UIImageOrientationRight: { // rotate 90 deg right 
      contextRect = CGRectTranspose(contextRect); 
      transform = CGAffineTransformTranslate(transform, 0.0, imageRect.size.width); 
      transform = CGAffineTransformRotate(transform, 3.0 * M_PI/2.0); 
     } break; 

     case UIImageOrientationUp: // no rotation 
     default: 
      break; 
    } 

    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); 
    CGColorSpaceRef colorSpaceRef = CGImageGetColorSpace(imageRef); 

    // madify bitmapInfo to work with PNG if necessary 
    if (bitmapInfo == kCGImageAlphaNone) { 
     bitmapInfo = kCGImageAlphaNoneSkipLast; 
    } 
    else if (bitmapInfo == kCGImageAlphaLast) { 
     bitmapInfo = kCGImageAlphaPremultipliedLast; 
    } 

    // Build a context that's the same dimensions as the new size 
    CGContextRef context = CGBitmapContextCreate(NULL, 
               contextRect.size.width, 
               contextRect.size.height, 
               CGImageGetBitsPerComponent(imageRef), 
               0, 
               colorSpaceRef, 
               bitmapInfo); 


    CGContextConcatCTM(context, transform); 
    CGContextDrawImage(context, imageRect, imageRef); 

    // Get the rotated image from the context and a UIImage 
    CGImageRef rotatedImageRef = CGBitmapContextCreateImage(context); 
    UIImage *rotatedImage = [UIImage imageWithCGImage:rotatedImageRef]; 

    // Clean up 
    CGImageRelease(rotatedImageRef); 
    CGContextRelease(context); 

    return rotatedImage; 
} 

// return the UIImageOrientation needed for an image captured with a specific deviceOrientation 
- (UIImageOrientation)rotationNeededForImageCapturedWithDeviceOrientation:(UIDeviceOrientation)deviceOrientation 
{ 
    UIImageOrientation rotationOrientation; 
    switch (deviceOrientation) { 
     case UIDeviceOrientationPortraitUpsideDown: { 
      rotationOrientation = UIImageOrientationLeft; 
     } break; 

     case UIDeviceOrientationLandscapeRight: { 
      rotationOrientation = UIImageOrientationDown; 
     } break; 

     case UIDeviceOrientationLandscapeLeft: { 
      rotationOrientation = UIImageOrientationUp; 
     } break; 

     case UIDeviceOrientationPortrait: 
     default: { 
      rotationOrientation = UIImageOrientationRight; 
     } break; 
    } 
    return rotationOrientation; 
} 
+7

Buona risposta ma poche righe di implementazione mi avrebbero risparmiato un po 'di battitura! 8 -) – kalperin

+3

@XJones Non riuscivo a capire la riga di codice qui sotto, potresti spiegarmi questo per me? contextRect = CGRectTranspose (contextRect); –

+1

CGRectTranspose WTF – jjxtra

6

Per quanto riguarda il motivo per cui è necessario AVCaptureVideoOrientationLandscape destro quando l'orientamento del dispositivo è UIDeviceOrientationLandscape Sinistra, è perché, per qualche ragione, UIDeviceOrientationLandscape Sinistra == UIInterfaceOrientationLandscape destro, e le AVCaptureVideoOrientations stanno seguendo il UIInterfaceOrientation convention.

Inoltre, UIDeviceOrientation include altre opzioni come UIDeviceOrientationFaceUp, UIDeviceOrientationFaceDown e UIDeviceOrientationUnknown. Se stai ruotando l'interfaccia in modo che corrisponda all'orientamento del dispositivo, puoi provare a ottenere UIDeviceOrientation da [UIApplication sharedApplication] .statusBarOrientation.

+3

Potrebbe essere che il panorama a sinistra/a destra sia dovuto all'utilizzo della fotocamera frontale vs posteriore? – VaporwareWolf

+0

No, i valori sono uguali per entrambe le telecamere anteriore e posteriore. –

0

In realtà, non vi sono errori nella vostra implementazione ed è possibile eliminare l'intero caso switch.

Perché caso interruttore può essere eliminato:

I valori corressponding di dispositivo e video orientamenti sono numericamente uguali, cioè fino a quando l'orientamento del dispositivo non è sconosciuta, faccia alto o verso il basso, è possibile equiparare UIDeviceOrientation e AVCaptureVideoOrientation

Circa la nomenclatura di confusione:

Orientamento del dispositivo LandscapeLeft => l'a p del tuo telefono è sulla sinistra.

Quindi, immagina solo dove il punto di partenza del video è ..... è vero, ANGOLO IN ALTO => Orizzontale a destra.

Analogamente nell'orientamento orizzontale, l'orientamento del video è lontano dalla parte superiore del telefono. Questo non confonde in caso di ritratto e capovolta

Proof

Quando si acquisisce un fermo immagine, controllare il valore di orientamento EXIF. per il paesaggio a sinistra, l'orientamento EXIF ​​è 3 (180 gradi di inversione) Per una corretta paesaggio, l'orientamento EXIF ​​è 1 (nessuna rotazione)

Quando si sta visualizzando, l'orientamento exif viene convertito corrispondente UIImageOrientation, quindi non ci dovrebbero non essere inversione.

Nel caso della fotocamera frontale, ho notato che i valori EXIF, semplicemente seguono i valori di orientamento del video (l'effetto specchio non è curato). Vengono utilizzati solo i valori 1, 3, 6, 8 di EXIF.

Inoltre, lo specchio è sempre lungo la linea dalla parte superiore del dispositivo verso il basso Così, paesaggio sembrerà a testa in giù, mentre ritratto e capovolta ritratto è appena specchia