如果您的Android应用使用设备相机拍照并调整大小(这非常非常常见,以减少上传的大小),您可能没有意识到此调整大小操作剥离 exif元数据。
这可能会导致问题,尤其是当相关设备依赖“方向”标签正确显示图像时。
不同的Android设备以不同的方式处理相机/图像旋转 - 我信赖的'Nexus One似乎总是在捕获后立即旋转图像,因此文件的本机内容在查看时始终是“直立的”。然而,其他设备(特别是我的测试中的三星手机),不旋转图像文件的内容 - 而是设置exif'Orientation'标签。无论何时稍后显示图像,相关图像代码都应检测到方向“标签”的存在并适当地旋转图像。但是如果您对图像进行了任何位图处理并将其保存到新文件中,那么所有exif数据都将丢失。
除了方向数据之外,您还可能会丢失其他有价值的元数据,例如品牌/型号等。
这使我困惑了几个星期(图片在电话库中显示时显示正常,但随后到达我的服务器时方向错误且没有明显的元数据)。我在这里添加这个自我提问来帮助别人。这篇博客文章非常有用:
http://vikaskanani.wordpress.com/2011/07/17/android-re-size-image-without-loosing-exif-information/
答案 0 :(得分:40)
[自我回答]
Afaict,有 no 机制可以自动保存元数据,甚至可以快照任何内容并批量传输。
相反,您似乎必须使用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();
}
答案 1 :(得分:26)
对于懒惰的人来说,这是一个可重复使用的功能:
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();
}
答案 2 :(得分:18)
正如其他人指出的那样,您必须将EXIF数据从原始图像复制到最终调整大小的图像。 Sanselan Android库通常是最好的。根据Android操作系统版本,ExifInterface有时会破坏EXIF数据。此外,ExifInterface还处理有限数量的Exif标记 - 即只有它“知道”的标记。另一方面,Sanselan将保留所有EXIF标签和标记说明。
以下是一篇博文,介绍如何使用Sanselan复制图像数据: http://bricolsoftconsulting.com/copying-exif-metadata-using-sanselan/
顺便说一下,在Android上我也倾向于旋转图像并删除Orientation EXIF标签。例如,在带有Android 4.03的Nexus S上,相机在EXIF元数据中设置了方向标记,但是webview忽略了该信息并错误地显示了图像。遗憾的是,旋转实际图像数据并删除EXIF方向标记是让每个程序正确显示图像的唯一方法。答案 3 :(得分: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();
答案 4 :(得分:0)
已经到2019年了,没有比@prom85,Mike Repass和Theo提出的更好的答案了。
2016年,Android团队引入了ExifInterface Support Library,如果您希望在两个Android版本之间保持一致的行为,这可能是最好的选择。我最终创建了标签ExifInterface#EXIF_TAGS
(source code)的子集,然后仅对该子集进行迭代以从输入文件中提取元数据并将其设置为输出。如果您需要复制所有标签,我建议您不要这样做!无论如何,某些标记的值都需要相应地更新(例如TAG_IMAGE_LENGTH
和TAG_IMAGE_WIDTH
)。就我个人而言,我一直在问一个问题,为什么我们首先需要保留所有的metedata数据(无论如何,设备和相机之间的差异还是存在的),我们意识到gps位置和日期/时间数据也需要保留。