前言
本文主要介绍带Exif信息的JPEG图片的保存优化。原来在这种情况下,整个过程分为两步:
- 首先调用Bitmap的
compress()
保存图片数据。 - 然后通过ExifInterface类设置需要保存的Exif信息。
是否能通过一步直接完成Exif信息和Bitmap数据的写入呢?答案是能。
本文首先介绍了JPEG文件结构,通过对该结构的剖析,可以看出一次写入Exif信息和Bitmap数据是完全可能的。其次介绍了ExifInterface的使用方法。最后介绍了JPEG保存优化的核心类ExifOutputStream。
JPEG文件结构
基本概念:
- JPEG(Joint Photographic Experts Group,一种针对图像的有损压缩方法):描述一个图像如何转换为字节流。
- JFIF(JPEG File Interchange Format,JPEG文件交换格式):描述一个JPEG字节流如何转换为电脑上存储的文件。
- EXIF(Exchangeable image file format,可交换图像文件格式):可以记录照片的属性信息和拍摄信息。Exif信息是附加在JPEG文件头中的,以0xFFE1为开头,后面两个字节表示Exif信息的长度。
为了更加简单的说明JPEG文件的结构,我们拿带有EXIF信息的最简单的图片作为例子,一共8张图,如下图所示:
第一张图是正常的F,后面7张都是对F进行了翻转或旋转,这些图片只有Exif中的Orientation值不一样,其余数据完全一样,因此这里我们就拿第八张图分析,我们通过iHex应用查看了该图片的16进制串,如下:
|
是不是一头雾水?首先我们给出几个概念:
- Exif信息以0xFFE1开头,后面的2个字节是Exif信息的长度,该长度为这2个字节和后面Exif信息的长度和。
- Exif每个Tag的结构为AAAA BBBB CCCCCCCC DDDDDDDD,其中AAAA为Tag Name,DDDDDDDD为Tag Value,BBBB为Tag Value的数据类型,CCCCCCCC为Tag Value的个数。
这里将该串进行了拆解,并一一进行分析:
十六进制串 | 解释 |
---|---|
FFD8 | SOI(Start Of Image),JPEG的文件头。 |
FFE0 | 数码相机信息的标记头,即以下信息是数码配置信息。 |
0010 | 长度信息+真实数据的总长度为16字节,即真实数据的长度为14字节。 |
4A46 49460001 010100DC 00DC0000 | 数码相机配置信息。 |
FFE1 | Exif 标记,表示Exif信息的开始。 |
0022 | 值为34,即Exif的数据+长度信息的长度为34字节,表示下面的Exif数据长度为32字节。 |
45786966 0000 | Exif的ASCII字符串。 |
4949 | Exif的数据是按大端(motorola byte align)还是小端(Intel byte align),4949表示小端,4d4d表示大端。 |
2A00 | 值为002A,常量。因为是小端,所以是2A00。 |
0800 0000 | 值为00000008,常量,表示第一个exif tag距离4949的偏移量。 |
0100 | 值为0001,表示后面有一个tag。 |
1201 | 值为0112,表示tag name为orientation。 |
0300 | 值为0003,表示类型为unsigned short,即orientation的值占2个字节。 |
01000000 | orientation对应的值的个数,这里是1个。 |
08000000 00000000 | 值为00000000 00000008,具体的含义在下文中解释。 |
… | 图片数据。 |
FFD9 | EOI(End Of Image),JPEG的文件尾。 |
其中,需要进一步解释的是ORIENTATION,值可以从1-8,每个值对应的是图片的不同偏转,如下图所示:
在Android相机拍照中,一般只需要处理其中4种情况,分别是:
- 1:正常角度,对应ExifInterface.ORIENTATION_NORMAL。
- 3:偏转180度,需要偏转180度去纠正,对应ExifInterface.ORIENTATION_ROTATE_180。
- 6:偏转270度,需要偏转90度去纠正,对应ExifInterface.ORIENTATION_ROTATE_90。
- 8:偏转90度,需要偏转270度去纠正,对应ExifInterface.ORIENTATION_ROTATE_270。
如下图所示:
在Android Studio中能够看到没纠正过角度的图片,Mac的预览图片看到的是自动纠正过角度的图片。
一般来说,我们可以通过switch语句对每个orientation的值进行不同的纠正,但是 JPEG Orientation 给出了一个通用的纠正算法,思想为:
|
具体实现如下:
|
这里用到了ExifInterface类,该类是系统提供的操作Exif信息的核心类,使用方法是:
|
这里需要注意一点:saveAttributes()这个方法很耗时,需要将原文件的数据拷贝到另一个文件,并删除原文件,然后重命名新文件,因此建议在调用该方法之前把该设置的Exif信息全通过setAttribute()设置完,使得saveAttributes()只调用一次。
Android 7.1中新增了ExifInterface Support Library,但是很奇怪,我使用该类获取orientation,始终获取不到,而用android.media.ExifInterface类却没问题。
ExifOutputStream
ExifOutputStream是系统内部不public的类,位于”com.android.gallery3d.exif”包中,它提供了一次写入Exif信息和JPEG数据的方式。我们可以通过writeExif()
实现一次IO写入JPEG和Exif信息。
|
根据统计,相比原来的实现,速度提升了10%。