2012-10-17 44 views
31

背景:我写了一个信使程序的相机应用。我无法随时将捕获的图像保存到永久磁盘。相机必须支持所有的方向。我的实现是熟悉的Surfaceview示例。我使用Display类来检测方向并相应地旋转相机。在takePicture JPEG回调,我构建从字节[]的位图,以便获得周围我在某些方面比的问题:Camera API: Cross device issues从图片回调阅读安卓JPEG EXIF元数据

问题描述:在某些设备上,在ROTATION_270采取的构造位图(装置旋转90度数顺时针)倒过来。到目前为止,它似乎是三星。我只能假设,也许摄像机是焊接在另一方面或这样的影响,但这不是在这里或那里。虽然我可以检查位图是否横向,但我无法从逻辑上检查它是否由维度颠倒,因此我需要访问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

那么我的问题有两个部分:

  1. 有谁知道,如果byte []数组包含全EXIF JPEG头 数据作为或穿过BitmapFactory.decode(...)的路径/ BitmapFactory.compress(...)加入该不知?

  2. 如果EXIF数据在字节数组中存在,我怎样才能以可靠的方式解析出 方向信息?下面

编辑12年10月18日

pcans'答案涉及我的问题的第2部分。正如我在他的回答下面的评论中所指出的那样,如果你想使用该解析器,你必须将源代码合并到你的项目中。在链接SO帖子中提到的改变已经作出这里转贴: https://github.com/strangecargo/metadata-extractor

注意新的直接metadata-extractor工作在Android版本不加修饰,并通过Maven里可以得到。

但是,至于第1部分,当我使用从takePicture获得的字节数组运行它时,我从解析器中获取了0个标记。我担心字节数组没有我需要的数据。我会继续研究这个,但欢迎任何进一步的见解。

+0

我已经提供了一个Android的Java元数据库,可以为JPEG,TIFF,PNG,GIF等图像提取和插入元数据(Exif,XMP,IPTC,ICC_Profile,Photoshop IRB等)。 https://github.com/dragon66/pixymeta-android) – dragon66

+0

关于[读写EXIF]的文章(http://onetouchcode.com/2016/08/23/write-exif-data-image-android/) Android – Shailendra

+0

中的图像文件该元数据提取链接需要一个.com:https://drewnoakes.com/code/exif/ – user4851

回答

18

坏消息:

的Android阿比可悲的是不会让你从Stream仅从File读取EXIF数据。
ExifInterface没有与InputStream构造。 所以你必须自己解析jpeg内容。

好消息:

API存在于纯Java中。您可以使用这一个:https://drewnoakes.com/code/exif/
这是Open Source,在Apache许可证2下发布和可用作为Maven package

有与InputStream构造: public ExifReader(java.io.InputStream is)

您可以使用ByteArrayInputStream像这样建立在你byte[]支持的InputStream

InputStream is = new ByteArrayInputStream(decodedBytes); 
+0

太棒了。我在Apache下找不到一个。现在我只希望字节数组具有我想要的信息!我现在就来试试吧 –

+0

也许愚蠢的问题,但会有任何理由,这将无法在Android上工作?即使我已经经历了将jar放入libs文件夹并将其添加到构建路径的常见过程,我仍然无法检测到该库... –

+0

对不起,这是一个愚蠢的问题 - 我应该搜索更彻底http://stackoverflow.com/questions/2536194/android-image-exif-reader-3rd-party-api –

4

所以用我的编辑和pcans建议,我图像数据,但它不是我所期望的。具体而言,并非所有设备都会给出定位。如果按照这个路径注意

  • 的“Android的固定” ExifReader库我点实际上是 编辑2.3.1这是几releasees老。 网站和源代码中的新示例与最新的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()) 
  • 虽然ExifReader有一个共同nstruter使用byte [],我必须有 误解了它的期望,因为如果我尝试直接使用 数据数组,它会返回到返回目录中的标签。

我真的没有添加太多的答案,所以我接受pcans的答案。图像的

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 取向

32

要使用版本2.9.1metadata extraction library in Java通过Drew Noakes的读取图像byte[](对于Camera.takePicture()有用)的元数据/ 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,herehere


要变换基于其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

可以在git存储库中找到更新的版本:https:// github。com/drewfarris/metadata-extractor – dwbrito

+1

@Pang这是主摄像头,前摄像头旋转怎么办? –

+4

@dwbrito,实际上原来的项目作者(我)已经将项目移至GitHub。你链接的那个现在已经有几年了,并且缺少许多修复/改进。 https://github.com/drewnoakes/metadata-extractor/ –

0

对于任何人谁可能有兴趣,这里是如何使用2.3获得方向标签。从https://github.com/strangecargo/metadata-extractor

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(); 
} 
3

1个接口如果您正在使用滑翔库中,可以从InputStream获取的Exif方向:

InputStream is=getActivity().getContentResolver().openInputStream(originalUri); 
int orientation=new ImageHeaderParser(is).getOrientation(); 
+1

此方法对我来说不适用于Camera.PictureCallback.onPictureTaken中返回的字节数组,当通过mCamera .takePicture(null,null,mPictureCallback)。在这种情况下,方向为-1表示失败。 – Andrew

0

如果你有content://Uri,机器人通过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,但可以很容易地调整。

+0

至少在某些设备上不起作用。为我返回'col = -1'。 –

+0

@AleksN只有在图片添加了正确的标签到MediaStore时才有效。此外,它必须是一个内容uri而不是文件uri。无论如何,我已经改变了我的想法,我建议使用Exif阅读器,并留下内容解析器查询。这适用于文件和内容uris。 – natario

+0

请参阅http://stackoverflow.com/q/34696787/4288782 – natario

1

如果您想要读取不依赖于您的URI来自您的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转换为真实的文件路径(而不是保留在临时文件路径中)。