57

如果您的 Android 应用程序使用设备相机拍摄照片然后调整其大小(这对于减小上传大小非常非常常见),您可能没有意识到此调整大小操作会剥离Exif 元数据。

这可能会导致问题,特别是如果相关设备依赖“方向”标签来正确垂直显示图像。

不同的 Android 设备以不同的方式处理相机/图像旋转 - 我值得信赖的旧 Nexus One 似乎总是在捕获后立即旋转图像,因此文件的原生内容在查看时始终是“直立的”。

但是,其他设备(尤其是我测试中的三星手机)不会旋转图像文件的内容——而是设置 Exif 'Orientation' 标签。每当图像稍后显示时,相关图像代码应检测方向“标签”的存在并适当地旋转图像。但是,如果您对图像进行了任何位图处理并将其保存到新文件中,那么所有的 Exif 数据都会丢失。

除了方向数据之外,您还可能会丢失其他有价值的元数据,例如品牌/型号等。

这让我困惑了几个星期(图像在电话图库中显示时看起来是直立的,但随后以错误的方向到达我的服务器并且没有明显的元数据)。我在这里添加这个自我问题来帮助别人。这篇博文非常有帮助:

Android 在不丢失 EXIF 信息的情况下调整图像大小

4

5 回答 5

51

据我所知,没有任何机制可以自动保存元数据,甚至快照任何存在并批量传输。

相反,您似乎必须明确检查特定属性并使用 ExifInterface 自己将它们复制到新图像文件中。

http://developer.android.com/reference/android/media/ExifInterface.html

所以像:

ExifInterface oldExif = new ExifInterface(oldImagePath);
String exifOrientation = oldExif.getAttribute(ExifInterface.TAG_ORIENTATION);

if (exifOrientation != null) {
   ExifInterface newExif = new ExifInterface(imagePath);
   newExif.setAttribute(ExifInterface.TAG_ORIENTATION, exifOrientation);
   newExif.saveAttributes();
}
于 2012-11-28T01:52:43.747 回答
39

对于懒惰的人,这里有一个可重用的函数:

public static void copyExif(String oldPath, String newPath) throws IOException
{
    ExifInterface oldExif = new ExifInterface(oldPath);

    String[] attributes = new String[]
    {
            ExifInterface.TAG_APERTURE,
            ExifInterface.TAG_DATETIME,
            ExifInterface.TAG_DATETIME_DIGITIZED,
            ExifInterface.TAG_EXPOSURE_TIME,
            ExifInterface.TAG_FLASH,
            ExifInterface.TAG_FOCAL_LENGTH,
            ExifInterface.TAG_GPS_ALTITUDE,
            ExifInterface.TAG_GPS_ALTITUDE_REF,
            ExifInterface.TAG_GPS_DATESTAMP,
            ExifInterface.TAG_GPS_LATITUDE,
            ExifInterface.TAG_GPS_LATITUDE_REF,
            ExifInterface.TAG_GPS_LONGITUDE,
            ExifInterface.TAG_GPS_LONGITUDE_REF,
            ExifInterface.TAG_GPS_PROCESSING_METHOD,
            ExifInterface.TAG_GPS_TIMESTAMP,
            ExifInterface.TAG_IMAGE_LENGTH,
            ExifInterface.TAG_IMAGE_WIDTH,
            ExifInterface.TAG_ISO,
            ExifInterface.TAG_MAKE,
            ExifInterface.TAG_MODEL,
            ExifInterface.TAG_ORIENTATION,
            ExifInterface.TAG_SUBSEC_TIME,
            ExifInterface.TAG_SUBSEC_TIME_DIG,
            ExifInterface.TAG_SUBSEC_TIME_ORIG,
            ExifInterface.TAG_WHITE_BALANCE
    };

    ExifInterface newExif = new ExifInterface(newPath);
    for (int i = 0; i < attributes.length; i++)
    {
        String value = oldExif.getAttribute(attributes[i]);
        if (value != null)
            newExif.setAttribute(attributes[i], value);
    }
    newExif.saveAttributes();
}
于 2015-11-05T19:38:53.460 回答
21

正如其他人所指出的,您必须将 Exif 数据从原始图像复制到最终调整大小的图像。Sanselan Android 库通常最适合这一点。根据 Android 操作系统版本,ExifInterface 有时会损坏 Exifdata。

此外,ExifInterface 还处理有限数量的 Exif 标签——即只处理它“知道”的标签。另一方面,Sanselan 将保留所有 Exiftags 和标记注释。

这是一篇博文,展示了如何使用 Sanselan 复制图像数据:

使用 Sanselan 复制 Exif 元数据

顺便说一句,在 Android 上,我也倾向于旋转图像并删除方向 Exiftag。例如,在装有 Android 4.03 的 Nexus S 上,相机在 Exifmetadata 中设置了一个方向标签,但 webview 忽略了该信息并错误地显示了图像。可悲的是,旋转实际图像数据并删除 Exiforientation 标签是让每个程序正确显示图像的唯一方法。

于 2012-12-09T02:23:29.097 回答
1

已经是 2019 年了,仍然没有比@prom85Mike RepassTheo提出的更好的答案。

2016 年,Android 团队引入了ExifInterface 支持库,如果您希望在 Android 版本之间保持一致的行为,这可能是最佳选择。我最终创建了一个标签子集ExifInterface#EXIF_TAGS源代码),我只是迭代这个子集以从输入文件中提取元数据并将其设置在输出中。如果您需要复制所有标签,我建议您不要这样做!一些标签的值无论如何都需要相应地更新(例如TAG_IMAGE_LENGTHTAG_IMAGE_WIDTH)。就个人而言,我一直在问为什么我们需要首先保留所有元数据数据(它与您在使用的设备和相机之间获得的数据不同)并且我们意识到 GPS 位置和日期/时间数据也需要保留。

于 2019-08-01T15:04:04.070 回答
1

为什么不直接修改 ExifInterface 源并添加您自己的实现以支持批量读/写,这样您就不必逐个标签指定。这是我会做的一个片段。

添加新方法以公开内部属性:

public HashMap<String, ExifAttribute>[] getAllAttributes() {
    return mAttributes;
}

添加新方法来设置所有属性:

public void setAttributes(HashMap<String, ExifAttribute>[] attributes) {
    for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
        mAttributes[i] = attributes[i];
    }
}

然后像这样使用它来保存Exif并保存到另一个文件

// Grab all the original exif attributes from an image file and save to memory or wherever
let attributes = ExifInterface2(sourceImagePath).attributes

// Later on you can just copy those attributes to another image
ExifInterface2(destImagePath)
    .setAttributes(attributes)
    .saveAttributes();
于 2020-06-26T18:19:07.193 回答