2012-01-23 36 views
10

imageIO kullanarak, genellikle bir görüntü dosyasını dönüştürme sorunu yaşıyorum ve üzerine yazdıktan sonra, tüm EXIF ​​verilerini kaybeder. İlk önce ayıklamadan, önbelleğe almadan ve sonra yeniden sıfırlamaya gerek kalmadan onu korumanın bir yolu var mı?EXIF ​​verilerini silmeden görüntü yönetme

+2

Başka bir yerde saklayın, sonra eski bir exif meta ile yeni resmin üzerine yazın. http://www.screaming-penguin.com/node/7485 –

+0

Bu tam olarak – preslavrachev

+2

'dan kaçınmak istediğim şey, meta kopyalamanın problemi nedir? İşte başka bir örnek http://nucleussystems.com/blog/java-copy-exif-data –

cevap

9

Çözümüm, ImageIO, Imgscalr ve Apache commons-görüntüleme özelliklerinin bir kombinasyonunu kullanıyorum. Yazıtın meta verilerle okunmasını birleştiren tek bir kütüphane olmadığı için, bu muhtemelen bellek kullanımı üzerinde oldukça fazladır; İyileştirmeler hoş geldiniz.

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

(böyle okuyucu varsa da kontrol etmek isteyebilirsiniz):

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

Çok teşekkürler. Güzel çalıştı. Tek şey, görünüşte, 'IImageMetadata', Apache Commons Imaging –

+0

için mevcut repoda 'ImageMetadata' olarak isimlendiriliyor. @Rigeborod'dan biraz daha verimli görünen diğer çözümü de kontrol edin. –

8

ImageIO bu işlevi kendisi var, ama bunun yerine ImageIO.read size ImageReader kullanmanız gerekecektir . Eğer meta kaydedebilirsiniz Şimdi

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

:

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

Ve sonra görüntüyü okuyun: Eğer yeni bir resim kaydetmek istediğiniz

BufferedImage bi = reader.read(0); 

Sonra girişini ayarlamak gerekir ImageWriter'i kullanmalısınız:

// 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); 

Eski bir konu olarak, bu cevabın biraz geç olduğunu düşünüyorum, ancak bu konu hala devam etmekte olduğu için başkalarına yardımcı olabilir.

+0

Bu, benim çözümümden çok daha fazla bellek verimli görünüyor. Sanırım görüntünün bir kopyası hala bellekte, ancak bunu gerçekten engelleyemez. –

İlgili konular