2017-08-07 84 views
1

在我的测试中,由BitmapFactory.decodeFile()创建的Bitmap不尊重EXIF标头。是否有可能使BitmapFactory.decodeFile()尊重EXIF?

例如,由设备拍摄人像的图像,根据相机方位,而是将其存储在EXIF头不旋转,实际的像素数据,当我打电话Bitmap.getWidth()Bitmap.getHeight(),他们返回不正确的值(高度宽度,反之亦然)。

有没有办法让BitmapFactory.decodeFile()尊重EXIF并产生正确的Bitmap

如果不是,建议的模式是什么?

没有经验丰富的Android开发人员的建议,我所看到的唯一方法是预处理拍摄的图像(加载,根据EXIF旋转并保存)。但除了巨大的处理开销之外,这可能会导致OutOfMemoryException s适用于大型相机分辨率(如果通过使用BitmapFactory.Options.inSampleSize加载缩小的图像无法降低质量)。

回答

1

有没有办法让BitmapFactory.decodeFile()尊重EXIF并产生正确的位图?

不,对不起。

什么是处理此问题的推荐模式?

使用支持库的ExifInterface来确定所需的方向。然后,根据您对Bitmap的使用情况,旋转视图(例如,ImageView)或旋转BitmapThis sample project举例说明了这两种方法,但我使用了一组单独的EXIF代码,因为在该示例中使用的某些内容不受支持库ExifInterface的支持。

+0

'使用支持库的ExifInterface':在你的示例中使用'it.sephiroth.android.library.exif2'库。与这个特定情况下的android.media.ExifInterface相比,它有什么优势吗?如果这是由于'ExifInterface'股票的某些实现有缺陷,它是否仍然适用于> = 14的API?或者对于这些更新的API android.media.ExifInterface应该没问题? –

+0

@AlexanderAbakumov:我想写出修改过的位图。这需要删除缩略图并更新EXIF方向标签。 [支持库的'ExifInterface'](https://developer.android.com/reference/android/support/media/ExifInterface.html)不提供'removeCompressedThumbnail()'方法。如果你没有保存修改后的位图,这应该不成问题。 – CommonsWare

+0

@AlexanderAbakumov:在Android 7.0之前,该平台的'ExifInterface' [存在严重的安全漏洞](https://commonsware.com/blog/2016/09/08/dealing-exifinterface-security-flaw.html)。 [使用支持库的'ExifInterface'](https://commonsware.com/blog/2016/12/15/about-support-exifinterface.html)或另一个基于Java的EXIF实现(例如我拥有的代码那个样本)。 – CommonsWare

1

事实证明,解决最初的问题变成了一套新的问题。以下是针对更多读者的完整教程。

首先,作为@CommonsWare pointed in his answer,我们必须使用EXIF方向标签手动处理方向。为了避免有缺陷android.media.ExifInterface车/安全性,以及第三方的依赖,最好的选择似乎是一个新的com.android.support:exifinterface库,并将它们加入build.gradle为:

dependencies { 
    compile 'com.android.support:exifinterface:26.0.0' 
} 

不过,对于我来说,这导致了摇篮同步错误由于该库的这个特定最新版本未在Google存储库中找到,尽管此版本是根据Support Library Packages页面的最新版本。

试验一小时后,我想通了,jcenter()仓库是不够的摇篮同步被加入谷歌Maven仓库固定:

allprojects { 
    repositories { 
     jcenter() 
     maven { 
      url 'https://maven.google.com' 
      // Alternative URL is 'https://dl.google.com/dl/android/maven2/' 
     } 
    } 
} 

好了,现在我们能够使用android.support.media.ExifInterface

下一个令人失望的是,存储在EXIF标签中的宽度和高度也不符合方向,即对于以纵向模式拍摄的图像,您将获得与使用BitmapFactory.decodeFile()创建的Bitmap相同的宽度和高度。因此,唯一的方法就是手动看EXIF ExifInterface.TAG_ORIENTATION标签,如果说,图像旋转90度或270度 - 互换宽度和高度:

ExifInterface exif = new ExifInterface(fileNameFull); 
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); 

BitmapFactory.Options optsForBounds = new BitmapFactory.Options(); 
optsForBounds.inJustDecodeBounds = true; 
BitmapFactory.decodeFile(fileName, optsForBounds); 

int width = optsForBounds.outWidth, height = optsForBounds.outHeight; 
if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270) 
{ 
    int temp = width; 
    //noinspection SuspiciousNameCombination 
    width = height; 
    height = temp; 
} 

我不知道,如果这个方法和代码覆盖100 %的情况下,所以如果你遇到任何问题跨各种设备/图像来源 - 随时发表评论。

一般来说,处理EXIF似乎并不愉快。甚至来自大公司的EXIF标准的实施似乎对细微差别的处理方式有很大不同。请参阅广泛分析here