【iOS】图片采集的方向问题

方向问题

刚开始使用AVFoundation进行采集的时候,经常会发现采集回来的图片方向不对。一般我们都是垂直(HOME键在底部)操作手机,但是在手机用相册或者在电脑上点开采集的图片时,都会发现图片逆时针旋转了90度。为了发现问题的所在,我们需要了解一下通常在图片采集中我们会遇到的各种方向。

图片方向 UIImageOrientation

日常开发中,UIImage是一个使用频率很高的类,但是一般我们并不太在意他的方向问题。我们可以通过imageOrientation属性获取UIImage的图片方向,即UIImageOrientation。该方向总共有八种情况,用于表示当前图片的方向状态:

  • UIImageOrientationUp: 方向正确
  • UIImageOrientationDown: 旋转180度
  • UIImageOrientationLeft: 逆时针旋转90度
  • UIImageOrientationRight: 顺时针旋转90度
  • UIImageOrientationUpMirrored: 水平镜像
  • UIImageOrientationDownMirrored: 旋转180度 + 水平镜像
  • UIImageOrientationLeftMirrored: 逆时针旋转90度 + 垂直镜像
  • UIImageOrientationRightMirrored: 顺时针旋转90度 + 垂直镜像

在平时我们并没有发现UIImage的显示方向有问题,是因为它在显示的时候会根据当前图片的imageOrientation属性,进行相应的转换,即逆操作。如果值是UIImageOrientationLeft则表示图片在显示的时候,需要顺时针旋转90度。

简单来说,UIImageOrientation就是告诉我们该图像的方向属于什么状态,在显示时候是需要做一个逆操作来让图像真正变成我们想要的方向。

回到文章一开始的问题,图片逆时针旋转了90度是因为当前的图像的像素点数据是真的逆时针旋转了90度。虽然整个图片中是有图片方向这个属性的,但是在用文件显示图片的过程中,系统并不会根据该属性进行逆操作,而是耿直的直接显示了。

视频方向 AVCaptureVideoOrientation

在进行媒体的采集中,无论是进行拍照和录像,都会有一个相似的操作,即设置连接的videoOrientation属性。系统是不知道你视频图片的正确方向的,只能通过videoOrientation的值来进行图片方向的设置。视频方向一共有四个,这里使用Home键的位置进行解释:

  • AVCaptureVideoOrientationPortrait: HOME键在底部
  • AVCaptureVideoOrientationPortraitUpsideDown: HOME键在顶部
  • AVCaptureVideoOrientationLandscapeRight: HOME键在右边
  • AVCaptureVideoOrientationLandscapeLeft: HOME键在左边

简单来说,AVCaptureVideoOrientation是不会真正的修改图片像素点的位置,只是给图片设置一个合适的方向(参照当前视频的正确方向)属性。

罪魁祸首

在了解了UIImageOrientationAVCaptureVideoOrientation后,我们就来解答开篇问题,找出引发这个问题的“罪魁祸首”。

下面的讲解中,我简单把图片看成是两个部分组成:

  • 图片数据
  • 图片方向

在图片采集的时候,图片数据是按照相机获取的数据。平时我们都是都认HOME键在下方是正确的方向,但是对于相机,他正确的方向是摄像头在用户的左上方的情况,即HOME键在右边的情况:

用户与相机方向

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的替换。

至此就是我在图片采集遇到的一个问题和解决方法。大家有什么意见或者建议,欢迎在评论区中提出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容