在我的测试中,由Bitmap
创建的BitmapFactory.decodeFile()
不尊重EXIF标题。
例如,对于不根据相机方向旋转实际像素数据的设备拍摄的肖像图像,而是将其存储在EXIF标题中,当我调用Bitmap.getWidth()
和Bitmap.getHeight()
时,它们会返回不正确的值(宽度的高度,反之亦然) )。
有没有办法让BitmapFactory.decodeFile()
尊重EXIF并产生正确的Bitmap
?
如果没有,处理此问题的推荐模式是什么?
如果没有经验丰富的Android开发者的建议,我看到的唯一方法是预处理拍摄的图像(加载,根据EXIF旋转,然后保存)。但是除了巨大的处理开销之外,这可能会导致OutOfMemoryException
s用于大型摄像机分辨率(在使用BitmapFactory.Options.inSampleSize
加载缩小图像时无法降低质量的情况下)。
有没有办法使BitmapFactory.decodeFile()尊重EXIF并生成正确的位图?
不,对不起
处理此问题的推荐模式是什么?
使用支持库的ExifInterface
确定所需的方向。然后,根据您对Bitmap
的使用,旋转视图(例如,ImageView
)或旋转Bitmap
。 This sample project说明了这两种方法,虽然我使用了一组单独的EXIF代码,因为支持库的ExifInterface
不支持我在该示例中使用的一些东西。
事实证明,解决最初的问题变成了一组新问题。这是针对更多读者的完整教程。
首先,作为@CommonsWare pointed in his answer,我们必须使用EXIF方向标记手动处理方向。为了避免错误/安全性有缺陷的android.media.ExifInterface
以及第三方依赖,最好的选择似乎是一个新的com.android.support:exifinterface
库,我们将其添加到build.gradle
:
dependencies {
compile 'com.android.support:exifinterface:26.0.0'
}
但对我来说,它导致Gradle同步错误,因为该库的特定最新版本在Google存储库中找不到,但根据Support Library Packages页面,此版本是最新版本。
经过一个小时的试验,我发现jcenter()
存储库还不够,并且通过添加Google Maven存储库修复了Gradle同步:
allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
// Alternative URL is 'https://dl.google.com/dl/android/maven2/'
}
}
}
好的,现在我们可以使用android.support.media.ExifInterface
了。
下一个令人失望的是,EXIF标签中存储的宽度和高度也不考虑方向,即对于以纵向模式拍摄的图像,您获得的宽度和高度与使用Bitmap
创建的BitmapFactory.decodeFile()
返回的宽度和高度相同。所以唯一的方法是手动查看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。
你现在可以用Glide做到这一点。请参阅此处的“背景线程”部分:
https://bumptech.github.io/glide/doc/getting-started.html
Bitmap bitmap = Glide.with(context).asBitmap()。load(new File(fileName))。skipMemoryCache(true).submit()。get();
Glide考虑了EXIF。您需要在后台线程上加载它。我使用的是Glide 4.9.0