如何使用 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 可能会被修改以支持这种结构,但今天不存在了。