PNG元数据读写 [英] PNG metadata read and write

查看:101
本文介绍了PNG元数据读写的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用了代码将自定义元数据写入PNG图像并进行读取.写入功能似乎可以正常工作,但是当我尝试读取已写入的数据时,它将引发NullPointerException.有人可以告诉我怎么了吗?

I am using a piece of code posted on stackover flow to write custom metadata to PNG image and read it. The write function seems to work fine but when i try to read data that i had written it throws NullPointerException. Can someone tell me what is wrong?

这是用于编写元数据的代码

Here is code for writing metadata

try{
    image=ImageIO.read(new FileInputStream("input.png"));
    writeCustomData(image, "software", "FRDDC");
    ImageIO.write(image, "png", new File("output.png"));
    }
    catch(Exception e){
    e.printStackTrace();
    }

写入元数据的方法

   public static byte[] writeCustomData(BufferedImage buffImg, String key, String value) throws Exception {
    ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();

    ImageWriteParam writeParam = writer.getDefaultWriteParam();
    ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);

    //adding metadata
        javax.imageio.metadata.IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);

    IIOMetadataNode textEntry = new IIOMetadataNode("tEXtEntry");
    textEntry.setAttribute("keyword", key);
    textEntry.setAttribute("value", value);

    IIOMetadataNode text = new IIOMetadataNode("tEXt");
    text.appendChild(textEntry);

    IIOMetadataNode root = new IIOMetadataNode("javax_imageio_png_1.0");
    root.appendChild(text);

    metadata.mergeTree("javax_imageio_png_1.0", root);

    //writing the data
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
        javax.imageio.stream.ImageOutputStream stream = ImageIO.createImageOutputStream(baos);
    writer.setOutput(stream);
    writer.write(metadata, new IIOImage(buffImg, null, metadata), writeParam);

    try {

            ImageIO.write(buffImg, "png", new File("new.png"));
        } catch (Exception e) {
            e.printStackTrace();
        }

    stream.close();

    return baos.toByteArray();
}

读取元数据

try{
image=ImageIO.read(new FileInputStream("output.png"));

            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            ImageIO.write(image, "png", baos );
            byte[] b=baos.toByteArray();
            String out=readCustomData(b, "software");
}
catch(Exception e){
e.printStackTrace();
}

读取元数据的方法

 public static String readCustomData(byte[] imageData, String key) throws IOException{
    ImageReader imageReader = ImageIO.getImageReadersByFormatName("png").next();

    imageReader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(imageData)), true);

    // read metadata of first image
        javax.imageio.metadata.IIOMetadata metadata = imageReader.getImageMetadata(0);

    //this cast helps getting the contents

     //Node n=metadata.getAsTree("javax_imageio_png_1.0");
     //NodeList childNodes=n.getChildNodes();
    PNGMetadata pngmeta = (PNGMetadata) metadata; 
    if(pngmeta.getStandardTextNode()==null){
        System.out.println("not found");
    }
    NodeList childNodes = pngmeta.getStandardTextNode().getChildNodes();

    for (int i = 0; i < childNodes.getLength(); i++) {
        Node node = childNodes.item(i);
        String keyword = node.getAttributes().getNamedItem("keyword").getNodeValue();
        String value = node.getAttributes().getNamedItem("value").getNodeValue();
        if(key.equals(keyword)){
            return value;
        }
    }
    return null;
}

错误消息

not found
java.lang.NullPointerException
    at PNGMeta.readCustomData(PNGMeta.java:104)
    at PNGMeta.main(PNGMeta.java:40)
BUILD SUCCESSFUL (total time: 2 seconds)

推荐答案

这是修改然后读取我所知道的元数据的最有效方法.与OP发布的代码相反,此版本并未完全替换图像中的元数据,而是将新内容与任何现有内容合并.

Here's the most efficient way of modifying and then reading the metadata that I know of. In contrast to the code posted by the OP, this version does not fully replace the metadata in the image, but merges the new content with any existing content.

由于它使用标准"元数据格式,因此它也应适用于ImageIO支持的任何允许任意文本注释的格式(不过,我仅测试了PNG).在这种情况下,写入的实际数据应与本机PNG元数据格式的数据匹配.

As it uses the "standard" metadata format, it should also work for any format supported by ImageIO, that allows arbitrary text comments (I only tested for PNG, though). The actual data written, should match that of the native PNG metadata format in this case.

它使用一种方法读取所有图像像素数据和元数据,以避免过多的流打开/关闭以及查找和内存使用.由于相同的原因,它一次写入所有图像像素数据和元数据.对于无损的单一图像格式(如PNG),此往返操作不应丢失任何质量或元数据.

It reads all image pixel data and metadata using a single method, to avoid excess stream open/close and seeking and memory usage. It writes all image pixel data and metadata at once for the same reason. For lossless, single image formats like PNG, this roundtrip should not lose any quality or metadata.

回读元数据时,仅读取元数据,而忽略像素数据.

When reading metadata back, only the metadata is read, pixel data is ignored.

public class IIOMetadataUpdater {

    public static void main(final String[] args) throws IOException {
        File in = new File(args[0]);
        File out = new File(in.getParent(), createOutputName(in));

        System.out.println("Output path: " + out.getAbsolutePath());

        try (ImageInputStream input = ImageIO.createImageInputStream(in);
             ImageOutputStream output = ImageIO.createImageOutputStream(out)) {

            Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
            ImageReader reader = readers.next(); // TODO: Validate that there are readers

            reader.setInput(input);
            IIOImage image = reader.readAll(0, null);

            addTextEntry(image.getMetadata(), "foo", "bar");

            ImageWriter writer = ImageIO.getImageWriter(reader); // TODO: Validate that there are writers
            writer.setOutput(output);
            writer.write(image);
        }

        try (ImageInputStream input = ImageIO.createImageInputStream(out)) {
            Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
            ImageReader reader = readers.next(); // TODO: Validate that there are readers

            reader.setInput(input);
            String value = getTextEntry(reader.getImageMetadata(0), "foo");

            System.out.println("value: " + value);
        }
    }

    private static String createOutputName(final File file) {
        String name = file.getName();
        int dotIndex = name.lastIndexOf('.');

        String baseName = name.substring(0, dotIndex);
        String extension = name.substring(dotIndex);

        return baseName + "_copy" + extension;
    }

    private static void addTextEntry(final IIOMetadata metadata, final String key, final String value) throws IIOInvalidTreeException {
        IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
        textEntry.setAttribute("keyword", key);
        textEntry.setAttribute("value", value);

        IIOMetadataNode text = new IIOMetadataNode("Text");
        text.appendChild(textEntry);

        IIOMetadataNode root = new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName);
        root.appendChild(text);

        metadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, root);
    }

    private static String getTextEntry(final IIOMetadata metadata, final String key) {
        IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
        NodeList entries = root.getElementsByTagName("TextEntry");

        for (int i = 0; i < entries.getLength(); i++) {
            IIOMetadataNode node = (IIOMetadataNode) entries.item(i);
            if (node.getAttribute("keyword").equals(key)) {
                return node.getAttribute("value");
            }
        }

        return null;
    }
}

上面的代码的预期输出是:

Expected output of the above code is:

Output path: /path/to/yourfile_copy.png
value: bar

这篇关于PNG元数据读写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆