剛開始使用AVFoundation進行採集的時候,常常會發現採集回來的圖片方向不對。通常咱們都是垂直(HOME鍵在底部)操做手機,可是在手機用相冊或者在電腦上點開採集的圖片時,都會發現圖片逆時針旋轉了90度。爲了發現問題的所在,咱們須要瞭解一下一般在圖片採集中咱們會遇到的各類方向。bash
平常開發中,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
在進行媒體的採集中,不管是進行拍照和錄像,都會有一個類似的操做,即設置鏈接的videoOrientation
屬性。系統是不知道你視頻圖片的正確方向的,只能經過videoOrientation
的值來進行圖片方向的設置。視頻方向一共有四個,這裏使用Home鍵的位置進行解釋:code
AVCaptureVideoOrientationPortrait
: HOME鍵在底部AVCaptureVideoOrientationPortraitUpsideDown
: HOME鍵在頂部AVCaptureVideoOrientationLandscapeRight
: HOME鍵在右邊AVCaptureVideoOrientationLandscapeLeft
: HOME鍵在左邊簡單來講,
AVCaptureVideoOrientation
是不會真正的修改圖片像素點的位置,只是給圖片設置一個合適的方向(參照當前視頻的正確方向)屬性。orm
在瞭解了UIImageOrientation
和AVCaptureVideoOrientation
後,咱們就來解答開篇問題,找出引起這個問題的「罪魁禍首」。cdn
下面的講解中,我簡單把圖片當作是兩個部分組成:視頻
- 圖片數據
- 圖片方向
在圖片採集的時候,圖片數據是按照相機獲取的數據。平時咱們都是都認HOME鍵在下方是正確的方向,可是對於相機,他正確的方向是攝像頭在用戶的左上方的狀況,即HOME鍵在右邊的狀況:blog
PS:圖片數據的方向就是相機的方向
所以在videoOrientation
爲AVCaptureVideoOrientationPortrait
的狀況下,圖片數據效果就是右側的圖片。雖然他的圖片方向是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 支持橫屏,有的不支持。在方向問題解決上是有兩種主要的解決方向的:
videoOrientation
屬性,通常就支持AVCaptureVideoOrientationPortrait
[UIDevice currentDevice].orientation
)進行變換(CATransform
)videoOrientation
)的時候,都須要獲取最新的videoOrientation
PS: AVCaptureConnection 在
input
發生變化的時候,會跟着變化。好比切換先後攝像頭,old input
被移除,new input
被添加,最後會致使connection
的替換。
至此就是我在圖片採集遇到的一個問題和解決方法。你們有什麼意見或者建議,歡迎在評論區中提出。