圖片採集的方向問題

方向問題

剛開始使用AVFoundation進行採集的時候,常常會發現採集回來的圖片方向不對。通常咱們都是垂直(HOME鍵在底部)操做手機,可是在手機用相冊或者在電腦上點開採集的圖片時,都會發現圖片逆時針旋轉了90度。爲了發現問題的所在,咱們須要瞭解一下一般在圖片採集中咱們會遇到的各類方向。bash

圖片方向 UIImageOrientation

平常開發中,UIImage是一個使用頻率很高的類,可是通常咱們並不太在乎他的方向問題。咱們能夠經過imageOrientation屬性獲取UIImage的圖片方向,即UIImageOrientation。該方向總共有八種狀況,用於表示當前圖片的方向狀態:app

  • UIImageOrientationUp: 方向正確
  • UIImageOrientationDown: 旋轉180度
  • UIImageOrientationLeft: 逆時針旋轉90度
  • UIImageOrientationRight: 順時針旋轉90度
  • UIImageOrientationUpMirrored: 水平鏡像
  • UIImageOrientationDownMirrored: 旋轉180度 + 水平鏡像
  • UIImageOrientationLeftMirrored: 逆時針旋轉90度 + 垂直鏡像
  • UIImageOrientationRightMirrored: 順時針旋轉90度 + 垂直鏡像

在平時咱們並無發現UIImage的顯示方向有問題,是由於它在顯示的時候會根據當前圖片的imageOrientation屬性,進行相應的轉換,即逆操做。若是值是UIImageOrientationLeft則表示圖片在顯示的時候,須要順時針旋轉90度。ide

簡單來講,UIImageOrientation就是告訴咱們該圖像的方向屬於什麼狀態,在顯示時候是須要作一個逆操做來讓圖像真正變成咱們想要的方向。ui

回到文章一開始的問題,圖片逆時針旋轉了90度是由於當前的圖像的像素點數據是真的逆時針旋轉了90度。雖然整個圖片中是有圖片方向這個屬性的,可是在用文件顯示圖片的過程當中,系統並不會根據該屬性進行逆操做,而是耿直的直接顯示了。spa

視頻方向 AVCaptureVideoOrientation

在進行媒體的採集中,不管是進行拍照和錄像,都會有一個類似的操做,即設置鏈接的videoOrientation屬性。系統是不知道你視頻圖片的正確方向的,只能經過videoOrientation的值來進行圖片方向的設置。視頻方向一共有四個,這裏使用Home鍵的位置進行解釋:code

  • AVCaptureVideoOrientationPortrait: HOME鍵在底部
  • AVCaptureVideoOrientationPortraitUpsideDown: HOME鍵在頂部
  • AVCaptureVideoOrientationLandscapeRight: HOME鍵在右邊
  • AVCaptureVideoOrientationLandscapeLeft: HOME鍵在左邊

簡單來講,AVCaptureVideoOrientation是不會真正的修改圖片像素點的位置,只是給圖片設置一個合適的方向(參照當前視頻的正確方向)屬性。orm

罪魁禍首

在瞭解了UIImageOrientationAVCaptureVideoOrientation後,咱們就來解答開篇問題,找出引起這個問題的「罪魁禍首」。cdn

下面的講解中,我簡單把圖片當作是兩個部分組成:視頻

  • 圖片數據
  • 圖片方向

在圖片採集的時候,圖片數據是按照相機獲取的數據。平時咱們都是都認HOME鍵在下方是正確的方向,可是對於相機,他正確的方向是攝像頭在用戶的左上方的狀況,即HOME鍵在右邊的狀況:blog

用戶與相機方向

PS:圖片數據的方向就是相機的方向

所以在videoOrientationAVCaptureVideoOrientationPortrait的狀況下,圖片數據效果就是右側的圖片。雖然他的圖片方向是UIImageOrientationLeft,可是通常的文件顯示都是直接顯示他原來的面貌。所以,用戶看到的和咱們最終保存的圖片顛倒「罪魁禍首」就是相機的原始方向與咱們的預期不符合

解決方法

圖片自己是沒錯的,錯的是顯示的時候沒有沒有按圖片方向進行逆操做。那麼,咱們只要在每次採集到圖片的時候對圖片進行一次逆操做,讓圖片數據直接變成與「圖片數據+圖片方向」的效果便可:

- (UIImage *)fixOrientation
{
    // No-op if the orientation is already correct
    if (self.imageOrientation == UIImageOrientationUp) return self;
    
    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    switch (self.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
            
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
            
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, self.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    }
    
    switch (self.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    }
    
    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
                                             CGImageGetBitsPerComponent(self.CGImage), 0,
                                             CGImageGetColorSpace(self.CGImage),
                                             CGImageGetBitmapInfo(self.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
            break;
            
        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.CGImage);
            break;
    }
    
    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}
複製代碼

PS:新生成的圖片imageOrientation是默認的UIImageOrientationUp

採集方向解決方向

有的 App 支持橫屏,有的不支持。在方向問題解決上是有兩種主要的解決方向的:

  • 不支持橫屏:
    1. 固定死 AVCaptureConnection 的videoOrientation屬性,通常就支持AVCaptureVideoOrientationPortrait
    2. 在獲取圖片的時候根據設備當前物理方向([UIDevice currentDevice].orientation)進行變換(CATransform)
  • 支持橫屏:
    1. 在每次肯定方向(videoOrientation)的時候,都須要獲取最新的videoOrientation

PS: AVCaptureConnection 在input 發生變化的時候,會跟着變化。好比切換先後攝像頭,old input被移除,new input被添加,最後會致使connection的替換。

至此就是我在圖片採集遇到的一個問題和解決方法。你們有什麼意見或者建議,歡迎在評論區中提出。

相關文章
相關標籤/搜索