很久之前,遇到了这种情况,iOS某端拍照上传到服务器,其他iOS端从服务器下载该照片展示,发现图片逆时针旋转了90度。当时百度了一下,找到一段代码修正image方向,问题解决了,但没有深入理解底层原理。最近又遇到这个问题,还是同样的解决方案。但是codereview的时候同事问为什么这么写,就深入研究了一下。

  首先我们要知道image的imageOrientation属性。它是记录拍照时手机方向的,iOS默认横屏Home键在右侧为标准拍照姿势,imageOrientation为UIImageOrientationUp。知道了拍照时相机方向,展示的时候就能对照片就行仿射变换,让它能正确显示。

  看到这里,就可以直接去大神的深度分析文章了:如何处理iOS中照片的方向

直观的解决方案

- (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, );
transform = CGAffineTransformRotate(transform, M_PI_2);
break; case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, , 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, );
transform = CGAffineTransformScale(transform, -, );
break; case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, );
transform = CGAffineTransformScale(transform, -, );
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), ,
CGImageGetColorSpace(self.CGImage),
CGImageGetBitmapInfo(self.CGImage));
CGContextConcatCTM(ctx, transform);
switch (self.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(,,self.size.height,self.size.width), self.CGImage);
break; default:
CGContextDrawImage(ctx, CGRectMake(,,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;
}

代码有些长,不过却非常直观。这里面涉及到图像矩阵变换的操作,理解起来可能稍稍有些困难,接下来,我会有另外一篇文章专门来介绍图像变换。现在,记住下面两点便能够很好的帮助理解:

  1. 图像的原点在左下角
  2. 矩阵变换时,后面的矩阵先作用,前面的矩阵后作用

UIImageOrientationDown方向为例,,很明显它翻转了180度。那么对它的旋转需要两步,第一步是以左下方为原点旋转180度,(此时顺时针还是逆时针旋转效果一样)旋转后上图变为: 。用代码表示为:

transform = CGAffineTransformRotate(transform, M_PI);

因为是以左下方为原点旋转的,所以整幅图被移到了第三象限。第二步需要将其平移至第一象限,向右上方进行平移即可。x方向上移动距离为图像的宽度,y方向上移动距离为图像的高度,所以平移后图像变为:。代码为:

transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);

再加上我们前面所说的第二点,矩阵变换时,后面的矩阵先作用,前面的矩阵后作用,那么只需要将上面两步颠倒即可:

transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
transform = CGAffineTransformRotate(transform, M_PI);

其它的方向可以用完全一样的方法来分析,这里不再一一赘述。

第二种简单的方法

第二种方法同样也是StackOverflow上的答案,没那么直观,但非常简单:

- (UIImage *)normalizedImage {
if (self.imageOrientation == UIImageOrientationUp) return self; UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
[self drawInRect:(CGRect){, , self.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}

这里是利用了UIImage中的drawInRect方法,它会将图像绘制到画布上,并且已经考虑好了图像的方向,开发文档这样解释:

-drawInRect:
Draws the entire image in the specified rectangle, scaling it as needed to fit. Discussion
This method draws the entire image in the current graphics context, respecting the image’s orientation setting. In the default coordinate system, images are situated down and to the right of the origin of the specified rectangle. This method respects any transforms applied to the current graphics context, however.

第二个方法简单易于理解,我就采用了第二种解决方案。希望能帮到大家。

 

iOS拍照图片旋转的问题的更多相关文章

  1. ios手机竖屏拍照图片旋转90°问题解决方法

    手机拍照会给图片添加一个Orientaion信息(即拍照方向),如下: 用ios手机拍照,系统会给图片加上一个方向的属性, ios相机默认的拍照方向是后摄Home键在右为正,前摄Home键在左为正. ...

  2. H5 拍照图片旋转、压缩和上传

    原文地址:github.com/whinc/blog/… 最近接到一个“发表评论”的需求:用户输入评论并且可以拍照或从相册选择图片上传,即支持图文评论.需要同时在 H5 和小程序两端实现,该需求处理图 ...

  3. android 图片拍照图片旋转的处理方式

    第一种:String str=path; /** * 读取图片属性:旋转的角度 * * @param path * 图片绝对路径 * @return degree旋转的角度 */ private vo ...

  4. ios中图片旋转

    @interface ViewController () { UIImageView *_imageview; BOOL flag; } @end @implementation ViewContro ...

  5. vue实现PC端调用摄像头拍照人脸录入、移动端调用手机前置摄像头人脸录入、及图片旋转矫正、压缩上传base64格式/文件格式

    进入正题 1. PC端调用摄像头拍照上传base64格式到后台,这个没什么花里胡哨的骚操作,直接看代码 (canvas + video) <template> <div> &l ...

  6. 解决ios横屏拍照图片自动旋转90度问题

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  7. 图片上传前 压缩,base64图片压缩 Exif.js处理ios拍照倒置等问题

    曾写过在前端把图片按比例压缩不失真上传服务器的前端和后台,可惜没有及时做总结保留代码,只记得js利用了base64位压缩和Exif.js进行图片处理,还有其中让我头疼的ios拍照上传后会倒置等诸多问题 ...

  8. android 选择图片或拍照时旋转了90度问题

    由于前面的博文中忽略了点内容,所以在这里补上,下面内容就是解决拍照或者选择图片显示的时候图片旋转了90度或者其他度数问题,以便照片可以正面显示:具体如下: 首先直接看上面博文下的拍完照或者选完图后处理 ...

  9. iOS 图片旋转方法

    iOS 图片旋转方法 通过 CGImage 或 CIImage 旋转特定角度 UIImage可通过CGImage或CIImage初始化,初始化方法分别为init(cgImage: CGImage, s ...

随机推荐

  1. php 写斐波那契数列

    <?php $arr = []; for($i=1;$i<9;$i++){ if($i==1 || $i ==2){ $arr[$i-1] = 1; }else{ $arr[$i-1] = ...

  2. 【CH6801】棋盘覆盖

    题目大意:给定一个 N*N 的棋盘,棋盘上有些位置不能防止任何东西,现用 1*2 的骨牌填充棋盘,问最多能铺多少块骨牌. 题解:由于骨牌只能覆盖相邻的两个格子,那么按照对角线进行划分的格子可以保证一定 ...

  3. django 前端模板继承显示model中使用choices的字段

    比如model中的一个class Need class Need(models.Model): """ 任务 """ party_a=mod ...

  4. 第二十六篇-单击事件、Toast(提示框信息)

    单击事件有3种方法: 第一种: layout.xml <?xml version="1.0" encoding="utf-8"?> <Line ...

  5. python面向对象中的一些特殊__方法__

    1. __doc__ 表示类的描述信息 class Foo: """ 描述类信息""" def func(self): pass print ...

  6. 记cccc天梯赛第三届决赛

        首先我很想知道,为什么我没有参加初赛,就可以去决赛,这个究竟有没有初赛,这真是未解之谜.     其次,会长说得不错,菜是原罪.不知道这次的表现能不能把我送去湘潭挑战赛....     我身边 ...

  7. (数学 尾0的个数) 51nod1003 阶乘后面0的数量

    n的阶乘后面有多少个0? 6的阶乘 = 1*2*3*4*5*6 = 720,720后面有1个0. 收起   输入 一个数N(1 <= N <= 10^9) 输出 输出0的数量 输入样例 5 ...

  8. MySQL利用binlog恢复误操作数据(python脚本)

    在人工手动进行一些数据库写操作的时候(比方说数据订正),尤其是一些不可控的批量更新或删除,通常都建议备份后操作.不过不怕万一,就怕一万,有备无患总是好的.在线上或者测试环境误操作导致数据被删除或者更新 ...

  9. opencv: 角点检测源码分析;

    以下6个函数是opencv有关角点检测的函数 ConerHarris, cornoerMinEigenVal,CornorEigenValsAndVecs, preConerDetect, coner ...

  10. 一名全栈设计师的Mac工具箱(设计,开发,效率)

        我喜欢把自己定义为一个会一些设计的全栈工程师.在一些大型企业项目中,我一般担任架构师的角色,而到了我自己负责的个人或开源项目中,我就成了一名全栈设计师.我喜欢用自学而来的那些设计技能进行网站或 ...