2012-01-23 10 views
10

Utilizzando imageIO, di solito ho il problema di trasformare un file immagine e, dopo averlo sovrascritto, perde tutti i suoi dati EXIF. C'è un modo per preservarlo senza prima estrarlo, metterlo in cache e quindi resettarlo?Manipolare un'immagine senza cancellare i suoi dati EXIF ​​

+2

Memorizzarlo altrove, quindi sovrascrivere la nuova immagine con un vecchio exif meta? http://www.screaming-penguin.com/node/7485 –

+0

Questo è esattamente quello che voglio evitare – preslavrachev

+2

qual è il problema con la copia del meta? ecco un altro esempio http://nucleussystems.com/blog/java-copy-exif-data –

risposta

9

Ecco la mia soluzione utilizzando una combinazione di Image Commons, Imgscalr e Apache Commons-Imaging. È un peccato che non ci sia una sola biblioteca che combini la lettura dell'immagine con i suoi metadati, il che rende probabilmente piuttosto eccessivo l'utilizzo della memoria; I miglioramenti sono benvenuti

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; 
    } 

} 
+0

Grazie mille. Ha funzionato bene. L'unica cosa è che apparentemente 'IImageMetadata' è chiamato 'ImageMetadata' nel repository corrente per Apache Commons Imaging –

+0

Controlla anche l'altra soluzione da @Rigeborod che sembra un po 'più efficiente –

8

ImageIO hanno questa funzionalità se stessa, ma invece di ImageIO.read sarà necessario utilizzare ImageReader:

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

(si consiglia di controllare anche se tale lettore esiste) . allora avete bisogno di impostare l'ingresso:

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

Ora è possibile salvare i metadati:

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

E poi leggere l'immagine:

BufferedImage bi = reader.read(0); 

Quando si desidera salvare nuova immagine , dovresti usare 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); 

Dato che è un argomento vecchio, immagino che questa risposta sia un po 'troppo tardi, ma potrebbe aiutare gli altri in quanto questo argomento è ancora googlable.

+0

Questo ha un aspetto notevolmente più efficiente della memoria della mia soluzione, Immagino che una copia dell'immagine sia ancora in memoria, ma non può davvero evitarlo. –