使用imageIO获取和修改图片的exif信息
一幅图片除了包含我们能看见的像素信息,背后还包含了拍摄时间,光圈大小,曝光等信息。UIImage类将这些细节信息都隐藏了起来,只提供我们关心的图片尺寸,图片方向等。我们可以通过imageIO框架获取到图片背后的所有信息,下面就让我们一起看看。
一、获取图片信息
imageIO框架是iOS中偏底层一点儿的框架,它内部提供的接口都是C风格的,关键数据也都是使用CoreFoundation进行存储。庆幸的是CoreFoundation中有很多数据类型都可以上层的数据Foundation框架中的数据类型进行无缝桥接。这也就大大方便了我们对图片信息的操作。
CGImageSourceRef是整个imageIO的入口,通过它我们可以完成从文件的加载图片。加载完成以后我们就得到一个CGImageSourceRef,通过CGImageSourceRef我们就可以获取图片文件的大小,UTI(uniform type identifier),内部包含几张图片,访问每一张图片以及获取每张图片对应的exif信息等。
你可能会有一个疑问,为什么会有几张图片呢?
这块儿我解释一下,imageSourceRef和文件是一一对应的,通常我们见到的图片文件(例如jpg,png)内部都只有一张图片,这种情况我们通过CGImageSourceGetCount方法得到的就会是1。但是不能排除一个图片文件中会有多种图片的情况,例如gif文件,这个时候一个文件中就可能包含几张甚至几十张图片。前面我写的一片博客《IOS中如何解析并显示Gif》就是通过imageSource实现加载和解析gif的功能。
下面是系统相机拍的照片的exif信息:
image property: { ColorModel = RGB; DPIHeight = 72; DPIWidth = 72; Depth = 8; Orientation = 6; PixelHeight = 2448; PixelWidth = 3264; "{Exif}" = { ApertureValue = "2.526069"; BrightnessValue = "-0.5140446"; ColorSpace = 1; ComponentsConfiguration = ( 1, 2, 3, 0 ); DateTimeDigitized = "2013:06:24 22:11:30"; DateTimeOriginal = "2013:06:24 22:11:30"; ExifVersion = ( 2, 2, 1 ); ExposureMode = 0; ExposureProgram = 2; ExposureTime = "0.06666667"; FNumber = "2.4"; Flash = 16; FlashPixVersion = ( 1, 0 ); FocalLenIn35mmFilm = 33; FocalLength = "4.13"; ISOSpeedRatings = ( 400 ); MeteringMode = 3; PixelXDimension = 3264; PixelYDimension = 2448; SceneCaptureType = 0; SensingMethod = 2; ShutterSpeedValue = "3.906905"; SubjectArea = ( 2815, 1187, 610, 612 ); WhiteBalance = 0; }; "{GPS}" = { Altitude = "27.77328"; AltitudeRef = 0; Latitude = "22.5645"; LatitudeRef = N; Longitude = "113.8886666666667"; LongitudeRef = E; TimeStamp = "14:11:23.36"; }; "{TIFF}" = { DateTime = "2013:06:24 22:11:30"; Make = Apple; Model = "iPhone 5"; Orientation = 6; ResolutionUnit = 2; Software = "6.1.4"; XResolution = 72; YResolution = 72; "_YCbCrPositioning" = 1; }; }
从中我们可以看出最开始的几项分别显示了当前图片的颜色模式,色深,x,y方向的DPI,实际像素以及图片的方向。我最开始看到这个方向时,心中一喜这不是UIImage中的imageOrientation,但是实验发现这个方向和UIImage中的imageOrientation并不相等,此处的方向是exif标准定义的方向,从1到8分别对应这UIImage中的8个方向,只是顺序不一样,它们对应关系如下:
enum { exifOrientationUp = 1, // UIImageOrientationUp exifOrientationDown = 3, // UIImageOrientationDown exifOrientationLeft = 6, // UIImageOrientationLeft exifOrientationRight = 8, // UIImageOrientationRight // these four exifOrientation does not support by all camera, but IOS support these orientation exifOrientationUpMirrored = 2, // UIImageOrientationUpMirrored exifOrientationDownMirrored = 4, // UIImageOrientationDownMirrored exifOrientationLeftMirrored = 5, // UIImageOrientationLeftMirrored exifOrientationRightMirrored = 7, // UIImageOrientationRightMirrored }; typedef NSInteger ExifOrientation;
目前市面上的大部分数码相机和手机都会内置一个方向感应器,拍出的照片中会写如方向信息,但是通常都只会有前四种方向。这几种Mirrored方向通常都是手机前置摄像头自拍的时候才会设置。
exif为什么要搞这么一个方向呢?
几乎所有的摄像头在出场的时候成相芯片都是有方向的,拍出来的照片的像素都是默认方向的。如果每拍一张照片就对这些像素进行旋转,如果数码相机每秒连拍20张来算,旋转操作将会非常耗时。更聪明的做法是拍照时只记录一个方向,然后显示的时候按方向显示出来即可。因此exif定义了一个标准的方向参数,只要读图的软件都来遵守规则,加载时候读取图片方向,然后做相应的旋转即可。这样既可以达到快速成像的目的,又能达到正确的显示,何乐而不为呢。
常见的图片浏览和编辑软件都遵守这个规则,但是有一个我们最常用的看图软件(windows自带的看图程序)不会去读这个方向,因此我们将数码相机和手机拍出来的图片导入windows上时,会经常遇到方向错误的问题。不知道windows帝国是怎么想的,或许和定义exif的组织有什么过节吧。
图片信息中除了上面看提到的那些,还有拍摄的GPS信息,iOS自带的相册软件中的地点tab就是按照GPS信息实现的。还有很多其他的信息,感兴趣的可以自己写个程序研究研究,这里就不展开了。
二、修改图片exif信息
我们除了可以通过CGImageSourceRef读取图片信息,还可以通过CGImageDestinationRef来创建图片文件,在创建的时候我们可以指定很多信息。例如我们可以通过CGImageDestinationRef来修改现有图片的GPS信息,图片方向,甚至还可以自己写一个制作gif的小工具。
这块儿我做了个实验,通过修改图片信息的方向,来完成旋转图片的功能。原图方向为1(exif方向),我修改为3。保存后发现图片的确翻转过来了。于是更进一步想着试一下看看能不能完成90°旋转的功能,于是把图片方向改为6,保存后发现通过UIImage加载的图片显示出现问题了,虽然方向是转过来了,但是显示却被拉伸了。但是把保存后的图片存到系统相册,发现一切正常。
到底是哪块儿出了问题呢?
我想了一下是不是应该在修改方向的同时修改一下图片的pixelWidth和pixelHeight参数,于是满心欢喜的尝试了一下,结果还是UIImage加载出来还是拉伸显示,导入系统相册依旧正常。郁闷了到底是什么参数没设置对呢?暂时还没有什么思路,各路英雄要是有什么思路的话欢迎一起讨论。
测试工程下载地址:
测试工程中包含读取图片信息和修改图片方向的工具类,以及简单的测试代码。上面修改图片方向的那个问题还是没有解决,希望路过的英雄能帮忙看看问题出在哪里,给点儿建议,谢谢。