背景:我正在为一个信使程序编写一个相机应用程序。我无法随时将捕获的图像保存到永久磁盘。相机必须支持所有方向。我的实现是熟悉的Surfaceview示例。我使用Display类来检测方向并相应地旋转相机。在takePicture jpeg回调中,我从byte []构造一个位图,以解决我遇到的一些宽高比问题:Camera API: Cross device issues
问题描述:在某些设备上,在ROTATION_270处拍摄的构造的位图(设备顺时针旋转90度)是颠倒的。到目前为止,似乎是三星。我只能假设相机可能是通过其他方式焊接或者是那种影响,但这既不是在这里也不是在那里。虽然我可以检查Bitmap是否是横向的但我无法从逻辑上检查它是否是尺寸颠倒所以我需要访问EXIF数据。
Android为此http://developer.android.com/reference/android/media/ExifInterface.html提供了一个解析器,但不幸的是它有一个接受文件的构造函数...我没有,也不想要。直观地说,我可以为一个字节数组编写一个构造函数,但考虑到它们调用本机代码http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2.1_r1/android/media/ExifInterface.java
,这看起来真的很痛苦我的问题有两个部分:
有没有人知道byte []数组是否包含完整的EXIF jpeg标头 数据原样或是通过BitmapFactory.decode的路径(...)/ BitmapFactory.compress(...)以某种方式添加?
如果这个EXIF数据在字节数组中退出,我该如何解析出来 方向信息以可靠的方式?
编辑10/18/12
下面的 pcans'回答涉及我的问题的第2部分。正如我在下面的评论中指出的那样,如果你想使用该解析器,你必须将源代码合并到你的项目中。已关联的SO帖子中提到的更改已在此处发布并重新发布:https://github.com/strangecargo/metadata-extractor
注意metadata-extractor的较新版本可直接在Android上运行而无需修改,可通过Maven获取。
然而,至于第1部分,当我使用从takePicture获得的字节数组运行时,我从解析器返回0个标签。我开始担心字节数组没有我需要的数据。我将继续研究这一点,但欢迎任何进一步的见解。
答案 0 :(得分:32)
使用Camera.takePicture()
metadata extraction library in Java的Drew Noakes 版本2.9.1 ,从图像byte[]
(对here有用)中读取元数据/ EXIF:
try
{
// Extract metadata.
Metadata metadata = ImageMetadataReader.readMetadata(new BufferedInputStream(new ByteArrayInputStream(imageData)), imageData.length);
// Log each directory.
for(Directory directory : metadata.getDirectories())
{
Log.d("LOG", "Directory: " + directory.getName());
// Log all errors.
for(String error : directory.getErrors())
{
Log.d("LOG", "> error: " + error);
}
// Log all tags.
for(Tag tag : directory.getTags())
{
Log.d("LOG", "> tag: " + tag.getTagName() + " = " + tag.getDescription());
}
}
}
catch(Exception e)
{
// TODO: handle exception
}
要阅读图片的EXIF 方向(而不是缩略图的方向):
try
{
// Get the EXIF orientation.
final ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
if(exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION))
{
final int exifOrientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
/* Work on exifOrientation */
}
else
{
/* Not found */
}
}
catch(Exception e)
{
// TODO: handle exception
}
方向是1到8.请参阅here,here,here或{{3}}。
根据EXIF方向转换位图:
try
{
final Matrix bitmapMatrix = new Matrix();
switch(exifOrientation)
{
case 1: break; // top left
case 2: bitmapMatrix.postScale(-1, 1); break; // top right
case 3: bitmapMatrix.postRotate(180); break; // bottom right
case 4: bitmapMatrix.postRotate(180); bitmapMatrix.postScale(-1, 1); break; // bottom left
case 5: bitmapMatrix.postRotate(90); bitmapMatrix.postScale(-1, 1); break; // left top
case 6: bitmapMatrix.postRotate(90); break; // right top
case 7: bitmapMatrix.postRotate(270); bitmapMatrix.postScale(-1, 1); break; // right bottom
case 8: bitmapMatrix.postRotate(270); break; // left bottom
default: break; // Unknown
}
// Create new bitmap.
final Bitmap transformedBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, imageBitmap.getWidth(), imageBitmap.getHeight(), bitmapMatrix, false);
}
catch(Exception e)
{
// TODO: handle exception
}
答案 1 :(得分:18)
坏消息:
Android Api遗憾地不允许您仅使用Stream
从File
读取exif数据。
ExifInterface没有InputStream
的构造函数。
所以你必须自己解析jpeg内容。
好消息:
纯Java中存在API。你可以使用这个:https://drewnoakes.com/code/exif/
它是Open Source,在Apache License 2下发布,可以Maven package的形式发布。
有一个InputStream
的构造函数:
public ExifReader(java.io.InputStream is)
您可以使用这样的InputStream
构建byte[]
支持的ByteArrayInputStream
:
InputStream is = new ByteArrayInputStream(decodedBytes);
答案 2 :(得分:4)
因此,使用我的编辑和数据库建议,我得到了图像数据,但它不是我所期望的。具体而言,并非所有设备都会提供方向。如果您按照此路径注意
我指出的“Android修复”ExifReader库实际上是 编辑2.3.1这是一些旧版本。关于的新例子 网站和源代码属于他最新的2.6.x 显着改变API。使用2.3.1界面,您可以 通过执行以下操作从byte []转储所有EXIF数据:
Metadata header;
try {
ByteArrayInputStream bais= new ByteArrayInputStream(data);
ExifReader reader = new ExifReader(bais);
header = reader.extract();
Iterator<Directory> iter = header.getDirectoryIterator();
while(iter.hasNext()){
Directory d = iter.next();
Iterator<Tag> iterTag = d.getTagIterator();
while(iterTag.hasNext()){
Tag t = iterTag.next();
Log.e("DEBUG", "TAG: " + t.getTagName() + " : " + t.getDescription());
}
}
} catch (JpegProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MetadataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
如果您想要数字标签值,只需替换
t.getDescription()
与
d.getInt(t.getTagType())
就答案而言,我真的没有多少补充,所以我接受了粉丝的回答。
答案 3 :(得分:3)
如果您使用的是Glide库,则可以从InputStream获取Exif方向:
InputStream is=getActivity().getContentResolver().openInputStream(originalUri);
int orientation=new ImageHeaderParser(is).getOrientation();
答案 4 :(得分:3)
AndroidX ExifInterface支持从输入流中读取EXIF信息:
implementation "androidx.exifinterface:exifinterface:1.1.0"
然后您可以像这样将输入流传递给构造函数:
val exif = ExifInterface(inputStream)
val orientation =
exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
答案 5 :(得分:2)
如果您想要一种方法来读取不依赖于URI来自您的EXIF数据,您可以使用exif support library并从流中读取它。例如,这就是我如何获得图像的方向。
的build.gradle
dependencies {
...
compile "com.android.support:exifinterface:25.0.1"
...
}
示例代码:
import android.support.media.ExifInterface;
...
try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) {
ExifInterface exif = new ExifInterface(inputStream);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
} catch (IOException e) {
e.printStackTrace();
}
一旦我们开始瞄准api 25(也许24+上也有问题)但仍支持回到api 19,我必须这样做,因为在android 7我们的应用程序会崩溃,如果我传入的URI是只是引用一个文件。因此,我必须创建一个URI来传递给相机意图。
FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", tempFile);
问题在于该文件无法将URI转换为真正的文件路径(除了保留临时文件路径)。
答案 6 :(得分:0)
对于任何可能感兴趣的人,以下是如何使用https://github.com/strangecargo/metadata-extractor
中的2.3.1界面获取Orientation标记Metadata header;
try {
ByteArrayInputStream bais= new ByteArrayInputStream(data);
ExifReader reader = new ExifReader(bais);
header = reader.extract();
Directory dir = header.getDirectory(ExifDirectory.class);
if (dir.containsTag(ExifDirectory.TAG_ORIENTATION)) {
Log.v(TAG, "tag_orientation exists: " + dir.getInt(ExifDirectory.TAG_ORIENTATION));
}
else {
Log.v(TAG, "tag_orietation doesn't exist");
}
} catch (JpegProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MetadataException e) {
e.printStackTrace();
}
答案 7 :(得分:0)
如果你有content://
类型的Uri
,android会通过ContentResolver
提供API,而且不需要使用外部库:
public static int getExifAngle(Context context, Uri uri) {
int angle = 0;
Cursor c = context.getContentResolver().query(uri,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
null,
null,
null);
if (c != null && c.moveToFirst()) {
int col = c.getColumnIndex( MediaStore.Images.ImageColumns.ORIENTATION );
angle = c.getInt(col);
c.close();
}
return angle;
}
您还可以阅读MediaStore.Images.ImageColumns
中找到的任何其他值,例如纬度和经度。
这目前不适用于file:///
Uris,但可以轻松调整。