iOS拍照图片旋转的问题

  很久之前,遇到了这种情况,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, 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;
}

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

  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){0, 0, 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.

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

 

原文地址:https://www.cnblogs.com/6duxz/p/10536757.html

时间: 2024-10-10 23:18:32

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

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

由于前面的博文中忽略了点内容,所以在这里补上,下面内容就是解决拍照或者选择图片显示的时候图片旋转了90度或者其他度数问题,以便照片可以正面显示:具体如下: 首先直接看上面博文下的拍完照或者选完图后处理部分: @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (resultCode) { case 1: if (data != null) { // 取得返

iOS 获取图片(拍照,图库,相册)

iOS  获取图片有三种方法 1 直接调用摄像头拍照 2 从相册中选择 3 从图库中选择 UIImagePickerController 是系统提供的用来获取图片和视频的接口: 用UIImagePickerController 类来获取图片视频: 大体分为以下几个步骤: 初始化UIImagePickerController 类 设置UIImagePickerController 实例的数据来源类型(下面解释): 设置设置代理 如果需要做图片修改的话设置allowsEditing =yes 数据

iOS 特定图片的按钮的旋转动画

最近做的东西中,要为一个有特定图片的按钮添加旋转动画,Demo代码如下: #import "ViewController.h" @interface ViewController () { BOOL flag; } @property (strong, nonatomic) UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; flag

Android 实例讲解添加本地图片和调用系统拍照图片

在项目的开发过程我们离不开图片,而有时候需要调用本地的图片,有时候需要调用拍照图片.同时实现拍照的方法有两种,一种是调用系统拍照功能,另一种是自定义拍照功能.而本博文目前只讲解第一种方法,第二种方法后期在加以讲解. 添加本地图片和调用系统拍照图片主要是通过调用acitivity跳转startActivityForResult(Intent intent, int requestCode)方法和activity返回结果onActivityResult(int requestCode, int re

IOS UIDevice & IOS检测屏幕旋转实例

一 UIDevice 简介 UIDevice类提供了一个单例实例代表当前的设备.从这个实例中可以获得的信息设备,比如操作系统名称.电池电量值(batteryLevel).电池状态(batteryState).设备的类型(model,比如iPod.iPhone等).设备的系统(systemVersion) 二 获取 UIDevice 实例 通过[UIDevice currentDevice]可以获取这个单粒对象 UIDevice *device = [UIDevice currentDevice]

ios 拍照与摄像

ios 拍照与摄像 (2012-11-23 14:38:40) 该类继承自UINavigationController类 步骤: 检查媒体来源模式是否可用 检查该来源模式下所支持的媒体类型 创建图像选取控制器,设置其属性并显示 在委托协议方法中处理 1.检查媒体来源 调用UIImagePickerController类的静态方法isSourceTypeAvailable来检查 sourceType是一个UIImagePickerControllerSourceType类型的枚举值,它表示图像选取

麦子学院ios笔记:IOS把图片缓存到本地的几种方法

把ios的图片缓存到本地的方法有几种?现在来看看学生在麦子学院学习ios开发的笔记中有讲到哪几种方法呢? <code>把图片缓存到本地,在很多场景都会用到,如果是只储存文字信息,那建一个plist文件,或者数据库就能很方便的解决问题,但是如果存图片到沙盒就没那么方便了.这里介绍两种保存图片到沙盒的方法. </code> 一.把图片转为base64的字符串存到数据库中或者plist文件中,然后用到的时候再取出来 <code class="hljs" obje

ios手势识别之旋转+缩放

1 #import "NJViewController.h" 2 3 @interface NJViewController ()<UIGestureRecognizerDelegate> 4 @property (weak, nonatomic) IBOutlet UIImageView *iconView; 5 6 @end 7 8 @implementation NJViewController 9 10 - (void)viewDidLoad 11 { 12 [su

IOS把图片做成圆形效果

利用CAShapeLayer可以制作出任意的几何图形,把它作为UIImageView的遮罩,达到把图片做成圆形效果. imgView = [[UIImageView alloc]initWithFrame:CGRectMake(10, 35, 80, 80)]; imgView.image = [UIImage imageNamed:@"ma.jpg"]; UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:CGPoin