Manipulate an image without deleting its EXIF data

一个人想着一个人 提交于 2020-01-09 09:09:42

问题


Using imageIO, I usually have the problem of transforming an image file, and after overwriting it, it loses all of its EXIF data. Is there any way to preserve it without first extracting it, caching it, and then resetting it?


回答1:


ImageIO do have this functionality itself, but instead of ImageIO.read you will need to use ImageReader:

ImageReader reader = ImageIO.getImageReadersBySuffix("jpg").next();

(you may want to also check if such reader exists). Then you need to set the input:

reader.setInput(ImageIO.createImageInputStream(your_imput_stream));

Now you may save your metadata:

IIOMetadata metadata = reader.getImageMetadata(0); 
                            // As far as I understand you should provide 
                            // index as tiff images could have multiple pages

And then read the image:

BufferedImage bi = reader.read(0);

When you want to save new image, you should use ImageWriter:

// I'm writing to byte array in memory, but you may use any other stream
ByteArrayOutputStream os = new ByteArrayOutputStream(255);
ImageOutputStream ios = ImageIO.createImageOutputStream(os);

Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = iter.next();
writer.setOutput(ios);

//You may want also to alter jpeg quality
ImageWriteParam iwParam = writer.getDefaultWriteParam();
iwParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwParam.setCompressionQuality(.95f);

//Note: we're using metadata we've already saved.
writer.write(null, new IIOImage(bi, null, metadata), iwParam);
writer.dispose();

ImageIO.write(bi, "jpg", ios);

As it's old topic, I guess this answer is a bit too late, but may help others as this topic is still googlable.




回答2:


Here is my solution using a combination of ImageIO, Imgscalr and Apache commons-imaging. It's a pity there's no single library which combines reading the image with its metadata, making this probably rather excessive on memory usage; Improvements welcome.

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
import org.apache.commons.io.IOUtils;
import org.imgscalr.Scalr;


public class ImageData {

    private byte[] imageData;


    public ImageData(InputStream instream) throws IOException {
        imageData  = IOUtils.toByteArray(instream);
        instream.close();
    }


    public synchronized void resize(int maxDimension) throws IOException, ImageReadException, ImageWriteException {
        // Resize the image if necessary
        BufferedImage image = readImage(imageData);
        if (image.getWidth() > maxDimension || image.getHeight() > maxDimension) {

            // Save existing metadata, if any
            TiffImageMetadata metadata = readExifMetadata(imageData);
            imageData = null; // allow immediate GC

            // resize 
            image = Scalr.resize(image, maxDimension);

            // rewrite resized image as byte[]
            byte[] resizedData = writeJPEG(image);
            image = null; // allow immediate GC 

            // Re-code resizedData + metadata to imageData 
            if (metadata != null) {
                this.imageData = writeExifMetadata(metadata, resizedData);
            } else {
                this.imageData = resizedData;
            }
        }
    }

    private TiffImageMetadata readExifMetadata(byte[] jpegData) throws ImageReadException, IOException {
        IImageMetadata imageMetadata = Imaging.getMetadata(jpegData);
        if (imageMetadata == null) {
            return null;
        }
        JpegImageMetadata jpegMetadata = (JpegImageMetadata)imageMetadata;
        TiffImageMetadata exif = jpegMetadata.getExif();
        if (exif == null) {
            return null;
        }
        return exif;
    }


    private byte[] writeExifMetadata(TiffImageMetadata metadata, byte[] jpegData) 
                                throws ImageReadException, ImageWriteException, IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        new ExifRewriter().updateExifMetadataLossless(jpegData, out, metadata.getOutputSet());
        out.close();
        return out.toByteArray();
    }


    private BufferedImage readImage(byte[] data) throws IOException {
        return ImageIO.read(new ByteArrayInputStream(data));
    }

    private byte[] writeJPEG(BufferedImage image) throws IOException {
        ByteArrayOutputStream jpegOut = new ByteArrayOutputStream();
        ImageIO.write(image, "JPEG", jpegOut);
        jpegOut.close();
        return jpegOut.toByteArray();
    }

    public synchronized void writeJPEG(OutputStream outstream) throws IOException {
        IOUtils.write(imageData,  outstream);

    }

    public synchronized byte[] getJPEGData() {
        return imageData;
    }

}


来源:https://stackoverflow.com/questions/8972357/manipulate-an-image-without-deleting-its-exif-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!