如何使用 IIOMetadata 在 jpeg 图像中查找 GPS 元数据?
How do I use IIOMetadata to find the GPS metadata in a jpeg image?
我正在尝试在图像文件中查找 GPS 元数据。我知道它在那里,因为当我在取景器中选择获取信息时我可以看到它。但是当我解析所有元数据时,我看不到它。下面是我用来解析数据的两种方法:
private static void readMetaData(final File filePath, final String ext) throws IOException {
try (ImageInputStream inputStream = ImageIO.createImageInputStream(filePath)) {
final Iterator<ImageReader> imageReadersByFormatName = ImageIO.getImageReadersByFormatName(ext);
int rdrCount = 0;
while (imageReadersByFormatName.hasNext()) {
System.out.printf("Using reader %d%n", rdrCount++);
ImageReader imageReader = imageReadersByFormatName.next();
imageReader.setInput(inputStream, true, false);
// read metadata of first image
IIOMetadata metadata = imageReader.getImageMetadata(0);
System.out.printf("Metadata of %s%n", metadata.getClass());
String[] formatNames = metadata.getMetadataFormatNames();
System.out.printf("Formats: %d%n", formatNames.length);
int fIndex = 0;
for (String formatName : formatNames) {
System.out.printf("Format %d of %d %s:%n", ++fIndex, formatNames.length, formatName);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(formatName);
printChildren(root, "");
}
String[] extraFormats = metadata.getExtraMetadataFormatNames();
if (extraFormats != null) {
int index = 0;
for (String extraFormat : extraFormats) {
System.out.printf("Extra format %s (%d of %d)%n",
extraFormat, index++, extraFormats.length);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(extraFormat);
printChildren(root, "");
}
}
}
}
}
private static void printChildren(IIOMetadataNode node, String indent) {
String nodeName = node.getNodeName();
indent += '/' + nodeName;
final NodeList childNodes = node.getChildNodes();
int childCount = childNodes.getLength();
for (int n = 0; n < childCount; ++n) {
IIOMetadataNode child = (IIOMetadataNode) childNodes.item(n);
String childName = child.getNodeName();
// child.getNodeValue() and child.getPrefix() always return null
NamedNodeMap attributes = child.getAttributes();
int aLength = attributes.getLength();
for (int a = 0; a < aLength; ++a) {
Node item = attributes.item(a);
final String itemName = item.getNodeName();
System.out.printf(" [%s/%s] %s = %s%n",
indent, childName, itemName, child.getAttribute(itemName));
}
printChildren(child, indent);
}
}
我知道有一些库可以为我解析元数据,但这是理解元数据的更大努力的一部分,我试过的库对我隐藏了细节。
这是我得到的元数据:
Using reader 0
Metadata of class com.sun.imageio.plugins.jpeg.JPEGMetadata
Formats: 2
Format 1 of 2 javax_imageio_jpeg_image_1.0:
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] majorVersion = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] minorVersion = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] resUnits = 0
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] Xdensity = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] Ydensity = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] thumbWidth = 0
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] thumbHeight = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/unknown] MarkerTag = 225
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] elementPrecision = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] qtableId = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] elementPrecision = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] qtableId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] process = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] samplePrecision = 8
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] numLines = 2592
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] samplesPerLine = 1944
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] numFrameComponents = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] componentId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] HsamplingFactor = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] VsamplingFactor = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] QtableSelector = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] componentId = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] HsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] VsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] QtableSelector = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] componentId = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] HsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] VsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] QtableSelector = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] numScanComponents = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] startSpectralSelection = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] endSpectralSelection = 63
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] approxHigh = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] approxLow = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] componentSelector = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] dcHuffTable = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] acHuffTable = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] componentSelector = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] dcHuffTable = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] acHuffTable = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] componentSelector = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] dcHuffTable = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] acHuffTable = 1
Format 2 of 2 javax_imageio_1.0:
[/javax_imageio_1.0/Chroma/ColorSpaceType] name = YCbCr
[/javax_imageio_1.0/Chroma/NumChannels] value = 3
[/javax_imageio_1.0/Compression/CompressionTypeName] value = JPEG
[/javax_imageio_1.0/Compression/Lossless] value = FALSE
[/javax_imageio_1.0/Compression/NumProgressiveScans] value = 1
[/javax_imageio_1.0/Dimension/PixelAspectRatio] value = 1.0
[/javax_imageio_1.0/Dimension/ImageOrientation] value = normal
所以,在 josejuan 和 HaraldK 的帮助下,我了解到 Exif 数据存在于我找到的节点中,它只是这样显示的:
[javax_imageio_jpeg_image_1.0/markerSequence/unknown] MarkerTag = 225
如果我将此节点转换为 IIOMetadata 节点,我可以调用 getUserObject()
方法,该方法 returns 一个 9444 字节的字节数组,其中包含大量数据,包括 GPS 数据。但是 IIOMetadata API 没有给我解析所有这些数据的方法。这就是为什么我需要一个外部图书馆。正如 josejuan 指出的那样,另一种方法是编写我自己的 IIOMetadata 实现。有一天,JPEGMetadata class 可能会被修改以支持这种结构,但今天不存在了。
我正在尝试在图像文件中查找 GPS 元数据。我知道它在那里,因为当我在取景器中选择获取信息时我可以看到它。但是当我解析所有元数据时,我看不到它。下面是我用来解析数据的两种方法:
private static void readMetaData(final File filePath, final String ext) throws IOException {
try (ImageInputStream inputStream = ImageIO.createImageInputStream(filePath)) {
final Iterator<ImageReader> imageReadersByFormatName = ImageIO.getImageReadersByFormatName(ext);
int rdrCount = 0;
while (imageReadersByFormatName.hasNext()) {
System.out.printf("Using reader %d%n", rdrCount++);
ImageReader imageReader = imageReadersByFormatName.next();
imageReader.setInput(inputStream, true, false);
// read metadata of first image
IIOMetadata metadata = imageReader.getImageMetadata(0);
System.out.printf("Metadata of %s%n", metadata.getClass());
String[] formatNames = metadata.getMetadataFormatNames();
System.out.printf("Formats: %d%n", formatNames.length);
int fIndex = 0;
for (String formatName : formatNames) {
System.out.printf("Format %d of %d %s:%n", ++fIndex, formatNames.length, formatName);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(formatName);
printChildren(root, "");
}
String[] extraFormats = metadata.getExtraMetadataFormatNames();
if (extraFormats != null) {
int index = 0;
for (String extraFormat : extraFormats) {
System.out.printf("Extra format %s (%d of %d)%n",
extraFormat, index++, extraFormats.length);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(extraFormat);
printChildren(root, "");
}
}
}
}
}
private static void printChildren(IIOMetadataNode node, String indent) {
String nodeName = node.getNodeName();
indent += '/' + nodeName;
final NodeList childNodes = node.getChildNodes();
int childCount = childNodes.getLength();
for (int n = 0; n < childCount; ++n) {
IIOMetadataNode child = (IIOMetadataNode) childNodes.item(n);
String childName = child.getNodeName();
// child.getNodeValue() and child.getPrefix() always return null
NamedNodeMap attributes = child.getAttributes();
int aLength = attributes.getLength();
for (int a = 0; a < aLength; ++a) {
Node item = attributes.item(a);
final String itemName = item.getNodeName();
System.out.printf(" [%s/%s] %s = %s%n",
indent, childName, itemName, child.getAttribute(itemName));
}
printChildren(child, indent);
}
}
我知道有一些库可以为我解析元数据,但这是理解元数据的更大努力的一部分,我试过的库对我隐藏了细节。
这是我得到的元数据:
Using reader 0
Metadata of class com.sun.imageio.plugins.jpeg.JPEGMetadata
Formats: 2
Format 1 of 2 javax_imageio_jpeg_image_1.0:
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] majorVersion = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] minorVersion = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] resUnits = 0
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] Xdensity = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] Ydensity = 1
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] thumbWidth = 0
[/javax_imageio_jpeg_image_1.0/JPEGvariety/app0JFIF] thumbHeight = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/unknown] MarkerTag = 225
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] elementPrecision = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] qtableId = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] elementPrecision = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dqt/dqtable] qtableId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] process = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] samplePrecision = 8
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] numLines = 2592
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] samplesPerLine = 1944
[/javax_imageio_jpeg_image_1.0/markerSequence/sof] numFrameComponents = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] componentId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] HsamplingFactor = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] VsamplingFactor = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] QtableSelector = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] componentId = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] HsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] VsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] QtableSelector = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] componentId = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] HsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] VsamplingFactor = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sof/componentSpec] QtableSelector = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] class = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/dht/dhtable] htableId = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] numScanComponents = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] startSpectralSelection = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] endSpectralSelection = 63
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] approxHigh = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos] approxLow = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] componentSelector = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] dcHuffTable = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] acHuffTable = 0
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] componentSelector = 2
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] dcHuffTable = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] acHuffTable = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] componentSelector = 3
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] dcHuffTable = 1
[/javax_imageio_jpeg_image_1.0/markerSequence/sos/scanComponentSpec] acHuffTable = 1
Format 2 of 2 javax_imageio_1.0:
[/javax_imageio_1.0/Chroma/ColorSpaceType] name = YCbCr
[/javax_imageio_1.0/Chroma/NumChannels] value = 3
[/javax_imageio_1.0/Compression/CompressionTypeName] value = JPEG
[/javax_imageio_1.0/Compression/Lossless] value = FALSE
[/javax_imageio_1.0/Compression/NumProgressiveScans] value = 1
[/javax_imageio_1.0/Dimension/PixelAspectRatio] value = 1.0
[/javax_imageio_1.0/Dimension/ImageOrientation] value = normal
所以,在 josejuan 和 HaraldK 的帮助下,我了解到 Exif 数据存在于我找到的节点中,它只是这样显示的:
[javax_imageio_jpeg_image_1.0/markerSequence/unknown] MarkerTag = 225
如果我将此节点转换为 IIOMetadata 节点,我可以调用 getUserObject()
方法,该方法 returns 一个 9444 字节的字节数组,其中包含大量数据,包括 GPS 数据。但是 IIOMetadata API 没有给我解析所有这些数据的方法。这就是为什么我需要一个外部图书馆。正如 josejuan 指出的那样,另一种方法是编写我自己的 IIOMetadata 实现。有一天,JPEGMetadata class 可能会被修改以支持这种结构,但今天不存在了。