【问题标题】:Android image resizing and preserving EXIF data (orientation, rotation, etc.)Android 图像调整大小和保留 EXIF 数据(方向、旋转等)
【发布时间】:2012-11-15 19:09:18
【问题描述】:

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

这可能会导致问题,尤其是在相关设备依赖“方向”标签来正确垂直显示图像的情况下。

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

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

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

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

Android re-size image without loosing EXIF information

【问题讨论】:

    标签: android image android-camera exif


    【解决方案1】:

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

    相反,您似乎必须明确检查特定属性并使用 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();
    }
    

    【讨论】:

    • 你是我的英雄。
    【解决方案2】:

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

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

    【讨论】:

    • 如果调整大小,记得更新或不要复制TAG_IMAGE_LENGTH和TAG_IMAGE_WIDTH
    【解决方案3】:

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

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

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

    Copying Exif metadata using Sanselan

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

    【讨论】:

    • 谢谢 Theo,我很欣赏这个答案并接受了它。您是否有关于此“根据 Android 操作系统版本,ExifInterface 有时会损坏 EXIF 数据”的更多信息的链接。
    • 这是基于我的经验,我不是第一个发现 ExifInterface 有问题的人 [1]。根据接下来几天我有多少时间,我可能会整理一些说明性案例并进行报告。 [1]mail-archives.apache.org/mod_mbox/commons-issues/201106.mbox/…
    • @Theo 我已经测试了你的代码,它似乎复制了某些设备和图像的大部分/所有属性。无法很好复制的属性示例:ISOSpeedRatings、FocalLength、FNumber。你能解释一下怎么可能吗?我已经在带有 Android 4.4.2 的 nexus 4 设备上对其进行了测试,并比较了在原始文件和新文件上创建的 ExifInterface。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-26
        • 1970-01-01
        • 2013-08-20
        • 1970-01-01
        • 2017-08-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多