从图片回调中读取android jpeg EXIF元数据

And*_*w G 32 java android jpeg exif

背景:我正在为一个信使程序编写一个相机应用程序.我无法随时将捕获的图像保存到永久磁盘.相机必须支持所有方向.我的实现是熟悉的Surfaceview示例.我使用Display类来检测方向并相应地旋转相机.在takePicture jpeg回调中,我从byte []构造一个位图,以解决我遇到的一些宽高比问题:Camera API:跨设备问题

问题描述:在某些设备上,在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 /安卓/媒体/ ExifInterface.java

我的问题有两个部分:

  1. 有没有人知道byte []数组是否包含完整的EXIF jpeg头数据,或者是通过BitmapFactory.decode(...)/ BitmapFactory.compress(...)的路径以某种方式添加?

  2. 如果此EXIF数据在字节数组中退出,我如何以可靠的方式解析方向信息?

编辑10/18/12

下面的pcans回答涉及我的问题的第2部分.正如我在下面的评论中指出的那样,如果你想使用该解析器,你必须将源代码合并到你的项目中.链接SO帖子中提到的更改已经在此处进行了转发:https://github.com/strangecargo/metadata-extractor

注意元数据提取器的更新版本可直接在Android上运行而无需修改,可通过Maven获得.

但是,至于第1部分,当我使用从takePicture获得的字节数组运行时,我从解析器返回0个标签.我开始担心字节数组没有我需要的数据.我将继续研究这一点,但欢迎任何进一步的见解.

Pan*_*ang 32

读取的元数据/从EXIF图像byte[](有用Camera.takePicture()),使用2.9.1版本的的在Java中提取元数据库德鲁诺克斯:

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
}
Run Code Online (Sandbox Code Playgroud)

要读取图像的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
}
Run Code Online (Sandbox Code Playgroud)

方向是1到8.请参见此处,此处,此处此处.


要根据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
}
Run Code Online (Sandbox Code Playgroud)

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

pca*_*ans 17

坏消息:

Android Api遗憾地不允许你从a中读取exif数据Stream,只能从a中读取File.
ExifInterface没有带构造函数InputStream.所以你必须自己解析jpeg内容.

好消息:

为此,API存在于纯Java中.你可以使用这个:https://drewnoakes.com/code/exif/
它是开源的,在Apache License 2下发布,可以作为Maven包使用.

有一个构造函数InputStream: public ExifReader(java.io.InputStream is)

你可以建立一个InputStream由您支持byte[]使用ByteArrayInputStream这样的:

InputStream is = new ByteArrayInputStream(decodedBytes);
Run Code Online (Sandbox Code Playgroud)

  • Android API 24`ExifInterface`增加了对`InputStream`的支持.资料来源:https://developer.android.com/reference/android/media/ExifInterface.html#ExifInterface(java.io.InputStream) (3认同)
  • 链接到库已死 (2认同)

teh*_*nsi 5

AndroidX ExifInterface 支持从输入流中读取 EXIF 信息:

implementation "androidx.exifinterface:exifinterface:1.1.0"
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样将输入流传递给构造函数:

val exif = ExifInterface(inputStream)
val orientation =
        exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
Run Code Online (Sandbox Code Playgroud)