我正在开发Android应用程序,该应用程序应该使用Google Camera的新深度图生成功能.
谷歌基本上描述了这里使用的元数据
我可以访问大多数元数据,但遗憾的是最重要的数据被编码为extendedXmp,我无法获得任何XMP解析库来正确解析它!
我尝试过Commons-Imaging,元数据提取器以及最近的Adobes XMPCore
XMPCore可能能够处理扩展版本,但没有文档如何让它来解析JPG文件中的数据,它假设原始XMP数据要传递
是否有正确的XMP解析实现,包括JPG文件的扩展部分,或者我只是做错了什么?
这是我的尝试:
使用Commons-Imaging:
try {
String imageParser = new JpegImageParser().getXmpXml(new ByteSourceInputStream(imageStream, "img.jpg"), new HashMap<String, Object>());
Log.v(TAG, imageParser);
} catch (ImageReadException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
使用元数据提取器
Metadata metadata = ImageMetadataReader.readMetadata(
new BufferedInputStream(imageStream), false);
XmpDirectory xmp = metadata
.getDirectory(XmpDirectory.class);
XMPMeta xmpMeta = xmp.getXMPMeta();
String uri = "http://ns.google.com/photos/1.0/depthmap/";
Log.v(TAG, xmpMeta.doesPropertyExist(uri, "GDepth:Format") + " " );
try {
XMPProperty hasExtendedXMP = xmpMeta.getProperty("http://ns.adobe.com/xmp/note/", "xmpNote:HasExtendedXMP");
Log.v(TAG, hasExtendedXMP.getValue().toString() + " " + new String(Base64.decode(hasExtendedXMP.getValue().toString(), Base64.DEFAULT)));
} catch (XMPException e) {
e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
最初,Adobe并未预期XMP数据长度将超过一个JPEG段(约64K)的限制,并且他们的XMP规范声明XMP数据必须合二为一.后来当他们发现单个JPEG APP1段不足以容纳XMP数据时,他们改变了他们的规范,以允许整个XMP数据的多个APP1段.数据分为两部分:标准XMP和ExtendedXMP.标准XMP部件是带有包装器的"普通"XMP结构,而ExtendedXMP部件没有包装器.ExtendedXMP数据可以进一步划分为多个APP1.
以下引用来自Adobe XMP规范第3部分,适用于ExtendedXMP块作为JPEG APP1:
每个块在单独的APP1标记段内写入JPEG文件.每个ExtendedXMP标记段包含:
- 以空值终止的签名字符串" http://ns.adobe.com/xmp/extension/ ".
- 128位GUID存储为32字节ASCII十六进制字符串,资本AF,无空终止.GUID是完整ExtendedXMP序列化的128位MD5摘要.
- ExtendedXMP序列化的全长,为32位无符号整数
- 该部分的偏移量为32位无符号整数.
- ExtendedXMP的一部分
我们可以看到除了以null结尾的字符串作为ExtendedXMP数据的id之外,还有一个GUID应该与在标准XMP部分中找到的值相同.偏移量用于连接ExtendedXMP的不同部分 - 因此ExtendedXMP APP1的顺序可能甚至不是有序的.然后是实际的数据部分,这就是为什么@Matt的答案需要一些方法来修复字符串.还有另一个值 - ExtendedXMP序列化的全长,它有两个用途:检查数据的完整性以及提供用于连接数据的缓冲区大小.
当我们找到一个ExtendedXMP段时,我们需要将当前数据与其他ExtendedXMP段连接起来,最后得到整个ExtendedXMP数据.然后,我们将两个XML树连接在一起(同样从标准XMP部件中删除GUID)以检索整个XMP数据.
我在Java中创建了一个库icafe,它可以提取和插入XMP以及ExtendedXMP.ExtendedXMP的一个用例是Google的深度图数据,实际上是作为元数据隐藏在实际图像中的灰度图像,而在JPEG的情况下,作为XMP数据.深度图图像可以用于例如模糊原始图像.深度图数据通常很大,必须分成标准和扩展的XMP部分.整个数据是Base64编码的,可以是PNG格式.
以下是示例图像和提取的深度图:

原始图像来自这里.
注意:最近我发现另一个网站谈论Google Cardboard Camera应用程序,它可以利用JPEG XMP数据中嵌入的图像和音频.ICAFE现在支持从这些图像中提取图像和音频.可以通过以下调用在此处找到示例用法JPEGTweaker.extractDepthMap()
我已经能够使用元数据提取器库和通过 XMP 属性的迭代器读取也存储在 XMP 中的 Picasa 脸部数据:
try {
Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
XmpDirectory xmpDirectory = metadata.getDirectory(XmpDirectory.class);
XMPMeta xmpMeta = xmpDirectory.getXMPMeta();
XMPIterator itr = xmpMeta.iterator();
while (itr.hasNext()) {
XMPPropertyInfo pi = (XMPPropertyInfo) itr.next();
if (pi != null && pi.getPath() != null) {
if ((pi.getPath().endsWith("stArea:w")) || (pi.getPath().endsWith("mwg-rs:Name")) || (pi.getPath().endsWith("stArea:h")))
System.out.println(pi.getValue().toString());
}
}
} catch (final NullPointerException npe) {
// ignore
}
Run Code Online (Sandbox Code Playgroud)