2011-10-21 60 views
7

我正在使用AVFoundation類來實現我的應用程序中的自定義相機。我只拍攝靜止圖像,而不是視頻。我有一切工作,但是被某些東西困住了。當拍攝靜止圖像時,我會考慮設備方向,並適當地設置視頻連接的視頻方向。代碼片段:爲什麼AVCaptureVideoOrientation風景模式導致顛倒的靜止圖像?

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

請注意我在風景案例中的評論。我必須扭轉方向映射或者產生的圖像顛倒。我使用以下代碼捕獲並保存圖像:

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

沒有其他圖像處理或操作。

我的應用程序使用上面的代碼。我只是想了解爲什麼方向常數必須逆轉爲景觀方向。謝謝!

+0

不要使用的UIDevice的方向,當你真的想使用UIInterface方向。 – Till

+0

在這種情況下,您不能使用'UIInterface'方向。拍攝圖像時的設備方向始終正確。在捕獲數據之後,「stillImage」的圖像方向不正確。我能夠使用UIDeviceOrientation解決問題,並在旋轉時轉換爲適當的UIImageOrientation。那時候,沒有人在這裏打手勢,所以我沒有發佈代碼。如果我有時間,我會添加代碼。 – XJones

+0

@XJones,請用確切的解決方案更新您的答案。 – Hemang

回答

20

嘿,似乎沒有人覺得在這一個打瞌睡。原來,答案很簡單。經由stillImageOutput captureStillImageAsynchronouslyFromConnection:...方法捕獲圖像總是最終具有以下屬性:

  • UIImage的取向=總是UIImageOrientationRight無論裝置的取向的
  • UIImage的大小= W x高(例如肖像寬×縱向高度,取決於相機上分辨率)
  • CGImage大小=取決於設備取向(例如縱向或橫向)

所以將圖像旋轉了解決方案是使用設備定向結合CGImage大小應用適當的仿射變換。正如我在回答我的問題,我不是在代碼中的解決方案,但我最後寫一個調用的程序:

- (UIImage *)imageRotatedUpForDeviceOrientation:(UIDeviceOrientation)deviceOrientation 

包含各種圖像處理功能增強UIImage的類別。

編輯 - 實現示例

我已經收到了一些關於本作的功能代碼的請求。我從一個工作應用程序中提取了相關的實現。

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

很好的答案,但幾行的實施將節省我一些打字! 8 - ) – kalperin

+3

@XJones我無法理解下面的代碼行,請你爲我解釋一下嗎? contextRect = CGRectTranspose(contextRect); –

+1

CGRectTranspose WTF – jjxtra

6

至於爲什麼你需要AVCaptureVideoOrientationLandscape 右鍵當設備的方向是UIDeviceOrientationLandscape ,這是因爲,出於某種原因,UIDeviceOrientationLandscape == UIInterfaceOrientationLandscape 和AVCaptureVideoOrientations把住UIInterfaceOrientation約定。

此外,UIDeviceOrientation incudes其他選項,如UIDeviceOrientationFaceUp,UIDeviceOrientationFaceDown和UIDeviceOrientationUnknown。如果將界面旋轉以匹配設備的方向,則可以嘗試從[UIApplication sharedApplication] .statusBarOrientation獲取UIDeviceOrientation。

+3

難道是風景左/右是由於使用前置vs後置攝像頭? – VaporwareWolf

+0

不,前後相機的數值相同。 –

0

實際上,在你的實現中沒有錯誤,你可以消除整個開關情況。

爲什麼開關的情況下可以被消除:

設備和視頻取向的corressponding值數值上相等,即只要設備定位不不明,正視或面朝下,你可以畫上等號UIDeviceOrientation和AVCaptureVideoOrientation

關於混亂的命名:

設備方向LandscapeLeft =>在以你手機的p在左邊。

所以,想象一下視頻的起點在哪裏......沒錯,TOP RIGHT corner =>右轉。

同樣在其他風景方向,視頻方向遠離手機的頂部。 這不是在縱向的情況下,混淆和顛倒

證明

當您拍攝靜止圖像,請檢查方向EXIF值。 對於橫向離開後,EXIF取向爲3(180度反轉) 對於橫向權,EXIF取向爲1(無旋轉)

當你顯示,你的EXIF取向被轉換成相應的UIImageOrientation,所以應該不倒置。

在前置攝像頭的情況下,我注意到EXIF值,只需按照視頻方向值(不考慮鏡面效果)。只使用EXIF的1,3,6,8個值。

另外,鏡子總是沿着從設備底部 所以上面的線,景觀會顯得倒掛,而肖像倒掛肖像恰是鏡像

相關問題