刚开始使用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
的替换。
至此就是我在图片采集遇到的一个问题和解决方法。你们有什么意见或者建议,欢迎在评论区中提出。